From 0df2b78fba62cb129990f3eb3f8252d95fef8fd3 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Tue, 26 May 2026 16:54:41 +0800 Subject: [PATCH 1/4] feat(build): migrate project build and docs support --- .gitattributes | 10 + .github/.rat-excludes | 14 + CMakeLists.txt | 462 + LICENSE | 48 + NOTICE | 8 +- PaimonConfig.cmake.in | 22 + ci/scripts/build_paimon.sh | 84 + ci/scripts/setup_ccache.sh | 28 + cmake_modules/BuildUtils.cmake | 4 +- docs/.gitignore | 18 + docs/Makefile | 46 + docs/code-style.md | 15 + docs/make.bat | 55 + docs/requirements.txt | 13 + docs/source/_static/file-layout.png | Bin 0 -> 309410 bytes docs/source/_static/prefetch.svg | 4 + docs/source/_static/sorted-runs.png | Bin 0 -> 181236 bytes docs/source/_static/theme_overrides.css | 86 + docs/source/_static/versions.json | 7 + docs/source/api.rst | 40 + docs/source/api/catalog.rst | 32 + docs/source/api/clean.rst | 37 + docs/source/api/commit.rst | 41 + docs/source/api/data_types.rst | 37 + docs/source/api/defs.rst | 31 + docs/source/api/executor.rst | 37 + docs/source/api/file_format.rst | 57 + docs/source/api/file_index.rst | 53 + docs/source/api/file_system.rst | 51 + docs/source/api/global_index.rst | 77 + docs/source/api/io.rst | 41 + docs/source/api/memory.rst | 37 + docs/source/api/predicate.rst | 52 + docs/source/api/read.rst | 41 + docs/source/api/scan.rst | 53 + docs/source/api/write.rst | 45 + docs/source/basic_concepts.rst | 89 + docs/source/build_system.rst | 99 + docs/source/building.rst | 291 + docs/source/conf.py | 147 + docs/source/documentations.rst | 68 + docs/source/examples/clean.rst | 26 + docs/source/examples/index.rst | 25 + .../examples/write_commit_scan_read.rst | 26 + docs/source/getting_started.rst | 37 + docs/source/index.rst | 108 + docs/source/user_guide.rst | 40 + docs/source/user_guide/append_only_table.rst | 26 + docs/source/user_guide/arrow.rst | 125 + docs/source/user_guide/catalog.rst | 37 + docs/source/user_guide/clean.rst | 162 + docs/source/user_guide/commit.rst | 126 + docs/source/user_guide/compaction.rst | 216 + docs/source/user_guide/data_types.rst | 212 + docs/source/user_guide/global_index.rst | 95 + docs/source/user_guide/manifest.rst | 132 + docs/source/user_guide/prefetch.rst | 48 + docs/source/user_guide/primary_key_table.rst | 82 + docs/source/user_guide/read.rst | 302 + docs/source/user_guide/schema.rst | 135 + docs/source/user_guide/snapshot.rst | 67 + docs/source/user_guide/write.rst | 166 + third_party/download_dependencies.sh | 125 + third_party/roaring_bitmap/CMakeLists.txt | 22 + third_party/roaring_bitmap/roaring.cpp | 20315 ++++++++++++++++ third_party/roaring_bitmap/roaring.h | 1943 ++ third_party/roaring_bitmap/roaring.hh | 2893 +++ third_party/versions.txt | 135 + third_party/xxhash/CMakeLists.txt | 21 + third_party/xxhash/xxhash.c | 42 + third_party/xxhash/xxhash.h | 7343 ++++++ 71 files changed, 37409 insertions(+), 3 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/.rat-excludes create mode 100644 CMakeLists.txt create mode 100644 PaimonConfig.cmake.in create mode 100755 ci/scripts/build_paimon.sh create mode 100755 ci/scripts/setup_ccache.sh create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt create mode 100644 docs/source/_static/file-layout.png create mode 100644 docs/source/_static/prefetch.svg create mode 100644 docs/source/_static/sorted-runs.png create mode 100644 docs/source/_static/theme_overrides.css create mode 100644 docs/source/_static/versions.json create mode 100644 docs/source/api.rst create mode 100644 docs/source/api/catalog.rst create mode 100644 docs/source/api/clean.rst create mode 100644 docs/source/api/commit.rst create mode 100644 docs/source/api/data_types.rst create mode 100644 docs/source/api/defs.rst create mode 100644 docs/source/api/executor.rst create mode 100644 docs/source/api/file_format.rst create mode 100644 docs/source/api/file_index.rst create mode 100644 docs/source/api/file_system.rst create mode 100644 docs/source/api/global_index.rst create mode 100644 docs/source/api/io.rst create mode 100644 docs/source/api/memory.rst create mode 100644 docs/source/api/predicate.rst create mode 100644 docs/source/api/read.rst create mode 100644 docs/source/api/scan.rst create mode 100644 docs/source/api/write.rst create mode 100644 docs/source/basic_concepts.rst create mode 100644 docs/source/build_system.rst create mode 100644 docs/source/building.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/documentations.rst create mode 100644 docs/source/examples/clean.rst create mode 100644 docs/source/examples/index.rst create mode 100644 docs/source/examples/write_commit_scan_read.rst create mode 100644 docs/source/getting_started.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/user_guide.rst create mode 100644 docs/source/user_guide/append_only_table.rst create mode 100644 docs/source/user_guide/arrow.rst create mode 100644 docs/source/user_guide/catalog.rst create mode 100644 docs/source/user_guide/clean.rst create mode 100644 docs/source/user_guide/commit.rst create mode 100644 docs/source/user_guide/compaction.rst create mode 100644 docs/source/user_guide/data_types.rst create mode 100644 docs/source/user_guide/global_index.rst create mode 100644 docs/source/user_guide/manifest.rst create mode 100644 docs/source/user_guide/prefetch.rst create mode 100644 docs/source/user_guide/primary_key_table.rst create mode 100644 docs/source/user_guide/read.rst create mode 100644 docs/source/user_guide/schema.rst create mode 100644 docs/source/user_guide/snapshot.rst create mode 100644 docs/source/user_guide/write.rst create mode 100755 third_party/download_dependencies.sh create mode 100644 third_party/roaring_bitmap/CMakeLists.txt create mode 100644 third_party/roaring_bitmap/roaring.cpp create mode 100644 third_party/roaring_bitmap/roaring.h create mode 100644 third_party/roaring_bitmap/roaring.hh create mode 100644 third_party/versions.txt create mode 100644 third_party/xxhash/CMakeLists.txt create mode 100644 third_party/xxhash/xxhash.c create mode 100644 third_party/xxhash/xxhash.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f692911 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +*.so filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +test/test_data/sst/none/79d01717-8380-4504-86e1-387e6c058d0a filter=lfs diff=lfs merge=lfs -text +test/test_data/sst/lz4/10540951-41d3-4216-aa2c-b15dfd25eb75 filter=lfs diff=lfs merge=lfs -text +test/test_data/sst/zstd/83d05c53-2353-4160-b756-d50dd851b474 filter=lfs diff=lfs merge=lfs -text +test/test_data/global_index/btree/btree_compatibility_data/*.bin filter=lfs diff=lfs merge=lfs -text +test/test_data/global_index/btree/btree_compatibility_data/*.bin.meta filter=lfs diff=lfs merge=lfs -text diff --git a/.github/.rat-excludes b/.github/.rat-excludes new file mode 100644 index 0000000..b1fb2b8 --- /dev/null +++ b/.github/.rat-excludes @@ -0,0 +1,14 @@ +build/* +build-debug/* +build-release/* +test_data/* +third_party/* +build_support/* +scripts/* +cmake_modules/* +.codespell_ignore +.gitignore +rat-report.txt +requirements.txt +.gitattributes +.*\.svg$ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9a7a7e2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,462 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +cmake_minimum_required(VERSION 3.16) +message(STATUS "Building using CMake version: ${CMAKE_VERSION}") + +# https://cmake.org/cmake/help/latest/policy/CMP0135.html +# +# CMP0135 is for solving re-building and re-downloading. +# We don't have a real problem with the OLD behavior for now +# but we use the NEW behavior explicitly to suppress CMP0135 +# warnings. +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build.") +endif() + +project(paimon + VERSION 0.2.0 + DESCRIPTION "Paimon C++ Project") + +string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_BUILD_TYPE) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(PAIMON_BUILD_STATIC "Build static library" ON) +option(PAIMON_BUILD_SHARED "Build shared library" ON) +option(PAIMON_BUILD_TESTS "Build tests" OFF) +option(PAIMON_USE_ASAN "Use Address Sanitizer" OFF) +option(PAIMON_USE_UBSAN "Use Undefined Behavior Sanitizer" OFF) +option(PAIMON_USE_CXX11_ABI "Use C++11 ABI" ON) +option(PAIMON_ENABLE_AVRO "Whether to enable avro file format" ON) +option(PAIMON_ENABLE_ORC "Whether to enable orc file format" ON) +option(PAIMON_ENABLE_LANCE "Whether to enable lance file format" OFF) +option(PAIMON_ENABLE_JINDO "Whether to enable jindo file system" OFF) +option(PAIMON_ENABLE_LUMINA "Whether to enable lumina vector index" OFF) +option(PAIMON_ENABLE_LUCENE "Whether to enable lucene index" OFF) + +if(PAIMON_ENABLE_ORC) + add_definitions(-DPAIMON_ENABLE_ORC) +endif() +if(PAIMON_ENABLE_AVRO) + add_definitions(-DPAIMON_ENABLE_AVRO) +endif() +if(PAIMON_ENABLE_JINDO) + add_definitions(-DPAIMON_ENABLE_JINDO) +endif() +if(PAIMON_USE_CXX11_ABI) + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) +else() + # lance and lumina are provided in so file, cannot be recompiled with C++11 ABI off. + if(PAIMON_ENABLE_LANCE) + message(FATAL_ERROR "Lance cannot be enabled with C++11 ABI off") + endif() + if(PAIMON_ENABLE_LUMINA) + message(FATAL_ERROR "Lumina cannot be enabled with C++11 ABI off") + endif() + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) +endif() +if(PAIMON_ENABLE_LANCE) + add_definitions(-DPAIMON_ENABLE_LANCE) +endif() +if(PAIMON_ENABLE_LUMINA) + add_definitions(-DPAIMON_ENABLE_LUMINA) +endif() + +if(PAIMON_ENABLE_LUCENE) + add_definitions(-DPAIMON_ENABLE_LUCENE) +endif() + +add_definitions(-DSNAPPY_CODEC_AVAILABLE) +add_definitions(-DZSTD_CODEC_AVAILABLE) +add_definitions(-DRAPIDJSON_HAS_STDSTRING) + +set(CLANG_TIDY_BIN "clang-tidy") + +set(PAIMON_SOURCE_DIR ${PROJECT_SOURCE_DIR}) +set(PAIMON_BINARY_DIR ${PROJECT_BINARY_DIR}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") +set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build_support") + +include(GNUInstallDirs) +set(PAIMON_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Paimon") +include(DefineOptions) + +if(PAIMON_OPTIONAL_INSTALL) + # Don't make the "install" target depend on the "all" target + set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true) + set(INSTALL_IS_OPTIONAL OPTIONAL) +endif() + +if(PAIMON_EXTRA_ERROR_CONTEXT) + add_definitions(-DPAIMON_EXTRA_ERROR_CONTEXT) +endif() + +if(NOT PAIMON_VERBOSE_LINT) + set(PAIMON_LINT_QUIET "--quiet") +endif() + +set(LINT_EXCLUSIONS_FILE ${BUILD_SUPPORT_DIR}/lint_exclusions.txt) + +set(LINT_SOURCE_DIRS ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/test ${CMAKE_SOURCE_DIR}/examples) + +set(CLANG_TIDY_OPTIONS + ${PAIMON_LINT_QUIET} + --compile_commands + ${CMAKE_BINARY_DIR}/compile_commands.json + --exclude_globs + ${LINT_EXCLUSIONS_FILE}) + +if(PAIMON_LINT_GIT_DIFF_MODE) + list(JOIN LINT_SOURCE_DIRS "|" _lint_dir_union) + set(LINT_DIR_REGEX "^(${_lint_dir_union}\/?)") + + set(GIT_DIFF_FILE_PATH ${CMAKE_BINARY_DIR}/git_diff_files.txt) + add_custom_target(update_diff_files + # get git-diff file list + COMMAND git diff-index --name-only --diff-filter=d --cached + ${PAIMON_LINT_GIT_TARGET_COMMIT} > ${GIT_DIFF_FILE_PATH} + # convert to absolute path + COMMAND sed -i \"s|^|${CMAKE_SOURCE_DIR}/|\" ${GIT_DIFF_FILE_PATH} + # filter with ${LINT_SOURCE_DIRS} dirs + COMMAND sed -nE -i '\\@${LINT_DIR_REGEX}@p' ${GIT_DIFF_FILE_PATH} + COMMENT "Generate git_diff_files.txt for git-diff lint" + BYPRODUCTS ${GIT_DIFF_FILE_PATH}) + + list(APPEND CLANG_TIDY_OPTIONS --source_file @${GIT_DIFF_FILE_PATH}) +else() + list(APPEND CLANG_TIDY_OPTIONS --source_dir ${LINT_SOURCE_DIRS}) +endif() + +# runs clang-tidy and attempts to fix any warning automatically +add_custom_target(clang-tidy + ${PYTHON_EXECUTABLE} + ${BUILD_SUPPORT_DIR}/run_clang_tidy.py + --clang_tidy_binary + ${CLANG_TIDY_BIN} + ${CLANG_TIDY_OPTIONS} + --fix) + +# runs clang-tidy and exits with a non-zero exit code if any errors are found. +add_custom_target(check-clang-tidy + ${PYTHON_EXECUTABLE} + ${BUILD_SUPPORT_DIR}/run_clang_tidy.py + --clang_tidy_binary + ${CLANG_TIDY_BIN} + ${CLANG_TIDY_OPTIONS}) + +if(PAIMON_LINT_GIT_DIFF_MODE) + add_dependencies(clang-tidy update_diff_files) + add_dependencies(check-clang-tidy update_diff_files) +endif() + +if(UNIX) + add_custom_target(iwyu + ${CMAKE_COMMAND} + -E + env + "PYTHON=${PYTHON_EXECUTABLE}" + ${BUILD_SUPPORT_DIR}/iwyu/iwyu.sh) + add_custom_target(iwyu-all + ${CMAKE_COMMAND} + -E + env + "PYTHON=${PYTHON_EXECUTABLE}" + ${BUILD_SUPPORT_DIR}/iwyu/iwyu.sh + all) +endif(UNIX) + +# Setup ccache to accelerate compilation if available +if(PAIMON_USE_CCACHE + AND NOT CMAKE_C_COMPILER_LAUNCHER + AND NOT CMAKE_CXX_COMPILER_LAUNCHER) + + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + message(STATUS "Found ccache: ${CCACHE_PROGRAM}") + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + else() + message(STATUS "ccache not found, compiling without cache acceleration") + endif() +endif() + +include(SetupCxxFlags) + +# set compile output directory +string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_SUBDIR_NAME) + +# If build in-source, create the latest symlink. If build out-of-source, which +# is preferred, simply output the binaries in the build folder +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + set(BUILD_OUTPUT_ROOT_DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/build/${BUILD_SUBDIR_NAME}/") + # Link build/latest to the current build directory, to avoid developers + # accidentally running the latest debug build when in fact they're building + # release builds. + file(MAKE_DIRECTORY ${BUILD_OUTPUT_ROOT_DIRECTORY}) + if(NOT APPLE) + set(MORE_ARGS "-T") + endif() + execute_process(COMMAND ln ${MORE_ARGS} -sf ${BUILD_OUTPUT_ROOT_DIRECTORY} + ${CMAKE_CURRENT_BINARY_DIR}/build/latest) +else() + set(BUILD_OUTPUT_ROOT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${BUILD_SUBDIR_NAME}/") +endif() + +# where to put generated archives (.a files) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") +set(ARCHIVE_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") + +# where to put generated libraries (.so files) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") +set(LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") + +# where to put generated binaries +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") +set(RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") +set(EXECUTABLE_OUTPUT_PATH "${BUILD_OUTPUT_ROOT_DIRECTORY}") + +include(BuildUtils) +enable_testing() + +# Add common flags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_COMMON_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PAIMON_CXXFLAGS}") + +# For any C code, use the same flags. These flags don't contain C++ specific +# flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CXX_COMMON_FLAGS} ${PAIMON_CXXFLAGS}") + +# Remove --std=c++17 to avoid errors from C compilers +string(REPLACE "-std=c++17" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + +# Add C++-only flags, like -std=c++11 +set(CMAKE_CXX_FLAGS "${CXX_ONLY_FLAGS} ${CMAKE_CXX_FLAGS}") + +include(san-config) + +# Code coverage +if("${PAIMON_GENERATE_COVERAGE}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -DCOVERAGE_BUILD") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -DCOVERAGE_BUILD") +endif() + +# CMAKE_CXX_FLAGS now fully assembled +message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") + +include_directories(${CMAKE_CURRENT_BINARY_DIR}/src) +include_directories(src) + +# +# Visibility +# +# For generate_export_header() and add_compiler_export_flags(). +include(GenerateExportHeader) +include(ExternalProject) +include(ThirdpartyToolchain) + +add_custom_target(paimon_dependencies) + +if(PAIMON_STATIC_LINK_LIBS) + add_dependencies(paimon_dependencies ${PAIMON_STATIC_LINK_LIBS}) +endif() + +set(PAIMON_SHARED_PRIVATE_LINK_LIBS ${PAIMON_STATIC_LINK_LIBS}) + +add_subdirectory(third_party/roaring_bitmap EXCLUDE_FROM_ALL) +add_subdirectory(third_party/xxhash EXCLUDE_FROM_ALL) + +if(PAIMON_ENABLE_LANCE) + add_subdirectory(third_party/lance EXCLUDE_FROM_ALL) + link_directories(third_party/lance/lance_lib/target/release) + install(FILES ${PROJECT_SOURCE_DIR}/third_party/lance/lance_lib/target/release/liblance_lib_rc.so + DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + +list(APPEND PAIMON_LINK_LIBS ${CMAKE_DL_LIBS}) +list(APPEND PAIMON_SHARED_INSTALL_INTERFACE_LIBS ${CMAKE_DL_LIBS}) +if(PAIMON_ENABLE_LUMINA) + add_subdirectory(third_party/lumina EXCLUDE_FROM_ALL) + link_directories(third_party/lumina/lib) + install(FILES ${PROJECT_SOURCE_DIR}/third_party/lumina/lib/liblumina.so + DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + +if(PAIMON_ENABLE_LUCENE) + set(PAIMON_DICT_DEST "share/paimon/dict") + + install(DIRECTORY ${JIEBA_DICT_DIR}/ + DESTINATION ${PAIMON_DICT_DEST} + FILES_MATCHING + PATTERN "jieba.dict.utf8" + PATTERN "hmm_model.utf8" + PATTERN "idf.utf8" + PATTERN "stop_words.utf8" + PATTERN "user.dict.utf8" + PATTERN "pos_dict" + PATTERN ".git*" EXCLUDE + PATTERN "*.md" EXCLUDE) +endif() + +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ + DESTINATION "include" + FILES_MATCHING + PATTERN "*.h") + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_C_VISIBILITY_PRESET hidden) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories("${CMAKE_SOURCE_DIR}/third_party/roaring_bitmap") +include_directories("${CMAKE_SOURCE_DIR}/third_party/xxhash") + +if(PAIMON_ENABLE_LANCE) + include_directories("${CMAKE_SOURCE_DIR}/third_party/lance") +endif() + +if(PAIMON_ENABLE_LUMINA) + include_directories("${CMAKE_SOURCE_DIR}/third_party/lumina/include") +endif() + +include_directories(SYSTEM ${ARROW_INCLUDE_DIR}) +include_directories(SYSTEM ${TBB_INCLUDE_DIR}) + +include_directories(SYSTEM ${GLOG_INCLUDE_DIR}) +add_compile_definitions("GLOG_USE_GLOG_EXPORT") + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +set(PAIMON_VERSION_SCRIPT_FLAGS + "-Wl,--version-script=${CMAKE_SOURCE_DIR}/src/paimon/symbols.map") + +set(ENV{PAIMON_TEST_DATA} "${CMAKE_SOURCE_DIR}/test/test_data") + +if(PAIMON_BUILD_TESTS) + if(NOT PAIMON_ENABLE_ORC) + message(FATAL_ERROR "PAIMON_ENABLE_ORC must be enabled if PAIMON_BUILD_TESTS is enable" + ) + endif() + if(NOT PAIMON_ENABLE_AVRO) + message(FATAL_ERROR "PAIMON_ENABLE_AVRO must be enabled if PAIMON_BUILD_TESTS is enable" + ) + endif() + # Adding unit tests part of the "paimon" portion of the test suite + add_custom_target(paimon-tests) + resolve_dependency(GTest) + + add_custom_target(unittest + ctest + -j4 + -L + unittest + --output-on-failure) + add_dependencies(unittest paimon-tests) + + include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) + include_directories("${CMAKE_SOURCE_DIR}/test/") + + set(TEST_STATIC_LINK_LIBS + "-Wl,--whole-archive" + paimon_file_index_static + paimon_global_index_static + paimon_local_file_system_static + paimon_mock_file_format_static + "-Wl,--no-whole-archive" + "-Wl,--no-as-needed" + paimon_parquet_file_format_shared + paimon_blob_file_format_shared + "-Wl,--as-needed") + + if(PAIMON_ENABLE_LANCE) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_lance_file_format_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() + if(PAIMON_ENABLE_ORC) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_orc_file_format_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() + if(PAIMON_ENABLE_AVRO) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_avro_file_format_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() + if(PAIMON_ENABLE_JINDO) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_jindo_file_system_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() + if(PAIMON_ENABLE_LUMINA) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_lumina_index_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() + if(PAIMON_ENABLE_LUCENE) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") + list(APPEND TEST_STATIC_LINK_LIBS paimon_lucene_index_shared) + list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") + endif() +endif() + +paimon_print_dependency_resolution_summary() + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion) + +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/PaimonConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake" + INSTALL_DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfigVersion.cmake" + DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) + +config_summary_message() + +add_subdirectory(src/paimon) +add_subdirectory(src/paimon/fs/local) +add_subdirectory(src/paimon/fs/jindo) +add_subdirectory(src/paimon/format/blob) +add_subdirectory(src/paimon/format/orc) +add_subdirectory(src/paimon/format/parquet) +add_subdirectory(src/paimon/format/avro) +add_subdirectory(src/paimon/format/lance) +add_subdirectory(src/paimon/global_index/lumina) +add_subdirectory(src/paimon/global_index/lucene) +add_subdirectory(src/paimon/testing/mock) +add_subdirectory(src/paimon/testing/utils) +add_subdirectory(test/inte) + +install(EXPORT PaimonTargets + NAMESPACE Paimon:: + DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) diff --git a/LICENSE b/LICENSE index 511feeb..0d31f3e 100644 --- a/LICENSE +++ b/LICENSE @@ -274,6 +274,8 @@ This product includes code from Apache Arrow. * cmake_modules/DefineOptions.cmake * cmake_modules/san-config.cmake * cmake_modules/SetupCxxFlags.cmake + * third_party/download_dependencies.sh + * third_party/versions.txt * third-party toolchain and patches: - cmake_modules/ThirdpartyToolchain.cmake - cmake_modules/arrow.diff @@ -284,6 +286,52 @@ License: https://www.apache.org/licenses/LICENSE-2.0 -------------------------------------------------------------------------------- +This product includes code from xxHash. + +* MMH_rotl32 utility in src/paimon/common/utils/murmurhash_utils.h +* xxHash source code in third_party/xxhash/ directory + +Copyright: 2012-2023 Yann Collet +Home page: https://www.xxhash.com +License: https://opensource.org/license/bsd-2-clause + +BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + +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. + +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. + +-------------------------------------------------------------------------------- + +This product includes code from RoaringBitmap CRoaring. + +* CRoaring source code in third_party/roaring_bitmap/ directory + +Copyright: 2016-2022 The CRoaring authors +Home page: https://roaringbitmap.org/ +License: https://www.apache.org/licenses/LICENSE-2.0 + +-------------------------------------------------------------------------------- + This product includes code from LLVM compiler-rt. * AddressSanitizer symbolization utility: diff --git a/NOTICE b/NOTICE index 9160245..25c418d 100644 --- a/NOTICE +++ b/NOTICE @@ -14,5 +14,11 @@ This product includes software from RocksDB project (Apache 2.0 and BSD 3-clause Copyright (c) 2011-present, Facebook, Inc. All rights reserved. Copyright (c) 2011 The LevelDB Authors. All rights reserved. +This product includes software from xxHash project (BSD 2-clause) +Copyright (C) 2012-2023 Yann Collet + +This product includes software from CRoaring project (Apache 2.0) +Copyright 2016-2022 The CRoaring authors + This product includes software from cppjieba project (MIT) -Copyright 2013 \ No newline at end of file +Copyright 2013 diff --git a/PaimonConfig.cmake.in b/PaimonConfig.cmake.in new file mode 100644 index 0000000..60a8b45 --- /dev/null +++ b/PaimonConfig.cmake.in @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/PaimonTargets.cmake") + +check_required_components(Paimon) diff --git a/ci/scripts/build_paimon.sh b/ci/scripts/build_paimon.sh new file mode 100755 index 0000000..c412689 --- /dev/null +++ b/ci/scripts/build_paimon.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -eux + +source_dir=${1} +enable_sanitizer=${2:-false} +check_clang_tidy=${3:-false} +build_type=${4:-Debug} +build_dir=${1}/build + +# Display ccache status if available +if command -v ccache &> /dev/null; then + echo "=== ccache found: $(ccache --version | head -1) ===" + ccache -p | grep -E "cache_dir|max_size|compression" || true + ccache -z # Reset statistics for this build +else + echo "=== ccache not found, compiling without cache acceleration ===" +fi + +mkdir ${build_dir} +pushd ${build_dir} + +ENABLE_LUMINA="ON" +ENABLE_LANCE="ON" +if [[ "${CC:-}" == *"gcc-8"* ]] || [[ "${CXX:-}" == *"g++-8"* ]]; then + ENABLE_LUMINA="OFF" # Lumina is only supported on GCC 9 or higher. + ENABLE_LANCE="OFF" + # Lance's prebuilt binaries can only be compiled on Ubuntu 22.04 and above + # which requires a higher version of glibc, + # but Ubuntu 22.04 and above no longer ships with gcc-8 by default. + # Consider supporting Lance from source compilation in the future +fi + +CMAKE_ARGS=( + "-G Ninja" + "-DCMAKE_BUILD_TYPE=${build_type}" + "-DPAIMON_BUILD_TESTS=ON" + "-DPAIMON_ENABLE_LANCE=${ENABLE_LANCE}" + "-DPAIMON_ENABLE_JINDO=ON" + "-DPAIMON_ENABLE_LUMINA=${ENABLE_LUMINA}" + "-DPAIMON_ENABLE_LUCENE=ON" +) + +if [[ "${enable_sanitizer}" == "true" ]]; then + CMAKE_ARGS+=( + "-DPAIMON_USE_ASAN=ON" + "-DPAIMON_USE_UBSAN=ON" + ) +fi + +cmake "${CMAKE_ARGS[@]}" ${source_dir} +cmake --build . -- -j$(nproc) +ctest --output-on-failure -j $(nproc) + +if [[ "${check_clang_tidy}" == "true" ]]; then + cmake --build . --target check-clang-tidy +fi + +# Print ccache statistics after build +if command -v ccache &> /dev/null; then + echo "=== ccache statistics after build ===" + ccache -s +fi + +popd + +rm -rf ${build_dir} diff --git a/ci/scripts/setup_ccache.sh b/ci/scripts/setup_ccache.sh new file mode 100755 index 0000000..e6888b8 --- /dev/null +++ b/ci/scripts/setup_ccache.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +echo "PAIMON_USE_CCACHE=ON" >> $GITHUB_ENV + +echo "CCACHE_COMPILERCHECK=content" >> $GITHUB_ENV +echo "CCACHE_DIR=${HOME}/.ccache" >> $GITHUB_ENV +echo "CCACHE_MAXSIZE=1G" >> $GITHUB_ENV +echo "CCACHE_COMPRESS=true" >> $GITHUB_ENV +echo "CCACHE_COMPRESSLEVEL=6" >> $GITHUB_ENV + +mkdir -p "${HOME}/.ccache" diff --git a/cmake_modules/BuildUtils.cmake b/cmake_modules/BuildUtils.cmake index fa83b07..21ddc16 100644 --- a/cmake_modules/BuildUtils.cmake +++ b/cmake_modules/BuildUtils.cmake @@ -150,7 +150,7 @@ function(add_paimon_lib LIB_NAME) -Wl,--gc-sections) install(TARGETS ${LIB_NAME}_shared ${INSTALL_IS_OPTIONAL} - EXPORT ${LIB_NAME}_targets + EXPORT PaimonTargets RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -197,7 +197,7 @@ function(add_paimon_lib LIB_NAME) PUBLIC "$") install(TARGETS ${LIB_NAME}_static ${INSTALL_IS_OPTIONAL} - EXPORT ${LIB_NAME}_targets + EXPORT PaimonTargets RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..a5909a1 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +_build diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..779537a --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,46 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Adapted from Apache Arrow +# https://github.com/apache/arrow/blob/main/docs/Makefile + +# +# Makefile for Sphinx documentation + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS = -j8 +SPHINXBUILD = sphinx-build +SOURCEDIR = source +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(SPHINXOPTS) source $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/docs/code-style.md b/docs/code-style.md index b413cb4..7cf1d65 100644 --- a/docs/code-style.md +++ b/docs/code-style.md @@ -31,6 +31,21 @@ This document defines the coding conventions for the paimon-cpp project. All pul --- +## Integer Types + +Use **fixed-width integer types** from ``. Do **not** use plain `int`, `long`, `short`, or `unsigned`. + +| Use | Instead of | +|-----|-----------| +| `int8_t` / `uint8_t` | `char` (for numeric data) | +| `int16_t` / `uint16_t` | `short` | +| `int32_t` / `uint32_t` | `int` / `unsigned int` | +| `int64_t` / `uint64_t` | `long` / `long long` | + +**Exception**: Loop variables iterating over small, bounded ranges (e.g. `for (int32_t i = 0; i < n; ++i)`) must still use `int32_t`, not `int`. + +--- + ## Formatting Formatting is based on **Google C++ Style** with the following overrides (defined in `.clang-format`): diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..ec328bf --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,55 @@ +@rem Licensed to the Apache Software Foundation (ASF) under one +@rem or more contributor license agreements. See the NOTICE file +@rem distributed with this work for additional information +@rem regarding copyright ownership. The ASF licenses this file +@rem to you under the Apache License, Version 2.0 (the +@rem "License"); you may not use this file except in compliance +@rem with the License. You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, +@rem software distributed under the License is distributed on an +@rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@rem KIND, either express or implied. See the License for the +@rem specific language governing permissions and limitations +@rem under the License. + +@rem Adapted from Apache Arrow +@rem https://github.com/apache/arrow/blob/main/docs/make.bat + +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..818836b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,13 @@ +# +# Note: keep this file in sync with conda_env_sphinx.txt ! +# + +breathe +myst-parser[linkify] +pydata-sphinx-theme~=0.16 +sphinx-autobuild +sphinx-copybutton +sphinx-design +sphinx-lint +sphinxcontrib-mermaid +sphinx diff --git a/docs/source/_static/file-layout.png b/docs/source/_static/file-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..0fe5f437429e32d073541754dc6aa4f92c028c25 GIT binary patch literal 309410 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
ecO)vIcI92 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�r1H?pm8S^tOH8mJ5? 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

=Fw>MY-( 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%0DcBnpLW`5xNS30XgyIQ^L6#K#SsJo#j8 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 z zaevX}E04)n2jtUK1^e2A_a~~0CxpMo<_@}#5!OQ`@~`i3Ia9WhQS_Uar3Zg5JLb9ouaT`ce5z%UDxhn8GWm^8e)u=wHrW3A`XIxCV(QTld^ zfLnTBj?a{?cF#j@ud6UQSkej`9>ibsTsH&&t@p8?oOKRfv6s_a{Cvp;BU0dMqc=L8 z5(&IFUZO3Yf^J%)I^18JY%ewBnVmgPuM$&1m0mQ&bEQk`J(<$+Vpe@YWsNHdICO}O zZ_QReKQI-pYa_hXH$^rdNT;(-2jSdw=V{s3iri6FUFje&XfPcEj@{XQ~9J! zmS`!I5n^Z(9A(A8HlH>H)i(U@4eu#WG4b-WU>bjn+ZB8u?t7L@>ZbN$wEDTl=Fc4d z`Cnoo91L}vX;T>iU_lZ3Lc(j{f7q#$f@_vI+NmQPG?y1M!k7LgVAk^#WbnQ8m*eRZ zKdw+(&UPB3oPq^7>BV)(06K5zgrf_L*{?IRe5Mzrpdhc#A;}mlYLTQaW`8@y072?t zC_TP=>)zq_g6jjSc#!kNuYLotZcqE>5FRPoAel27PMthxn4$}}5g#B6 zEMF~*msmO9B+N^^BaRS0onX-u4xRrrGw&DlOi0`2Sy$RrVTg26*}r?^FN|;A>pMrq z4{~2eX#xUof(t+_@nRDT&V(R?qCB!RRWv_UF7I0i2HadXn zpdrgwlzID3{RtVeI)e=9j3mQlv@gmc^WPQdOn~_XB^NagUZoMNL=c|k7HraRy+M!Q z7L%ABLaoJGw(vdi%Czup2v>f!0jh!BxfT@Kbb#h2R)Xo!Z!;&p8u& z|2HyW6AQ|C5imH4ZZT%ov#v^To;N@Qu6xo&r25%PX2W~t8&nu(QEK)S_#-fRpH{f1 zRFAUX9(}M6x>r1P8&zG~eujH>#vhR~l)RoQZ}kG{x++5V43G4dN(@cwCOJsK^BsL3 z%f>sg#HSh>Hb^Z$5lhW7;E1dOSz%gMWd`C_8_s6W3M~-dZ!#2u_545_ywCswlcwftKmOK_n&$Fwm{h@z#DFu+oRAV5Sm8|Gq_z)rdUw`J>!J$ zWl=dKF!vOdDXh@{A@?C&PsRk^pnThQSfSn%xg_IDgCXhvL-qcR7<~BPyMAOdTA%Za zvGtYm(OKS!G3FZrjfV0bTLH_SR-8fH1Q+@YxlU?Ngg*N|v2!HO_mnZt| Z;fWGts;Ng*`w1NIB`2jUSu1WF@;}%J2M7QF literal 0 HcmV?d00001 diff --git a/docs/source/_static/prefetch.svg b/docs/source/_static/prefetch.svg new file mode 100644 index 0000000..1db39ee --- /dev/null +++ b/docs/source/_static/prefetch.svg @@ -0,0 +1,4 @@ + + +Reader 1Reader 2Reader 3Queue 1Queue 2Queue 3{0, 100}, {300, 400}, ...{100, 200}, {400, 500}, ...{200, 300}, {500, 600}, ...ConsumerPrefetchFileBatchReader diff --git a/docs/source/_static/sorted-runs.png b/docs/source/_static/sorted-runs.png new file mode 100644 index 0000000000000000000000000000000000000000..bbc061b4be4034bcfab312c982ad257c219e63a5 GIT binary patch literal 181236 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

5Z$}n-+9rCUFy1wWJ4;Q@UdfWS8q>e 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�ZT24J>7TGEOC|a&r5tMgO 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 s?w#sddOo$ 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%0A5yxO)NiDG|ru?aU8>3dY@Gy+s+Kf?- z#WVt}Zhc2rTSK-pyPPf6fF5(^Jd-V&?{Yh)#(gYKbsP(#LvK22PQF!aALhU(MqaM! zVi7RQpjSMmq3T;x$S}cztQBVU|F^YFoK^DHc)8w@$>vvuyuMsxHnGY@;&iQ}LFIsW<%shTxR~4yaRAt2a3SE*7N~Uuc(7pM zd)xvmn_*sU!6`m<+k#UDNL-janWb;Lj&J;;7QL?o&Jh>OG*6g!-$V0aZNDE<~EiXzUQ8M{`0S=@d>FCjOpab$35I19fU~c zG+aJMrU+o#wbQS>@=8X9YIiD{+Y~m!96nCufDki7uZP@KtRo3ml!)66TL(qWWvF>u zl(=g#EK08%30(x-hYcJQKlOK4sOCI}Ai*4YzB_oMW#U{Gfz2*BWB2aeS54@9{p5iN zo?wW1zt{w`v4AhutYj&1f~>S2IUWlPQzALI;tj1e2imkiry9_vgtt}gbcIfke2kGw zD_!TRvv&9=xr0aHt7L1f>S*-Qf-*a*eo~bdRrL>#AEGBNh=I#HP%CnZZihSGl z2K2tkFDTNS`;0IPtT5}UfiGZwnXG*+gF1^VLB+>D(`p*biBBov<2;WDW1I7m_#jXM zYofMQXzb$!TxbBeNNxe1!aJOu$BY@Xc=6(Q-+lLzOD=A#*c}+)NSqxj002M$Nkl~kHufipv8ux6qFX&Pn;AnD83IYh|(RrPIgFb<7gmeIiT)Gj1!6cy*b+wI^3il zfwnMd37V@(^mBpzJy$|RiVI^11@@K921H|91+Q9wV_-jX?*~u3aWRPvyUnpza3c8} z2Tgj13rN0+f3YMF=kk;~31#>tl6oMf-Au2bB8AhJAwS8V{@augM#C zw@4il#-HX17<1k%U&q98g9a9KJJjNjSjkZEuGZl${?Iq-Vt)hsI;-7Ti?q|uyNU-(^vr9VYuo&*d3 zt1c)it?k{X-;hxuT}L?OFIbrFvx8(~I3SH?(A53Wkxu~!|LAMK`DuE07V$}>;HT!w-raBgsc*Ru< zr^3;Y>2N4+0gxd;Eor8#GzHY)g5lxm4nsXAT#?!?V>kI0*|X{!vBI`EHS1~I_Hj_+ zs&O;^a1|VZKrQ+b!Ga}AxCIIFz=BLC@$T0fPs{`#P)-`Lx8|(y%whPgyw{YZ8v%lH zURKZJ7WA2>vZ^xl63-S!3Y1X&jaTvvW-kFWc*}r;(8iu4cjJEsurmfpH+GkS#3<)y zkKiD3C@v4xnvc6&2{_Y(uWOck;S@qO|I7$%(5C`>X?B z^k6|kPt0G_VUiSF66&a$cZ@m!L)VX)D*r=i1^-t}3bc@eY?HvJ?rW9bX!oixDgc1ni~rg8mSM zCdFGO2(r$)Z6fg`f=|3ru?AqXc>D6YDIv;yQo;rTB$li&%&6ARz(WTc1ZH(d^m58$q8cQhd&=F|;q>4jUqncL&N=6Rzug6InZw3H@hY}h#Cclja+6;Q z$eyqP*UNk~u%K}+Y!uNGg0S<*kt0gOt}}+F?%1~Zya6s@;=8?pWqax?yM656??_U+t*HV$L`f{S zBek^Xj_Q6UlGN(AlRV*;)v9lJQ{)pEEyT=P(-ABNaRN(%iO{odo2fY$UjtMUgH!aG ziF}1{W;3uzp7RlBQeYeR7e(Esv}8ML{Y*+cymeO*E`$Zx2rvhqEhkdEG}Q%nD7gjj z_wTS^(@_km5^**WsT`kofiof#f>{HkHs?^RW=_5l>n$8d#ac18BscK>Ni*EJ|1z~z zN+{c$Z8W-)Ryg|AYE4;c)>BISH0VI=zv7p|vDNZbu#ZacFkvfWCK(?Jv_1B@hQH2u zSW-7fo-q6O^6;Rl(_Nrj$kaRJ+8Dqv%GuAE{Cip0xpPx7$T|PwEEdHaG4S^A=A&xc zM&;sZ=G4DQJ}Tg`6Va*=Mke#ht}= z?PD};5m(0-8Ft`Hq!Q?I@YC_K8-oTRANW#oD|FmQxEkKYcqWLzgkFj~R|065XFNB7 zS4T6WR&otde$3pC|O3=XmGx(_`{vJ%s&(y-bZ;NdJ=i-Ah!;GrTxg9<_h-mq5z8I&vr zP?B9CjNy2=I+Df4k-PJ=r@+pDk6ocpcTT}Rz(G_^XiRNBR!^iZIgX7AFBHpVwi(9} z5*nM*cu?L8Xsqrx!Qah!mOhW*ZrMk36beiirx;^$6ceGcr}153Xqe2p7G5lo+5tOJ z>x^n7Nzh&oLQ=qm0DZmRiW~}pHuoi^_d^~mC`yJk8ZSk$niF*_NY=P?1ri)aX6MWD zU_tf)duf&c0d~I0l1c{lHSFU>D?SmJZ^?n1-S-WtcPXfl8Qez zwU$Xl0m}%w#ro4Fq4Rc#j>IqeCP|VJD@oFsoVBCKoP0Cmjf;o3BTx~gDUif2*y6KLRra3+j~8f37Mi9JAsjbGdFC!yX{(E_yFLqA9;7$Qs;_w`@Gy zZvMp1u=Vs?(1hJQ{-3qmX~RLxI<*jPLWqEVgm!`j$rUiWku;E`3mJ+{kXNizs)UGY z_Op0OVVCiaZDmGKp@P&S1x~3Y%cSRpzDZ`^k!KnDE=W<30jWCFSGH&$FU8T)$WkGI)(Beca zh(8ukg&9XqO{9 zZoIFsQCj}%TJ=xnzH^AQJy@`QyNsfQ>)Bb&dQwTcNpNlG2&1Z3a2PRm$w+JQ88MBOgWq1=gGe>aHe8LzL_6IC@Lenm0pO*EUDf~>}2NsV8 znS>Tf$WXALpb`2`oXA{OZWI(AI~lbG?lsiUsREs$pSrPOWRPvJBXE%4PyP)fM||=& z)PD{f6pO)(>LCG6jCJ(r(XbG|y5en0*eE6999$6&42veRD!g*l*Pt<8nC8eAF^9fM z{!UC}iF~fQ%&G8gnnWIq9td9Q1DL=>5{y?{3;B4t~PLH|NP%i3$%R0$LQqtk9lq2}1iuXmmq4$A9=_Pk(f#6xJx!Xit8#F+z*&>=Y zrW^(if|I?Of!o~`5-b#_zDdxas0@Pb*SxLd`w5a$VlxK0Th%@Gv((!rH7Z#fQfF~Tn@q=SOc zLvYCM$0D8!=y|f(@TR96;>PifpTt$@tEecn`G*>&U#4Y8qmp!Umxu&X0pdjKu*;l; zEo(X=Fhy5P3!v9GNH0<(3WYYqDuDmZvih%=#LuON1pekDmah<>s9JKduU!lI$q7LSe> zfvn;uvkcQrTA7Fr)N;Zm{fihpg4*n_B;0d>N>R&;1-Mq-(9Sf|`T=I=XZhG=juwcTbs82FC zA9S>Jqy-gm5Odn?SXVAd%ooGMmVn*BI2aV2Tq~+vUpshRWY2dFV^J($uMBpS|7{FHY;7|g6 zj7@WsK!gal^qC_cFfZWCvD;iQ^Z^eOklh7^^{?=#`=$gQEotwU9-37ddI(ZuHb-W$8Qbw11|GgLrbDu|~ zi4O^o(xtRKKyxgN1DKCU64ot#DiW8j8|6(gLST;3-kdigGB!SuYFdm*9Nut1u?C3?iyVgy6qjVcbp3E}t~bw^{P}mKASd~NdkFZ1h(|Q(}10P1$$L#n3H|vB$m8$SmU#V<-yFD|{D1QI}(OQU_*wZ14|K_Eb>VIo&HOm9Vx1rUuMGK|@s7cgvmT29ozZ0Tf=cPr= z0dRJ=$_bg$3Dfk3d|!zU0EcAXZArdW635|V_4%&}+zCI6mVSbGI+}Fq?n3{lVF+9# z#s#y(*r`q&A=)yIEGWuVD@K;mle*u`p(BNEi9+tspL-OvO17o?J8~8VIu!B=sTL!E zvIEFVpBPM}yl9{m4j5xfvWCdgzqiEVWB?T}V+XuANRNRFJk?s6E!POESGCdP6=>Gw z5$^mqH9HEA2m&dgsd1=`X+jeKE1WjDPHGVX(lLTMT@h4(urkbb865*hKJmY?%@=*u;w_>;u`CE@Sv{e9Fz5Yjk=aL{8<0 zema5#$)KhFPuJv^D6_4^OgW3;hX`Gu;zX7(Nt7%4rei^yps~gSfctVMMH_lCN5nXY zJ;`(g4>hOV%~=HE6Y+CZnwtX(TqC|mDiEv76KNw1=gpOXXk#WblBLXrHs+YT&P}i& zwJDB55WkpyB8>v*=S(Hye6?ni5f)VDh&^BXdfM&;tLdAxqf z!uoiJ5|DUU$<9+SDR7*E1xY$HLxHf!c_E7Y(^5v=MQ6Q$u;ifd<9?;{dnfxpszDH8 zH`zg?&*XM7gC|i>pX_A+i@zBI_%secN2-RVR!Q8Oy~N-gene=S$nzvjB_rH|)ba{B zEV0C~RRaC3erokkQ1%jsh!(0v-b>^KqNtJFpHzLqAYvJ;gBJ+B?I@His=S&UJQ)cM zyS*M?;+t@h$r{W6V}HSd*ubz60v#|BC&reL>$paQD;PRToB3zdMRGl;OAFr8b%(Tu zxJf?g+z2h0dL%A{|0AiQtHZ_o5( zfWM;msUa};o@In&%nJp*AhI^$qAE!`i|;s*39H$H1a-A#;EJ^3QFWSJ=snp zS&Vz_Dciyo(W{bD5_?md$eeTs%EJWSX^jBo;R33FL{zDh(1@Q3Y3DvP3ns!MOqjyg zaaaO|l5T`?kg?ZvNS+45q!U&{5RcPRV2+t6-mR@HC_I(^R}Sy~GJ0_$B}?QLM_wRa z7zd3Y+bdWt{y>CMm3b zwAa0f0K=X8lCVWEJ|xG;*D)~k4YvgWDTQ(Ny+T(rYw(GLNk+7cnh<#mnem!URG&c3 z$y_V`KxcW~*h9t)rx3}kZ-NpwmP1O{1v;vtb@7NCcES!Q$SMGfoxzNleE=ear7a$Q zhbF$@69q?4?u9%%1Q2mWXXKC6TX<`DE9@{5efMS-w+c?13|tl@(2cDN4tB>QV*^iF z3UV@b@MeAMPM*cUf~PTFX}s@LA|2*rfWKs*pkezzCmLROFXVTmfI&03Sh{%gKG1@#t_ZnNK02lItP7Ml)gG z0iF;viJcWJs81v)OnyN%NSchlMxHOT1xc1sX4TOoW)x2kFG!FL77Q7o&#OX-+C`W8 zl!+lujLXH70YMi?-Vj#VJFLl^+TuS>q`n|U3CKQ`Bpnr4oPtThy>J(u@fI*wlA*9) zk>_!Ra?N$WklZ0s0TG%k-bf^kwR)szfP10Bl$tWMBPwCj6>?K_!o#rYc!lwVSb>9d zXYjFG*T%qLgs^pea`In{QGR-Gu&u-D8>G`44SP)9!Gc1J4RM>)^fZBq6|X>zk~hdy z+^6c6DXJ%`+>X-i8(*FK_P#qFdH<XA7D!qV^JfRgUv?kIS|N(N-$k0tCz3&H3@9f9%&=@mW| z8DG#qQX7UHG$E1E^{gC0&ed83q(BK^$Yo@y60eya5Lz z@=#}$xz_`7ogf=t1S%1t!IDpe^o_ex1g5-M0@RQZiq%(4fldgyv!BB>Sq8tZxhg?U z!U-8dPr4SMe6ZLK)}(sbf*HN|Y>vSD5-bR#+?*#lgm~&b#oWoCV2BBHJM36a2A;S# zJRk7@x;;!8%pX$1uFA1BAK7?-*{+Ok4ko34)&1rWXKRAvV5{sfJ zaD&7i7c*tl+MmS{=o3xEGbn9{pfHI#$xUnkS1pQI-*{BE$S}w}itj)aK_p-Bjwxuc zC+7%SMSKbIiM&+2ZLlLqydd2~WNDNg{AckO1)?d;np zEvv#WCAAj?0mhwn|L18Wi+f777#9OO2tnRxuQ7~_yd$_3kP3L6WGiV9XN@pnaepB# zhCE$rjEwpr-Nx-1*b;9fzvxO_?VGI57euZJI>ABeAGP?|wg1hgVE9 z9iVZ|lBo~Jzu^P{;_L_j%)K5oZX8RcQ5%|Ytt3wndJ#^eyLA+J5_d3* z96{vKyTtFuU4hFJ0V_%vWU}Ojuyve;-a1~v5=+3iH)pwXUp6P-U?!Ky!=ZgF6)=?D zP<-7P%;0K8Aj?34sEVH^X+0E2kU`8%#wWuYHncNMth?rI(qz%#9jVr0u)(R3gQA4x z1ikM`#ubjzurvJZzI#2%0sjyV8XdN%7(kAFlJ9r@N$#YxcU{+qr~C!sV4EeL1_6>d zs938bWeYk3ae+*Dh&s5g5ItbUI6~CnFQJ8@uhEKvb*jE|hEH0(}9{j+oS zeb-?(B^e}GFq2yrG=tWMyas$KOpm^C{nAc__DcX4y*m$xLBK9^!_>iamGp{^MMUFR z|C>N1{^YEZt3niPJa%LvMp1&vWV1LO`JZs?|KD(soG;_Mr*M$5#BfD94Z@JyY1l6r zD;?*_Gl#kW%;E67LWE;5se-+z&+tn$z%DoS!ef7(_uheh*>)V&=y8`3STi!;eP=;O z>E{~KL$Y-kGHg%|^+{gBXX~$$)48qTViuz>;;%B2QIn7_>&!-x?~n`{a#(rtvN3Kc zf16nh@NN!`ji;~K7=qvU5@QTJE1~Q zU2j+=S%8>=L~tst_KCz$%}assM#iw9Phx1vwqMd^7Uc$Ep?DySNta!SE`l*waFQko z3|)(iSk)OFlL(vJQ#c6ghMhS*q@_Rm|J4r$7>lWNr;ZA~t^1hu#A`yAR%dUMR0a~R zfIg^{BxyyYhu<<9ykU;WjfM;%))hLLEPStfF_`k$?R#1pDmNW8Wp8`0$~3~^4t5-P zqNXf+ciVN}ezUPD=TpheK@MT`Leb7I1kxb@^$Sh+x3TPluXr-49g*CKh*e0vMNg7p zrf-xNFoPKRlsy5q#3*5GAyF89q}Y(5gH}ngb(#yg9@;zPyeJ_@#5f8BO)U7Mmt06n z4sM3FaHwL8gM;cwcHJSpBg(72rv;D596UE zhl@V`giScSq7NMYV=-AFv~j~5;@XHk+A4&(gkFJ7WK@t{rF@wBw)O`1{<~4VTL!*F zW&?qvEcC@XQoJUq_Cq{&jbK4)VgU5?`LFp=^4Gds7wrwlbd*Tum#`T}S-w_YGo5(XSY);m(LAn8=XCNjCG4`EaIE?gaupAd(_0>$D{y@)P#9!MXyhWaVtw4){n z)L^V|VKvg81KLRf*|@1jJqvS&(Ufvm zNEAjAsF6ZKX?GH2j=zd>*S!3aMz5isK!mA9dlB?oWi%U`VkVDiY@j;9pmePpB&X0P z33FbckJ(GeMW_Ws#DJQl=p#*S7qDUtCLb-yM`J}%_$OvXutCl4@K&qZgT`L!^k;@98Kv;2^_^juT@2eN*)LEaxMz6DmtGCIKSmCH{j{i#V4#ZzOa1 z)-w-J-@4Due9VMB$V z09N7%32L+dNv2h*hV_l{Oi~PR-^Bt7o}+069Gj8#6HK{|N+XP#4op>s2cyMY`wbSb zpxW1c@FCF)>QTEeMidUCbiwn1cfo>iIovFK31z=vLFtt+UAR3Y6KEN+zoHV8u_SSn zfd%RF$MA;NFSt4XD!Dp|3=qiyTM3};9;i?h3p{Am+gDGG4v*g{tg0Q)4 zIJ2s+o_uHDOV2)X-!rRkz5nHD69-&<=i*ylEPra=yjNd(X5p81djE=k24!~PB90($ z#2UzI6~2~MC0uISM4Ls3=p8DdW>CMbRtAFarGPI&j57%Ygx%=*VlI(L5-Uon9BD*= zTBqoO%MmO{eTLq{PxpUco0I-3pQHS%-);y}<+Aw%CJ7^kcO+~eiogLggj%uz9MT&P zRV&_<{!LV_K-%f9LE?dqWmetEI_S@gZ;7 zkkAqWkb13_db7aact_XFqv6 z2GK>+iR}5w1dtIECfyJe zmC3TP4HYEuV&s@;uVI3*2J|3gfmQ+lJQ_BNC(Ntq+Qr1S|SHIsRpO)*=tFvN%VI5R8mJ z*U4WoZf>Xk%T*j%QXiNpXAO@+uppHKDExqlq%tV<#C;rk#N?lWT7%yODN0mqrO&IL zS(bZ{oEv0301?X>f0c-9F_mQJ5n2#48-X+!!{|LiV8anae<`FpdIoq?nwnMEHQ4SJ zOFG|@gLY&4)8U{;|M;J$Msk>%n#$+hS7BqOd+PY{+{DoC)>@Ny2q|`9;2`tqk03T; zzoaR{MP(^d*WDu7I^QIs4Y3Z`p!n;afBb>N`S!&0A@|%e5k0j4d>B5LegCc>na1OX z`k;2wO+&Q|M14sdMv1?*sa^Bq{Ru3if`n=#-*~B|)tO;({{rfLzXdsM21jZKeI*0Y445eMPR#ji?QWd!i*~DajE7?Xu@YpMi z2uUI{8D~2UJBua~o=+$}q%+darXGMuFa&>zZ?>v+aOOoj-yJmiGReQtVUEHmf)$ZV z_Seq=eMe>73gUHWOOMJM4R!Xj3b7i?o#pCc1I?5|PmPPJdtYx=yW5JOl8>-6-qAZ8ToO#apln8V6*M$|X!fLOvm;S|D z&ZoB|tld(r^>^&~G$l1dqkzV!swTKc%H}8#l_B@rnG=`q5Qm`95&}k(JRPO-|EQ*@pgJht0yP<@B12U8oHyO;UZ6u!VC}gK2TJI z&9kt)y;rCR6?oU=E_C-Pk%Je(!1==T=P%$1196ONg)>$|&&kS~jpHXql~gaol60F% z(b}qj_({F#7(l<@{vx3ZJHu!g#CPk8xBCZ%qga){(kL!ypmc8Y3ignZ?19yi@{wNB z5`lL|ix463CJ7CLnhNfjR0FU31inYo9pq|30XR{ZPP%xIi-5EvE5|Ywv@Ge$nl&F4 zrdj0CVZzVh3ki?mwqb+d8d1UkJ;8!FSD>DuQ^dkKn@OSAcs}TW(Y8vX05j+WGbo%N zrJ%{T8s1uN3j0a^*C)yo<%&aD!2^VMH}aK|TNJ(QIIW)i-82c9v*w$E2bzsWCk$nH zrt7V@Bj>x4;7lNVxa$(BENmoV= zxMAD2ZU6k|KiP%sO{#x3BTATNI`vXSN;|gR50c38C7JH5Y-&r=aDQMd9(Cz5OsGVF zgshR{WhB5vbgEY5glfC{@N5H{)>l6Sjt`C=1WF^$G-p0e23Ba}#2I9+CEJV4iFUb2 zt;m*8jn1N5xi6Y2LpCZ+IaL1Z(7g;Z5Gu!3qApPhhDF=Zn{g}78rFuK38`U3dtAT7 z&(}*NOn8IP5DaQCiV-QGl=+4vJ^s-D_}bSAz}E%8M>oILrUs-$TYI1JS6P^g zF$5AfQXT{c6m$o!_{Lm`8y4Ye@DNcCkiVcRnf_afH;rJ3 z2E?}^UTN4z56MQ;h4HM7-S7)gLy6L_nM*BSB@*UlwFM71o~@f!Qqhyl;peEeznJK^Cy?X7L52$PznwXYw3=A?&j`q<_lX^y;5YksuFW|dV^ z*i@$@`z@`t#c$yK?!vd>v(aFofDI+TxI&(%n6e&IP=vddt@~Q{BCo0v0~X(wDnc`iht03sUzaWU6 z-jPe57-&*6YJ+cTXl`#V`ob$EUA0r4I7Nmi%MIIYD7@q8X*L zAk-SU6!ci^pcfx7r18E2MyYs%Eu(KR{Rha{V4_9gJyd8I1OU{abZX=Q1OnhFauR`8 zm|Q9?8se^G42$TN3D^~I0w`fC0kvc~h_49F;hs_2Sp5`hw3s+){s0yXkSHr!+<~#R zA6o^C@C{C+C50p+rF-jejF^;yVzp>rtjo>yT$8r|7s`Ya^%5LrlB`iA1#=$!N&o{& zh(a#5NfIVt$EvTT3+WKxtZc%c(J7X8I}qw<*NBag?zkO^2{9#2F(^|FMYXYWcYcbz zq=)^LFrkL+?auLyeTaU8yzL`LUKA4WpItZNK8!#%NyeMAvhEo2fWScSCX*H*b)rHW zZ-?c9n9!AfGh`@IoZ*)&=@+v%;7C0Re|X9o&A1rpl?&zc9Y2iC%PWdAN^v$!EQQAh+d50Ew|iq`Q?}Y(E(2e2mk0h|M@vx z=qNjdL+1Lhga5h1AAQp1X!Lg0T6{uzWKtg#b+M875GfuC6$+ZD%^?7VY_P?M@(StFhqIHCuUz58nH?$aYrxy229+maqAFe=WrI_zR~|KgGKgIg6exB%aYLj`h zuhn(g+0j#Oc~M9ntOf;i^}E{xhJ3ti^V+R=zAwZR`zBsXubTrwj1$nc9E5cG+j01*a*i6I+4?u(@_ytfD%$w$(-wzz3iwCQI#z{lP# zZ^I6@C<&r?KoV<pB#pqDOGh;01en7EZ^q zhqbjeY5j+r)9&DD>&`&2^nDywLt0v(%7-pBa{AnzF-5u4qJ@$Ivj%>;>W9}pIZ#vc zx*RIRdFuuKEB0P=IJerk5yUrm9jPOu6G6i+#Qua3TGDdIvq${M0}ZTo8L;NT0doa)Yb-q%NI zv!)nl|LOBNFT6?4EA@fDEgm;!j1ofCaX7c; z?tlK&s`|4agwa$|L=Ilzzx^x)OiY%9!83pY0%u`aND~uip}3{)xT~`aZ5l*b#bSC^ zk%%#IQ1_RwF#~4_#|eats2YEd63F6*Q>1M;KP4wXbHvaQ#+{z>XZMLWM1B}S4oGqF zDXZ#Ed0uvyK7BfA6Sh8;zv2<|ki+7`BuX*c zy4X7@;iN<{`Q`&3&*4#z>`isO$&&GSFjbtaa+ERwc7)L=a)Me`z7DWZMccU{Bxvdg z@wbSUTBu)Y^6Gv?S96^Fp!gK-gqB{oH*(FmH)``yOT$2uhO6By^~gZ6&R&~PiNd{U zn)Hz+zd^`K4`;m}miy@A|eJh+_AwF*oVO&@j1pBp_qMV zYw0)Twe68{i9zs!XkqmYJl9%tkURAwHU=!h3lhMC1#7n;d&q9gC@5`3hx?i4s=>t9$Iv-gxuORJqo-kD% zNTMaM)T<2HLF$gnS6c@BYt2&&xznr9@fmymWi5f{f4ySGM<0FUIWD%eIZv^TlydCo z2_kETSrna2l^BP>KEb|gy#x9&*#&lk%;06E&B_d^Vm7H={H}2{^~e)KA=4F7YDYF4 zt8S-|&db}E!6r|hOqwhuazIQ?!gX>$nr*43%{j3L)-G~Z?+#KAkDmPGFgivg{YgkM1;~@8; ziB~@u53Xe`F-9^g#1_1hZBO5~@X1LTd;Lee_SQQKH>~?SHmLE1+1HW{C!R4be(%;# zfB9i`qs!0Vos%|T!X+Vv#4U_l;vWW>1UBS_rRwD-&&>i|{8I>W_^12G5q*0UzL|rNxLGplvDe+U`%xLj1 zWx|S8x}*jTg76T-8an!&w-szt;OGm_q(@O@R?x^QuaX< z%hI<|7c5&ef63C1vG_J`#U~`Qp}uC>lK0OYF(GK%EVe&5)QN!@u=%0B=!f2%8Na%=nQ5mzq zjxNQOzY^ym^P+$nF@&!E=0i{#dH{au=J;bzV>94j=f6(Jnbmmo`e)QI-U$)k`5+qyas_~B= z+E(x2v9DP8$dXT15GOF|-dOm>(DUyMC|TU7r9A!G2Vm24hlbqs;G5o|1HSn9$@f0W z&n;wJ9jo)7v~TC*bWgnRf!PBh3r$+@v1gCF^1{sDN>hLNaUy}8%DUp`+JKzx~ zWRBTak-E3gvU>Gux}idNFz*BexH~o-peWzckaywjScKQkl0UXInrLV$)JOQEXGKYQy~Ti)kN2oz+O{X zRZ^528Xhf_IdFkh%V-PI$=~RTx}%UK(kq18Olo^+v_zpODYmE!_3T89p1eNKb4-Q- zY8gr~tbt`6gT8s|>B_<#K2|L{!+-MVw|yNY=3$)yC#Al}KCf)geCg>AvyL3V;PmU0 zdfgQrgGO8^F)E32SKZ#N%U*nUV?(PCdERr*EhDH#2?s<5-nID-Qfzs??KB;WRwB<~ z{4l604=jFXUER*?e7P97ZCCB%a~~fb^QA}tjUod=L^@?sKx5>fn9E?nfL=T_d87u`W4Q38VIt!sXaI% zg?bBM3hx?}BRDwu7HZT)uZHY|b^JQhN@Uoz{8ttFSYa477u?~Ny`j6xv+@dVSuv*ZhFTRurDE?knZ zq}uojgw6?@d;eXMP9cP*zhLE8_BLNfa~&gR-LEd!ck>%dwge=M>y@x7Bc?TW&`s*G zW%R^hQR-)wR31F?g9sRClI5TFW8UI>w9qkq+(OyPih}k-2M>JqLzT<+-5Hr)uPxcl z=;iX>Uby7tH$M?49(HDFQ{*G}Os1WauzEDp@QL14^3klD4y2|)#GECt)i>^kpMKl0 z`orWokCuMDcfNEQEacZ6paVI>dKm4^va1h&d z${M&o2M!w9K;Q_f6szuk_yoJFhZ7p*Tfnck4NjYNqhK%_I)z@NTb*?Z=lRCZ#pWO zL8P^*rYToH_SVOutNq}ur~iKDP83wj*KY?4`r5o7zxSGcL(X|42Bqp%KWJ^W&uxHbIOO`C=QCxCn z^i8uqVU3MP|8ez!qe@NTFY)K!?On#r0+@fRtPXtavSMX$;t^f>Zq=Y`0+YE;<4 z3mQ+7Wd_>oESXP9j@!P&j3+SajtRxGn8_ED3KtO)lO*=CE|<``uRZ1MjNw7F;CUCP`X9E^aQRRyr`q@5{+=xng(275i z{s@c{o(#{HL`m@y1_^<+MP1_4QL3aqVLU*5u(VnY9jSL6@?4(ar-B6}nmu@w>W$(L zcNT^benfg7{s2B$X-k3I|=N21N7Fw*-O2!{`sE{J?=cB|1Pju$ z7Y$bRcj1{gP8<~S+iyECrjtg-Sm+l6C*5?!5_8$`{^tc`WvyAWhEfKek31Hyz-=Wd z$GCB8z;%CpP>P=@F~*P5MV5&vjkTtEZjaSXXnCy{I+zo$krWC2JHUdvbe(C-+=QAH z0MUBPM4c8ICge4)s`jSBkl4^9eHgpcU_MCVU=Sozdu@Itx*i;rF6l6wJV9(#MYY(e8S?hex z{&>Z=M|P|%E^i75?>#2%o$1$I&qNXnz&b`oxd8t}H%1RguW)Z$V8+04!jqw_KuOXW z{z?7;9v4+N?T`@La2dF_$aC?RBH)H5q!%#-3dTipom>p>npfFw-&U0P-1DR(ixsOM znkr@ICplYd?t6N*M51gg6nkt%r7Puc4~mw9*-8L^IJG{_LzPQY=5hH9B2qnOyJL+_Y{*k#+%axqAOwa~2$^bcFE)&Kf zQPQVBZPBbFF%)KTGCL$CB^4j}Ub=+#u%!l$vZrvcr>EfZ*zC3C0V;^uK~0Y&c3CQh z-tuH%K|DMYR7suP2B-*8S*Hp)J7dXYnlifm@OHJlIo#wZk$+B^GIi?IK%e%v=YL3s z#Mk0LRY_-DlP!tW5CjV%rov@Qv$m;0VQVbzUmdkag1wss08yObsv_p3OAD_?E=%9Y zP4TELgUI$afCaH0?e?Q&TDhuaOFzI+NX}#@()mUPzymjwojrDxqyEU8_e%1Ls^g=B zXP)WE7(T1Ob=Ff)J@xFoN8Wj2LTplhS$w74?jL)W$&|Hc$B!F-3I~{_Cr9QU%E>(z z+g7mDC;F_ShR9j>+~{a-7(V*^YsOg<2F_dcZPw?Xyfgpx+y#p_nyD5*nHDq2w`o4r zrE7jHJ#_F(uwd`Bgq*_aBV``y?v0n9+_?55zp%d7PlyJh~h9~!yVWCT2#g*4ee13X*kwyvNN~d zdh2)JeaAAJp7xo`$p8RA07*naRFnR<{|VXizu%0@qCG4Fy+=oby6n@ZPw(E0q$5T` zonOE=X402PIOz=MmrQM%$P@_6q>0QW5Zdtnwy?g>-8b%xk%K(eP0CQpNm~FS=}kbO zP*LlaE*^SQA1;ZYP*5F;F{__7AX!sUFW*08bnlMvemCsNbIap$)qJvSwRC-hA$JsS`eqHb zX!Nk&w?DDo*FPAWb>|J|{_UC(eMeoz*pesQ-cXpk_mLOYS2p{_#KswP#nKO6?Wo<( z{W2lN*K*6*^T7(1zE9A&heS343&Sj8YNg#MLTS{BG!%djfXt`GL+6IEz+_-^PsD;m zZ)u~KryG@zQ1tXY1_u)!5CrR5 z7A{Ciz*r^(-8#Mc06lkI7~@HqpUpZ)lo+1AY&M180!=2 zacXtxA-?DpK1@6@oD=P4^h}>Id5d!Qw-&9Wab1ni`ES4V7DMs6oBlz;hg$KK)EOX= zCMDF8Ye~C9!frPDS3~|;eVHI~gYXc?mh+I!z3yQMN&7CklSs!+K{n!&;h_;|Q(w|r z;Zi~aVutq!3v!U|6K_Zc@44q5rs1EZ9hCd+-p0`Fk!9~XYV43(K^KLGzGJdui+E-< zRTdQ;N)np9&@zJprp_o=_&>jN;a`guRg@p&jqsH<>o+a`=+y&9%NyGKO7nNE__lDx zhJ8{zPBUk{H=&$MuXf~F;&h#+5aGWIqywBVn8cJb#+-|7`sxw9zrqt7?# z$JW%c6-ep7+*-liiQP7t9=PS)snce_=e@qXpuA4j1^IOK799&lMMTETn>TOx@ZqhE zWzRk{SBsnkW9qCw!ts5>dgHkvc+}#hFD%|)*%Cw}z{MZDJL|f{S&uwHexRYn)TE?x zd?q^?14zE(SdftkpX41%n55r@g#bpX7U(m~0=R+&R|po2_FzE{B8$9B+lT4;U6>O6 zi;Hvjb(nm6>h;*a`^L~Xmz=$ai+=Hy`i7Q}=>9lFLHUU@1q*W5;P?P3Ymn@}u~7p@ zfQObwFP>#AjZA4=_|d9|ADzo8x^z~$>86_kF;Zo#8r!gz&ZxArVGuBz78o2ZjLKX> z6aXBm(7?<HiEzL&1f#5!@+xJPefD z7Vg_{=Uw-_@xuKJ9-A;~1WP?ty!ifBM@`m>ZN6Z^^z_u1Zn2(m-d`dH--Du&LCbsm zhq6WPSXkg$y?aIb;HK> z%l*Roy!69=oxV}E1QjHX+JUbRY4I1F4^hs2736fwEW!ne*UEm9yAzAb~?M{TxDana*p1l*@ z29HBhSM&jH&U*6o@3W5VKWAj>%sZa$k~T|05IqPW8i`!JLZqz>fo$csmed)zOi>@# zd{1GNTKNs64#A&XhIB12HmBSy^{2c@x?~?)Ughp@G6}OnbP|*%!sO(zri`fCtQJc} zgI7saEZPMsLmQfsN^086Hg9GJipc&xEi+8XA=KEG+sttvy*}2*$IIk3ps1zSx^=G# z>C#rQX?M^R(}&Yf}$_Xea+0$Jw|1ds21ibzNzMnM`gJ- z=uf(qNIx?Vb;te?SJF1NvxrZgM)i**lI(5%!C~@_va+&z1k{XC(R~YOu!G$k&#NLOMsnJ@LLmFNv!0dZ zztZpJccwjMJr;`W60eVr{t?Dy>$cq9@!nTVoo)^qB#AJXOX@E5#UUE%tHA5T33vQs z&hUg|O4!6<%?OiS2l5?x2Y&?X_Zu|o?gyW_erCb`@Bh_iyQ+6YUD}{YNQAV8Tv`HY z&b5t=qA(M+8S-bna*$^IF8fhHTZ=oC3kJm^PB0!InK)l>&0fC$@wXb!nlSkD<#*fb zYccc}UknvQlR3BO=%q4rxvb!+&sOGw5GwQ^p`#P4O?ZXySLA3sE-bI`Pf+rF5yFWF z!tkc}z%NNO-BB01{W9BJG@}kS&ffi>jJ@uYD?Kt_-M@xY?%93J`nyAsI$+J>!tC8j z#8do9tCAWK5Dzv1HD0GgU04c{#sUzdE}+pgtCb`trJsB8^om+%gsnOwZNRYe{}yA} zzvjyymwvJ5lckGCXUs?+baB_Yq!$LoJqY}xFK%yt-N%)uH8hxeKhnh=>I-|=rB+v` zi*($X%#f4HDps1I@er$=fdxC4>k$7(iy~Kz-8TNEH=e~GKYZ_B&m0_uT%jg=QBI{z z@sIlY>#rwIo*Zw6!;T;VDJuGb12V3rO&WOam2=3$)>NGP(~hEL@6En*YGQF!J6RgA zChE^2LxyBTHQ%-O&DlZcN7c0DFX4gs#b2Nsv6a8`Ept z1u+DNdJUq1{A4`P)15}DLMZ#h-3}*k&8Qy-Dp?zz9-X2y^;~!dCRFJ(pDK+z^ z34Uk;&_Hio_i=IFJ|cV~nZ@tDaP}MXyb+akwA3r;gO7p*$Bsy!dDR)#sPoPk zS<&GgKYYN*yz`bTV$BCMb5N^0c*$2sxiH3jZM*H;ZABBuG%*pR@IW5fK~zS1;fs%8 z!SeM4(w=%*{>&_fl2@Jef!BI@{j2-qLI0}ctQku^b(F?;|Le~`WCy7#|Ni^$UwP$~ z|N6DxeP~=(_x*j>*A0@%<`-=A=74=6xJ1!OBq7zcw3Q(qB>_tH5M^a3+38SCixBK! zhbKwF6ZC8PJ|HyN-^&pg99dgg%qy7o*GHpMI+f{CUcO3cuNMbq36PM8j#gx^=(J3L z+nfXG4L63v6Hds$g3`5oC*7xbc@ps;nn9ftb6I6Q4cnR!s}f0I+PK=rti=_TbqvRj z(D8?k7UCVRzI5c9i`J6=z5VvvA9>`Fx1POuSkzI$pm9^la4{PXmHL8Uf6k2{@M0Vc zOKwh2O`rwLi!VN3b?^fQWlU@g7q)28qTO3Qm2Nm#C5jI^1^X?k|LaM2)Gunc1(jFDl5ybRpY#2dLPgBykuH?+|%}%cl zy&Demkh*dSc{_B|Y7vrlE^`FijkAdhX|u2t zgwab+u`vGxEE-r)N)>`1B`zB2s-snUtkd)u<<@jOV8ozGHF}h*p|t#K8)l@8g7| zwSmbrrhhL#$f>EfQ4Pz4q{ipH^p#5zfsBnb^%7y zPfBmyEP*2Rzj0G|p{&*l{bNhM{DDWFd12Otvu>L><*&~s`{#O_8n2#tPp@bvOwi$) z^tz&$S05fi6VhcGKu>zK2)JBuIE z@xHShl^7nikFf0FXB{k}O0Rn2`NPf{OGniXwSLb>s}RU08js?m1qhU_M*5{Bdkf83 zsmDqi%K8vHi2ui5b~DE*&f8zxku0W6=N*%iSp{*QPw@-qj-P~{r?j+`dQz1vq+e2- z(*-rfsQ%%oxPRDvUrS3){;{n)SWm@;T<6h8A7xb@;pQ3YMlTZX4uJ$k#8Kk~|Dhlc zI|~;hsXhNxXhH%oSy~M{K+7$o{VGG3gh};}vT%H75j~7f!r<#Fs+;U~JH2Ll^rA6_ zY&|VNVa)gFEYxYCrIwzE1QJmf3JBAe=oP{R9N*dYk3wUUBNttVlcFWZB6b z7-tmB$3m2@ng&-W10XmgTxxe{8>BO=G}}WzLqsjf6Z@W^i1gN$`h$55GGdWgQ@wXW zQ&I$@RDw&l1lEQq`9$OjbmNz}B04-!iwlf@amFq)56;JMA|LY)HVsrXfn;cb9|t+9x;7d zCW8Cm!-o&Im42m|eMyfLxAtnbWPJANhp)dfA7XFfrBi$ryS>@<+JX;U_H6WHl{HNa zG5e_S`vk}#EC)->wE0A_7l_gn5w?=Jfl%`fh9g{{3~A_lJswvF z7>@G)0uJ^l8`yPyJ^ck>0Z5|zpz%d=1S&LMKp)-YnTaa&wp+R$ld?F54pxaCS{;qY zJ1o@wg5#0Z#?Y3!8gQaOtI&jzT~`8AP2Hji4LjBoO)InzLQZCge-iJ@o%5VI@*EnJ zqoROD;#)##P&7v~Nn|1B;#SC4Gg9kU%mVoDy(uZNE-h6r9j2mye?txVeZ8pD< zfYv7IpCA9kE+R(m~ zm*#VWusaVu^bpIdkeVcpKYLg_c#-sw?34R1mTx9COgAa@=sW^q(geoSkiplu-_q}C z*$v;UwSySC7OTJzOX(l0qtXMca}zp^HMX&&7GIAZUv+Zc9t{sXH{YcSuk)y<`2Xp# zgB&!e&sfZ#xM8pe_MM_^m|eY$n!idSK=Khm(o+N3c*7x99}(co`f2EVH9QlI?{PW8 zfU*VKTJQ~_2_snE4I#56(2)(%!qli8CY?m<@3gy`njHSL!?k#`9z2mZsMPp6KMARU zN0vUbC4JvoMv1pjkGh^359C$&+%u2;ebQB|1mIOsnzLr*(ic7{ykqVP8Uf6oKi^ul zCNa|N7vAUg>(0a=vb+Z`o$J401`EWMlO_v^krTc6#H}t<2xA)(QKZ~uE7j;L%8#xK zWT6RLtWBx+@{c5>VNYB*O%oZqseMPmHU)*p|6bXBWbUL3M$UTT?ez4t=sfPgV=;TX!%8AR z8royoXf8vq17cu;WMlmd>7P|BMQ`*ag|_iafR07ZB{gBjDh{$kiNpYv8!kM10AxmE zn@7^EhXjjc9O}8kj@H_arUIesm>T*9T9WBNr#jpi!-mB9E)LIG^hr&v zostDc)Y2tO?A3YPW5M3l7+AXOR4c~~_@dVzZ9n{k7B{WX9w}ce`-Z7xNw}VoTwhG; z^Jgt)>dpfs%1w9OeCgtUUi8@9C;oO-+Pr6;o&Uls2~jrp;kjVwP-7wOJf}+)BeGXY<0H;BGJWy{sBDEYpqXeVs8x^5rLkP>(A&tNF^qc(2N}R@ zJ%WRnP&O{6`x`hFu7u6Ysz~p8W^UY-pe5c>SNu;9iy@MQwTNI@=Y#cjBQHGe5%iNu zYN$C|5^G^+wAu^M4+I6U!qy~tEEH?{#FB@Vrp>GoOtbWui2)slZQkbCz7t83ih7~5 zfddDVzGfwh=}Kc_aYZEEwVTmpin@u?Ovy7WyYBj#oM`8^Z>Qe3amG!5zi!rsA2*T1 z3BTdyyJ%f9F2l}TczI#RXv=Z zdg+*WTduU5iM}|wp9ms%{_<6Hu$CC@(`JskckaUJ)35#L{a0vUcgH^-zU}IPv+ul! zJkjQ#f1CSWL0MZ$hob^-e(TjEY{^q)aSD?OIEdlJL5~@K4e9B%-ygbm&W?v(-I14H zm=fnd|A7l0c;emRL;5-FMYG@7wSMEzZjJ!=On+>@Bq3-@v0%6HU?mr{&767% z$4^N~L0kyl^=Uhd%2WUQsedpe02>&Y9u;hyVSR3H$cR+LQ^hW~%;od#;z=QV)2_?x3Aqo6LNGM5wP?8WHIP_jZ z3nWxiY}~so$+Bg&Q$3xo?ESyL9o_mkNvAv6fh1_`&uVUVW_EV=H#5KfdrZ*zu5}{# zL-`&4oWS(qV093w`1HE)JC|?y=z%Anf2$=B%FogFKjNIPoPAM_Be4IJ@T#@t_doE^ zv>9_qa5RM#hjz}`Q@X?Mz`Ezp{^k$c+OK|Vxegr$!uQo9a*mzvbxiwyk)HFjOTO@f z8&XH(9<>>|84o+$SM29C(b+3w)kZnUQC`m znbpW4VUI_?@Wm^qFTQ(geJ#8|maYT>igx3v1YFiS@~Nq$0H7v<#XwNk_FT1og_^xg=wfqYO{v*XWX@8e@-;=@m#n3Pz#oTk|_kCQqWc@}{Q zjd9tKUL%`2Y>DjA*_zg=k+JuRR|pur@I|7%T9w3PhjraAegZ#ZHyeMZ0W$^(Cd`%U zZ*jo)HNX4RBlquh_U}Ux0j!O_q}qv|4CsEg&Oy(3To5-l(VIq&N<+}e&(+u1?HlQ z8ygniKl8wI_sMzBKI`iu^9^l_t+@^w7u2c*%sByf?WjpsNE@& zW{&}c&`z0pwRn4hF@YEXQ!|qVBN6iu4Hxcn=&`eG-D~fC+-PrW?C8x0A*-1*{fzT2 zJMh2*G4VX(5Kmj|xVJtMYza2-IcFVw?4h%4ZEu&%`t}Vsm0x|ug)NQEcR#-2reFQq z?~0x}cXK@EMW}_o8I;hOrylblS~kNxqQ^QU~UGXDz;Pq)-+WSQkp3kWoQVr!Ac^rZ zzOQkDh{Hk*@`+rOhs;j%5v%yyiIvj;k7P`kFhabv(Z=1V7BI{Uz{ zLXWajGbB6VoALdYGv$B&>l<~8e;e<@JN$RqwQ&J}LX3oQG!wEIbYFCqBCZ^fK()Oq z^9+h%`ojb_imm*f*g7P_Y0yaQ3VCI?0xWw+E}k#OR~8B1wCq@)?}===gY1ALHJD+q zBjyf5LGoqmH!X{|63=E~lFHzFaQaR3{_btuvAliaX^{ z;}zH4)3N4{>}&e$o5YKON>C~2GhiM8=XmRbJrP%Np-+g11b>BEqA~*GSz^=eC%7Pw z%bTawd`E5}k-^Hi{6uSaC}>}F$o|s~M_%I@3zpMT`%Y2k!;YdaXhpM}xVl8o8l@#O zYMin8ceoy0aQxZyv){~|YcDzQ=KovX(zxYMPle(SzhQT>fd;*#dN{R$x%Pz+Yw?6%+{1VBa=3d)c!I8Y*tb)8E zJpCRZ?%cO75+5ZZ@~#yg15TRIArs~CJw)gu`9zF|j-_U_k_r716J&GZw;S-ZS6YgLZqqNzO7qhu>Bxlu%%5&$={osQSL?A0JKTj7A zAYz#LHEs$24|4~UeZd9S{9JS)CjJ+qJroJnfAHA-&(+@l%%xvBa{O_pe=o23-8HK> z-1+C{YFm8XoI>%HNHiLJXS#|-`N2%$j|omK9@mV1R0Ab!pv7K(h6Klh|Bd2-IDW&g z;VRGwApj^raaJXG@W{eYz{L+AZURxL*#1Px%yWKlY3Z+Sy|cHc2VLs<^AG&S#n)7N zHpja+pMKFz(Y!|%{pr^)zP_Wn5>ZKE_2e0so?7z9=O!(F{{z5y=A8LQoP6<-M;`g+ zn{V=jgZ3<)vT(-(ug==k`#${TggM29@K6sBgRQzhum`pmosa7Z_!=N~A~Ap`iq14fFcZ#aS>YWC-wP;G zgV9P1p>QioY!+Wo%%nEmijzfg!eu7x9@rH%)a)=QZh0NTc-+_$aoRHObQNbpRZD+m zCj@~1+1x?am(SVP+p^4)TOqihc<HUBT%Bf&KVn>2u6V$co>Psw(QSye;DBPFNamb;EFF54bhNjL)`?|8~FXUAn z#dzp8-tt=R+#g=Ga2{xGIIyBHvKR(7n~CYZV9&DH+jsxyzzuy@O`Nv3+vPmx0C;=i z;HDp$i$_mCFml>H$2m$5pHR5ZZMPlUy6#bzJ(M@~91vnQWbvln_VzwAiywOUp{?6{ zCcFP`N56iy91v5SwT=R56N|7;&~0_;_eV^8kb zYkzSMh)qt{3&j=F@jF3~GY}hb>WPYx^6Sp)V4`krp(6j}XZwdeEz7}z0^LSY^}*Ks z51UbVSBBg{D9qPhdyV)4Y22m`i4HQ93|WKLpff-3RTB#x^w?T$_I!Ph|FuQhBtSJx zd$CV^M9?)QXpi*@qUJ1=HLT{8b7i|o=$T5v1tB%Yeg)3NH^0gMZ<_rZ!XCrk2RK92 zi?I*dUAVcGq z;=QkHt{ibvv*sto)6Q1RBr*NVo9!r^cm4I#ZomC@6i#1x=8v!RHL@Am-I@^7-pbRD znK7;!8@&T^SsVC#W>1;^b1DehfzL(F;T(V3bY&5|={D$8ecBJI9R<~sXCib)gweC~ z$}a85@_l4iLfuOEg5ZJ%xZt^h;f1!9bw5x#DI)lXyIg!|;}3lLRcf9=?2I)(L*or@ z88zV%+DZ-*=F z24!qWHVLIwd9t%{QL(>ItGRL~F39HtOGfda;$(?Ols7vV>@DzyEYy?MFtCj^Ke3NH zThtH{!L0r<@wKqD6&ORh7kC%#OiLoqA%umdsaRb~TSu&IOfT#flub`17PjoCc#%}N zAf_r368>285gffw?zP~{_e`mHZ%e_Z6@P0F?NQ+ELrZ%0f^)~@^x|XIUcq9?p)ypH zDO)^E4s1f;6z!`>EVG|_Q66%a>dMjuBe)O|XukH^6L)ZG9=4xpU5>Xo$f`+~i05t4 z-j>(U*?>PPv2b#yvaIk@!M59v{1HY1kha?-FeppaRMSL8J;D~o3Zhog+Z?k`!6(?hEen~hMxF7)6NALpM4=y0) zK~OI^-Yb|d!I{_(6JdN|FNygGC@H8wRN~H~CknXDuWotp<}+K{dJ3u*dMXYBX^6M4 z5cQt8apr>1QG5Tmr@bz>Xu_S3ZPr7zV<(^R!^=ILOV+q1-R`Y6IoalnAdjDP;; z4Mg&WTj43_Q~L!b@W11m?^O7=xYP+XIviS6QG|dind5?V49t_VLI;sbX9r7tO}%wb zx?GOFf+IY>#o)D~Dud3C>B?J7*3gggS1XS>avq6H3Hd<2IrnlA6b)1fM?b9M-tDyI zs6GC;WA{Gh8nH3Ql(=VuP}G12u6fIjh9wbO_TLVM=3Ib_`G=PSR=D zvi1>MgHCBPe*tMBkCAw)e~}{wno~@p#3{WaXZnP~2K!iIz#Kvq4EQiJLgh*A8@Vve z8zp9Xe9Ju|2gB+Pqo~%m^rCswOAn;Jp!n$RKyxPS=tZ&-+~}x6r`c3OXnVWJL%7BB zgxw=P$q(c>j-%6Dd5XVa#+05v$Gcl?gttz3;bBpTzg2iEp~e`IfpDNW`F}ixQ&4e( z%_%YvyqCkS0$njf)lYCOvH2nlOl0vm;ubf+rzuN5h9}G;{9U^%1)1UPIG$QwZ`jcr zHT2eQZOjaNgnd(zn^2l*-88&L|x*G z{2+z->EQ;CBZR3hN4PTKl=`5X$O^Gy25ZEJBteQPVdus47!pX4I@eE~bKxEnyiPl>CW_ml z$Bn#l;wAQixe_}P>$zx;sP`#c5Jn5`j_2GeQf!sP5d9`kUX_1Ze=Gsf7sNX^0ZFUQ z7hI5yd&@nj!QtB1?yVMzpQ$78fT>OkDk&}oOOFO!7L7Vys{DAyAWTr4XGYp3US!T5 zQr}82lQ1qTJqj8w&e+Br3eR9;V|?PY{gild1iFLt%?@ZiA$>-12ig8cpc3J6+1Ayz znk-~DPZ2;OrWSh{QBcDH!QpUc+OwG%7TOnC31}j!gr!GYkO0z#sV3Y4K>&DD{-#<*jKI*1w`G~(-kP0c(hU|lqcfBiz!6~hs_ru?7Hg_)rk^1u2!*DfqRD+ z+=N!UD8jST@wxj0eBp!Rar$6e5#@U9E>USR$!<}2!f}K0Yfbz>cI1-7;qto2>~oNN z`NDapqi!L@3_>m(cH%6s;uK|mBsw~{IY31+9>sKq^`cyhnUk|pg0*NqeJWmyOJF+* zHZb9QH1uD5_7RUGxc8BlGKSdN>pcNd%=scL}z1a@MjH z)ql_W`?`g%zO{1IhcBZeJF_Nl&qKb3Dk_YJ*v6Y}JzIpyZ7PSL2pivgmpB75^`eB; ziuiBi9ca`DE(j$Iyg^|5s!U>LqR0W!Kf@}>(w`%CsJQm1R-Ycy1;iA)V>8H;M|b3u zRupR$hY7ihl_j=QICI!~AFHg`(^i~c)$TsX7XBq#IPqvle8bJTGcH!-giHmB9H{6J zAK5(}bq)$(1Y(tm@rNS1VvxIpm9zRZ}}BGU^yAuya3dIvTSb&=@sts*s+4pRJE@Km8uK@Kyv7P`Pz$e>I{yY(w;DmDGCC+#LD3-;#_`wjm1=WSFRf@(CymQC% zV#2I&K^!7-6k@#&H&>L@w~KC7&eS;0Lp)>qaX~zXOPt0dcM9uKmW7|a^1=nLd`AR<2(M3C=@bWzYySFwiaDjyWh0iK>a zQz6PST{K4x2B(R{M}||wzK1QHi7AR%IvgLkq~e}^vGPYDpbOK1;Kdju;f0$x4oT!U z9g0UMLL}hR2l$kTyUb_Nn9?$TQDoWLcd-7EdCM$ig$|;=KWwkN2OS*tF@sh>47y;! z0^Ayzwqr}#T$ENRj0doF32Lqo8kqdV`+~Nxb|Tm_a?1dblK8)9$R(^L)Psa{BdiT- zOc5}E6XYmGrQMA6#1{&0yqhrTV&kJ|1va1=b6~bsO9F_?J#N2tlwB_|R^jj=F|OoK1a4 zF=T;fS$!dVBSr;E%7PR=iWy))1ecA^h%bYLH3OSk4AT%JtP_^e)bwK62>Q!Ho$z%C zGQnvu=R}xT{Y_LESe)p6%VIEhd0Lqw=V3c#`Cabf{TQSea|AF;0@-YYtM7($wA6>=B9 z!DiqoQ$1+09HN|maJV6j@+E>hov<5iuaBY8f5fThIN}|# zmDfTY2{(l%ShHeGfD=&mM~oZT?;@Q{f@Qlcs^X|~l%EJ2zi9a5_(rtr%qM*tgebXn2E4BE0u<<#*j&6&_QV3OlqdonO>70GmKc?ezt9oiU>Y$ zme9kTF~VSr#y_IsMjALMhc0J%?^cuqL_v`A5(P3d z30oZzTkbUR+ls>n(*Xi!vycavWVvxX)P%-1bO>j>R(dqVs<`Te5?})X@fv@Qr+FD` zuq#+b%wij^p%Z{1D`@lYyxiDnmhs=1zUyJr9#!$*@#@xdi;qZ(iQR&EfF>ZbOdu} zKo|h4A_g%{+ zeaHz2U`4~5J&IB_O2BX*)c%Z!5;Gc`t)TkIK;YL{D5L(a9-YZGD|8TzWa6!-7m{e1 zJayPQYFJdu3S>8L-i$p&d1>K6vz(&X1j2-;N`xcef++Kvq>Mtzg?W=Wumx_Soj`z` z%@}*aHtz@T06-hXM zc-UEU9Sktx3}LUGNV38~9Y99{i)HsmX`PUi{z>>6#9O#A85olnoRrmMN_~wJ#D=`eAaCt^ScMcN$(sWiY5B5yg0q|CMbK z3>)7osGRdq1uZD`vsdC1(X_pwzR1+t7Sk=qe7q4D4oGE<_;`=lq8&SNL5LH^hQAL^ z%I>woFGsHn&t3`_-1r+-gzyL%01^;v_sWh(L)_T*6jvw;MHE4!8|eK&`BZ;Hs4RRA zL+JuJ?9@izi%;A|C;QGB2ltx#{VIgmv3f*kP5ykMw7u$d&MHt5szbax7Yk$uWsSPVx{ZGY)E!R&`Hd<%9$*f}FZ2OT+b zB-DTW0LUDAuIODM;}gUSzTmP;#m5^wDy2~kGlN5vEsM|Rp9G8{{}TD8!=ZaC`f)+f z9ZWv)C!m7bfl=XbAa@TDI-3bjRbYh#ARK_$Fk7@oSb&ipczc%^tk30e6H+hLIAL13po_&^mM+4u)DE- z1&1XO8q`R{I<@u>EV@Ep2qtt(d!O)f;Qink6rv7R7H4qBQgPd)@LE<4%0gJhp~+Kz zyyhM=t8m2`ryhRU1t(m1#jVF3cU*O8%(j*2L7cv`MgBvWNi3%aN_RuFaia8<|p1~*a^Y)*%`noF;0iCyyFFT<&nDXz*Bv^?*6zD;wY4I2*n-G zLKq97PC~N}F!NOjdyoDb9>WiYsKDYO!Qtz-KLZzp*F&hV1Ue<;J)b)o$-7g8CdSq8zFE_DoA9w2u~6l z8mvuWg`f%G9Y+9LJWUG8U5t5R1t$e8OuEx9e00F?nb8VHZ1%4uqkF<#!py~^-nnH{VI+WE{&I%nQDhbha|L1>_RKm7SE=BTgEpGlDl(h2Vp@u3- ziZ~R75r`{9jAzkT5We&oG!z*PA`;waB{Uu=fmRcb!Qx2eDez;gE95L^0i*1X&;%jo z6;>Kz z8-K&@13jnDz8OIqnyE~bux}FybjM_jeV~;d&iWVhdd#7{dKJKrMU?0ZuDlv9g@lU* zuFy(>(OdO3!3A6Y!I=u_X-fU7r9Tu3u?ck+>IvwD3^f`aw`;nvjR~D6LFz7$^N+zJ z%1@a1(3QAOfSiSNL7)Q{844c#QPWSDzyq4HrV=Fh0oy&053V3e+?6K_W>$Q#DC>aJ zvsGbGkJdcgBZ)00Y-1ptp*1f5^Qw(4_R{aZ7u1Ar7p!UQ`+BE()|2}@dV@;b}#m{UL=nJsKQI|ej$#H7MI2KeFCbiw2@STQzX9z9!U6Ww*q> z4biaBr5c})rK=Ir#!Rwxph$`f0#xJKD_CFfZ^{90sD-uSgB{imS{4vXgdSmE6(Z^> z*`o;cK~3@Nl!GH!Kw{Fk{NAW{Z8uMmWNv703g;$RTUsqV><{00xwqj}NLElrVd){l z#*_;6WnVutUN8wjG1kL&ZFiO8R$UnFoYP7}pRhw5|9JWkT=3C;J{AllT&_o ziU^3>`Y;=p@I(Qm@?#+_OOeu$S9k9t$=^y6Vj1<05t# zm?kj!MCVd*mkkpaL^aFwK_gBH;2^>yjCKiRDmEUtAZIu1xAAB8?=WII`6M(jFecvu zssm)8XCs_8H8XJU>~6H%FSaHsw(?3?LgMF(gbN`6661FizHy--o`&Cq)xt(43>}Wh zO~0jsIs~o|d`;j)!We!HU}ntn?t%AZMX*1EFA5p9H^3?K9I;_qV#}S4 za8iu8A*;M&r8iR7T4-_+@|2&%FLT3xjJFWRr(FjTggZ8UVn1KWY>?3fSjwDlVv)x~ zbv>l8`0l&!M)wd4S+uZ+YP>7u@J^a`Y_BLQYYFCzsSzTK1VV-lzdC{BC&E*{H;PtD zyamAlz?B`%7JwTOKCnLX2D4rNMW|TxuGkGh7NZ@6oz_M4Xqe6d_Py$yN4|J!W+wDt*x!)9;9G(_5@uk z3R^3_kBE)US_FB4#iCrr0ZiwBSGXm%`F2Qr9rqBme-a7_kqRWQatH@_@m>NM;^<2T zhPLANCq!>gv|r~-2|8HtvGE;+v;r;{8fKUUpsT??Yr-pJJqj5XQ=O4Za0sX|*r>1& z(2_>`2NnWEVb@C8xCtc6qEeu5K2(Bg;M`Rt&iC6w&BWM|nKvT~LC7{WZksmk7_2U+ zI|A-t=C~mJbJ9cFW)9`%@NG7K4O$wNdpsX}^Im1WFK3n5lApqai#c1UUZ3+jQ&Xf! zG?zErEJ9VNI!L`1j6&wYA%oy!3lQc9+RX_>pZ;YIokb!zfB+~tOb~i-_kz7^SFaY% z6NcO90E{`D@L4h#NJeZX5U|EMUEmF$CYn{nBK4EIs1i#!_Au%S@{<$`aV!CZV!(tc z1}A#uDLgYSdSm^9S8B;N#1@$|RU{gG7ue?z-#o?qMx0y1&O{}J=fg8@deL5XyyAaw z0Koqi+|jNHpN0hz>;bJ;_@Cgl;_qD;ije7!;3`CWUSD2L?tFV1tzT0r}~(26+)B2j_h=e5vGWXqAC3 z^h7*jx`A>VW}$fMf!W+!r`LR2v8+U85V6wx-D^D#5<-i%M?L$Y6)hqQ{4fA`A}l_M4T>WloL+R^O=k|=f<6(Z zJG+o#2}4kcyOw|`QD`2Ue>PAG_9p8NTo4-BY}@0EQ?EGjfO+dS zHG5uu`ITwadBQ1i!ScC4`gaxQjz4kH3Z-P4d6Hau;!iDWbL#AZm!N_Q}GXt##J zE{$>VvSrHxoxQSO;Yxx{z?skY5=08&4=6ighlG~>6Au6YKmbWZK~yM-hUx$nEt>KZ zK$ki4L;%8sBaT3O3Ql3?azVJ63gTZQJ4R;?6d#*B*lh4?QI&H}acup+m|=^AL!ynq zBU)Z(JCbuM;e<;tPK=sOSNuQ)vkIn&juR|iMfb|d3mcN7eC(G+cERHyLI|enpK2=9 zjuYBI2-Ft^GEHGi49jTL!{+_YdVNG~lt@fu5~njV7FvP161jw^R8XYs`FPI;I6W0| z_KAG*t2?PC@I2(axSrVYywD6&uAm7VV%cVac?NH)pMo=BRABNE9|wUJT)FHRkQTwb zJ)%~)b3B1}*h%GBF@qGK`zWU^F%eA8l&AA=V~TT2$BT|l<_S&&l{uY9pkzPjQ-(wb z#qpKRmWlcy)fg7=J?I05Dh!pK+a0{vGw$e?){BVbCY`js1p`)vL6o1!ATVy{j4U*b zy}|CW`~{Rj>LDZgc4$aB`xWrzAp&G@vno!3?ruF*VpT&2` zB#LYf*%s&;yBI3)Xa{4CBte->%l5X#NE=1DSnWQjn!%^kJQscMck`EToH3~&UiXwD zAEkSae;g@=ci@|*pV5s#0m_EGn{SWjs@l6n9$j&w5EFn1tU(Y>)5D*D^$rvWh$eod z9M}vDB$&X>j(iQ1B@-8vRSLF>11-T9*^%#;Weh5SY71_SIQxTJkge>*Cfk>={-KjN z288AoKtBtmMl4w4dafNVEX$w@9=NPDNqFRea)R4WoLnsI;GcsuPr6v)8QlN){a0pA zMDgoHbt-VaVh3Qh%;=(^HggD}b3hz)Fe~k8CH%VLiW$H8&2Kn{y;_IprohIBn*|RT zk7%mARD$rXOADL$hX99Hv70ClJe;_TPfi5MIAlyPS=FMsz=&4zu3{ZqSLdWpmd7fSO49g zKh+t00u8^;mQ7vVyny05=e4)_{A2wSj`8M|W6hwFyX8qwWF4y66K5Zbu1rP%^vOzDaR)!ybXR|2 zm;$m(q_tr?0_IWS9~n9bwdsX@Q*bCjw{>jWwr$(CZQHhOCnx5KZQHhOpCmWm_gCGA z`|{7jRL!pG?wM-$UVANu^27BOGiX4*hyHeD4m?EvS?5t~H2;_-E*uH5P>I zzs7q}uK&~_!-+e3=aM5c5Jq0i4VKOu{c4&LJVDoSfNn40c#Y^0(lAUM+At-Mv4WYE zY(`qw6BEc%3t%FfywM6;?E5Uhy=n1_|A5kjFwisD1)j8>sFk^e? zU7yc^9^`OZCNh{I3=#r40*}Mg#U3$pAhMqw1$F4OPqxRVsi?!9H@a0>%|V{0G0gor zP5{oEjLMZkNYC>=?y+rjEr*j{=IZ))exF~-$2gK*DhRk{o}oa{$+Rchiu6;@BfQ-e zXL71uI)V!2sKFZM0*Wk{VaSry2DB6u1p(Fn=(a%F0+R{^B2&Yl!F!9bVJyFRXI#jw zrH51;?^_?4hX-+&@mCt@UI9^Wl|RA<-j3Q_fuC;eiHUa-F48nfNf6G5fSLa9=NIU3 zxOZ$Z8b>J3jTFm#!xR`KYylaGE{6We(7z4M-|bJ)0lyLkd@eDuy$$E3I2{om>>iv0*<|ceOU~6D(i9sGCyD zDJ|yDE z$-=lIE*Trchj#_LfD`2#P8Hm8ob&uUO$POry3y70wg(_|6(4aCfV259s+Q}N_1ND~wpm46KTydmB41^+PD>^EYtLlS@%2a>4K z5Z{Sqa8a5RVRn7OY2Za@EyxQdE|SmKBj}IE#3eDw^{#3(l!7C(K3am%(66cYK$|BN zDyCK_SYAB>8|gHB$;=T^0#WSHn6P5clY|4zFw&SR6Y3|{S4?NGbx0+W9^OfwwJ~}{ zP%xY=9F}EmU9Ev%EO9wMTWvL_8cG)UzrA+j+&%^MKRlHeO)&rh;mrP&)D;jpyC@{U z^zvKZ6m)gL8mnS3U#fR;Rykl|z&Fv8(BuVAH3Hs>#y4DvCQ;vz%*Xg?ib#A@64lRr zZL}4Owm`#~KY&er1Bfc)gtdcByglBaEa6nj&BI*dFZaB50?E^JXDy5uM`qv|6UTe8 zGXKVf90r+FA0?IB-RVZi#taN*1griO0YqU-1jT16>6$>b1bMn`+t(pw3i$_p4mkU= z0(s=D+o;|>COu(=eo6>HjE2mFJzr1e-8fBM+fClSZN}nGV|V5_b7RrpK(M$q@o!_& zURZoci@P`RmLHz};hjT@9D;(1uJ56_19^1D0X=qpfXD{4bl;FA^|FoD|9h_%f#bZNMC4 zw3uu#4guX|n23|W`Sp=?MKv(~80XyKEF8%C611qW4cv;H0zD+hHG$aE!j_}{x}wX*K6mv{`dsF&_8KOyi^m2)+`i`C<*Lp`96Q2 znL!M$iFI@%-I6m|G!W_}f}xgeO(UB$9BXa1h}RxVcCpYVa?k(zKtPx$r`QjG6o58x z1E7uI6ch+809My$Oioey3?MX#83F|%43isA>R)*&3jSW@r}NTH{6)q_7yX0Uyi%&M zVjhuGr9*Eeu!{BIphr|9ux^NYnI!1UM333~gpH$=%gR5f-SlbL)BOk2(R}=yOW4(3 zl-LiWea42;QVV-+k7d|~F5!c~2aQQBlvtzw>v>o9jVSooRj(M?+mRNd-4&zBOgMYe zorS39fQI{4oY9Xq(3d;oLs_qJpj`Xy`kYwNORd__7VeR}qTU~I@6YVozrwg9 zCccaKx~ZcV+34+5@rxn@VYhlj47u>SBpHxB3iUIHo7eY9sY|+c^jU=ps(Mm7F9oi# z95W_246Ytt=v=bgwc+DY6GDp{_}&yCkx&!bQPv${u(9;LNX; zef1KSc`SEn14u(471q~>!+5Fo_^_i~t5|OF`*_wE@|d{PF>Pi~%^MHgFq=krf=mO~VAcV& z#J8H+*XuI^gDuyavt3DRclP7u5q9CgaC1PnT%Ep&^>~gE*ZBpATCnB{@&(&N$Rm`4m#4IESrxljWXX z*txLKG0DpIQrIYJyvEK>B4+c;yF}`Q;sDH5+iVe~lNePS0$}#T*WEZzokd@v`#k0u zjJ7Du1m&gHxvlaoF80)nGB$HNsJoBtplWX}yK*-XYY3T3>x$3q>@2V1Sun>)80NLs zm(-SfQ0~CqpNmpK{d{dDpQOxmL!?VgP$MY>>Rt4~bE!b)6Hu=y3>+lh_vw4=hZgb7 zPR8$E@jotX-EsJDip{2B_csab%94D0%*WXEPiP$JzZDJ<)@TIo$MQ4@&*+xRgy^hP z3>uj+BQf2R-iM-isB1(z&=bHyt|44j*xOK7Yuc`@96X&-$-1V-&$T?TwVlcIeFJ@Z|C$3m45ga$D__rXJh8KKcQnR z?02k(TpB;yxP1Ish9bigS4_m;)kZLz&8>L_DI$r}gc?jDpXm`dHY|AZePIo?Nbx%0 zv6tH>kDP9QkXxnyClG2NEU=peJNi$3!d@cW8l*>QMEExqeICF0FWA!%n7N=%Hve7v z3E|t&rhl93tZ3`UJEdfpB{@Xe-v<}byhf=ZDW$jLq&QO)E5pqVPiP`0*{j$Ix1 zuYfssSHj?)Glcx4N%@nzT)=Y@=m9SXU=Wu|1i|Qh9R6OPxZAGB{OId!P~T=1IehUUapyKEx9a01kWl2Ai9-FrvRFGpxNXiyMniC4f2yUg@lO9~#7kx7 z-CYa;1c=-k+p$IsU&q~@@|E#PR0jP5t@H@XRRJA$VVmrTA&|7vu(co}!cXpv6QHpp z2e}Y?23;CmLIw&3qM~1DqNZ2dT_3_58EG1=my<$ER(mznkjIOG`u+HA1XNi_^n{6e(uR zzo!!Fe&f)A8;2jH%56iqjpzKfv-r&YjwVDUmc_dL#}kY7n%?bzdZP&I1$bteZS$~5 zYpzxJ2}i`JQ^+AWkJixzW{Vr<5D`v+cmo^S9ZSoO#YdZF0Xv$z=>D{LUr$iFh+U*u zjKqsIJASf|a~m)H60DW@#>vVK-Erh$y9cPXd-2fCGsn^X-lgu9&kZ17`!QtIt6A=6 zE%f`Km#F`>nRkDZdMY~YGdt~sqr*qrUzPDu+J03whtc^&|L0>>25_u;^zA*Hb2kK- zwEJW#;f&zO9?LzHGI-{ttBkW+uvtB-d$wit#>Xdm5qa-$fY|{9u%}^hvlM%KCWwr; zX?*|btU=bRY5WEgKep4T_I}<0ql6I+QjCiYBa8ophRMq-!RlSdkyyjN@Ae!L`)w9j zKStE(n%Z&8pbg5Xo7*Vy=0*Uxs8+UlQ-GU8##gJewA1#cV7muSARH)c-oicHwngEgf%j1c9o4fkf!YXyI^Q#_ z-~89d2K|AM$==}gJzeMDVYwKlZ(G}Ym?+)R+1bGj%-fCF`(xPqYL&lwJxhCcA*ipH z$$rX8Fjw=+N#wy>vtlfNxx?gr#^*gF(rFC4C^ztKTU!iF&NuLSQPF&C$CTjjX$^Cj zsUcreh(6?Xfu;3qpwGJ?{1R=!fpJ|Co)w98pYfP`GID$Tw$0edkM!QN1q)i_Xg*c1 z47;6`;rQzr8wyxxdvEU@ME2D0{Nl~7(FYdm?(y-O&qN6IYhYzePRWpL1twHf`Re-S zAj{RrWY~HUuojnQR}goI)1Jb+oZkv{`+N*R+kfo%2jbGUc+8;FV5YtU zlI|YK+%ot_bZgjIr?!{jr#V|~7Q^28Gdh=dk{4Od>HJdXJ$dU!bc={n*ji^=zV>&6 zC%${K(J-dmlHYozAzwGT{2f*HCQ)PT$j@ryhphoIMlno+|LB%&yVK=K^$_yB&om1G zo5tvl@F)G`#LVa3eCJ1jm0T{T-0sh=k}7V`$yi&k%}rGc2z}3666ohGv67#P&YVQv ztGBhvn_Lnt#LsNbS<%I4I?2S!CYwlu^&M2xoQX7o?`DdPL*uos;SaG_V+KnRve=p2 zR()?|-{kQ_K-M0FEY5SLWX#Zgx|(0i2jxKZiMl}`wJn;b0khEo5BA8p*_Boh8MHcv zPm>3q25uToni$qm6|5Q*k$syi0oot&;WXV5!|Hcynk8i}klbNFL-kGqqWW>hsEbWH zv4V^`pqIN=fyt>@jggoTkSdR$xPf#g(mVw$BMES8WRRI74@p+YT_>~{0pQk057Y_{ zxKcPYh683mmJM#X?pz++VG;xpJXXdSF@Y z?+2y9VE^{(VCz~ylo~&#S%5)JN+gH1djhr*7<}@Qy1I_8u7fq=mx_qqaT%e}tf&BS zsT`mLGY$wUL#eKFw*(T+nk7iPG=z~-z>o@69=SFb91~mna36h(8tn}<7toC?t|raE z-rg8Bd4oaaz!TcsmkM0ppXK?#?Zu5%RqFQEwl`;2|3E(|lkh|~k`Eh>Jz{bWB>6^_ zdW%QX)eoH8ecD^17I^g66Za%;;#PUvp}Rbdc^klPLy)(2c+%ngdD>6v9W?wLLb89gFG zjoXO?!CKC3bcc zZ)19{>t35_t{XiEKi2Bq&B~7t6%1(Qv=bLXu1tOOl^h21gB-=jICn2e&gyl=b5r;{IlSJ~sK z-z|hOZ`()p+>XLZc4`=(w#II7YGS(2(gzHvaeU|sDTTY7NVCv4sx40?;dTgnd3o`t_Z%CN5S*FUuSe-0>^(D3?nE9M%S zS_-SoMg+AyI(ItP72PtrZ|KDD`Mkbe!rf!=F6&#nZzy=z_VLR=S2uy}IjH5?wo)5r zSQ}d6jeG%%9R#P8fw2&;w-9W#u~==w?4_o70ZoDjerqC>%lZ>4-q;zQjUM{^kYusF zJN>7HG1qnz%NwX$QQ!U+4B_5D9-Zr(FMsvad*b%d16Qzj@(w-b5nXS_^7QqLPrPh* z{dASvc@#IN(F4(x<|0|1&l9AtFWP)rsxWw+sSgtVZB7{ey7|^Hv2F2`o`RC{##VQG za~Z{H>B$8=D3`W7thcPHofYSHv!kKT9`%$i`2h_Oj`OeHdgdN;d(W7aznq`lxeSAAV!V6z8P&$3B7=0m%KjceK~2M9Du!3pyk53> zP>qE@AmO8<2?@Rp0^{o%b1OSr8kR-+Ox)@9vVWB~wA&n0at4O+`AIg#aUz}Y1XMKnFieH%gAG-^(`Syef{o^ zWptR3&Wiy>arLA5&Yvd>iY06)Ucj!z53f$4eT>V|!|p)$d`{<+l@D4x-lK8K`!~Im z3Wv^o|L^x|0it#$W!ZyGQgpb~-jgirg2;yOK`Og)F3hw>5WDVYHIWfCy^K=Awyir#@hK25I+g*YsK3*Ng6*AgelmLQ*O~WZv$Eg)RQJs1uGY{H*$g@@HGe zHM|AhVYpp!uP$-=jm|&ER0o2a-t3S1t)fp0Ow-TaC)0SBOB=h3(637e@M~CjOU{+A zyaxNw_SJs>+FPeRm#=8+e473me&;Dab$M<7M0L9t+m1TN75P3HC@hx5b=zlmzl%y( zbUzu`euBFV5%0?NlsmcN(&{=OV_<+IEKh{A9VJobhv6L*Ij|dqC1{CJ=Y5ty2qfl(6%B~3tG#_OhSVOnZ#Z#?A0(**- zR}nsE?nObM*m&-`2l>{F8#@P{z=pbccCGfw%tfs$E9N?P4w6T?ZXNS5_O>0i(vf(kM6yzr~eHZ$i_fy0XiZdFyHo+am%- zzdMV|V0&-NE$FzEH)wv7rbLON5|T1r7=Pr9)+Z>$OZOBp5t%hwfRYY$5FiLfSRRDt z>f-Cn8y^L(J)z}BkP%^&jWHp^!IebeJE(9miD#^oh0`zJi0Ngrce* zHF#`zr&Ea^CdXI9MPsy8#h?n-OR>D*KJ#rDw$7(aGgYRY9jtrtn__5Fm- zFr-`TkgXEJ-CXsL2mQ-ew5(&av_mS@4LP=7i?gOth~zoGARzl>&SW!J$r<0cA_6C@ zvFYIS)Ka6>(o@yZk!7EgH=RU zLgeAZ@`6!D{FV^`{dVhe9tWSnj!TQKoqzLW)zWKuUr^h*=Wpo+9KPtfsyn(27tFb` zjq1en`Ebxyl~RsfG(x+;GBY5K2{0~@y%GT>X~PrSKxG805ver#A_e15@$zXRfm0W; z5>qa*WulUTaD58Kbc6vhMiie+m}s+KDfRQh)_Kz=?;t^3E_`m?Y7MijT(bU+Pl)I` zJ9u&@gay}_0Pr!gk$8PUm+hE71ZTMD_PvIK%OW3+c+Q9;8u+6B`9!zBKB}Ae7M+-Z z7U@I1u!@VgfB0Hxg#pbCKoyV>LigR7BcZY-mp#cpa$Ru$3@&D8(Kte~ag%e$}O zO%iAJ;@^lgSV%zT?Yj>#g&2P-x04RC)$z=b=+E13YS*f$r^Z#taj*bgm`R!?L6m@d zxU1G#Nr+Q#yL@rei9mQ_=tgP?)As0&adh zW@6l6U7VOeNwHWQ&@hc0OPKs6QiKl9(s^hOw|(OBnKPq}LM^_=D;7SeedWSSB$8;i z>b=VONXE_cnpGl^J=maGAe|(>+G#j;5s+sd48%jbP}GTT1z7zyNECFbSLi9lDh}z6 zd!vIwcibm*KDSbkIxVcPV1~zEM=FX{de(Nj#C1KKJTzQdp&v+AJKY;j!FMu+vPrqa zyMzMozERl=gF6t7-Mu+?7;`!miOngdj$U3iuZxZET%xF7S9+WzJ>=yJLMiNm5aYZy z<4(>s_GAiL=kWIH!%@N7z5itj<2#s)Or=x5eAIA?1lQI0fI!hP-kXigCwX&r_?Fd4 zXhp(B#F46qAF`iXavaGn(JS7;(UFo6V=UXnE(|v1CUQ>{G|(#aX^2{9!mE@6HNCQr zxR$rbHi&vFV5H>~(Z-)7Sd=AjO(=^9vLzaF^{0lZCh$_I?iWOiqB-j%aS)^uAk2Ke zUTO#$c>WE|jaadrVVUE;&*(#hD>6Wudq7dpTRalw62!-UMt@>MI?7#VekdRpmG@!L zQAid+)O`NdN*Lh(I+q7$V~pjL1Bc1rvTmudf*1m=lIsU;zaB%zLA-<|Bit|Buu7$4 zW}%ay1|(EQlNZsdF~j(hCb1|w%y1yhcu*+82%v5wIHtHM*}DFTtPvne8rnmp07XY> zJU%7yNDqaEDybpIP~m9{v}QV*Ba+Kd)bv#be~ENUTmUnMM`LT;jsK`n$!4)!fp7&? z#uu9HLlQ{xGOpi+0f=Epu5qYeA*Kfo;8Y`Q6`KQHasB0tijW9Dcd{_YM*zE@iAM~G z+T>@Bgn%J8AL1RYMpAc3$NZzN5cC(G~><;txp{ae%KR(+GaO{{!)E*I<<`6ey zaLkULRa8_AqwA_1`2|R$M+S8+bopXY_ut@ffy$icyWrir;6;t;f000xq7 z6cZ{4Os7_f2PX-N6DqnB~=+6n&(y za|}XY>ZFEbiR=${?RYN8{?NmGmC8c+vSTlF%DOZU|dH z*=FFBk9o2jnFJXDzgeikQF(`VbzAx@CWTzxdk>zX^egs4ZFbL^Z00?eMNF|2QO%%3 zq~MiBWz=f-0U)gmE?w-GTgH-_f%(yYf*Yx%l;n)~3E(oW>M~lQy|Gl4?B`2FjTf*V z|IM7tWobZQ@_;ZjRDZSx#az*aed&-g5QvA#HO7!&2_=s;TaYhf^d9$bDaNDXW{*Me zpH(4ScqF_X5VD$rMdF9_>HEeGWu{+Zj(ViYl5GRJU!)a?f6`iNz6=1&g9zkX2r^v| z`M&g)szHTSf9*)0TK2c{3;SZ|K9(Y~Oh%c{UvjxIKFH8;C&S0fjUoGBh^Wl5fJH4B z^?7ubP@aJzvKX4#;WCZ;z_c@C^Vy+f`%)k8F4qazs%_9!5HE_=Q4dT6EtEfk_AAjhxyFL{9Y+VaSOm6k}~)4yZxUWn`R+ z;=>i84e7wlX zo%@^wjLSsd7#HzF-%2CIf1!Z7YeyHvlY_V+4>&EfA@}h9Z-Yo!`Juj(5g9i*c`@r}d8+*Tw`!}fl?k$|dmRYw5B5lzW0nOF53>})i*fqm(G zf&eFXEms9}SnPXr+YcI?%8Ebhyego2Y@mZ^JQZFTFUtwtz7Js=!%X1_p>SqE2l+lT z1ic~bCsa5`Vk4sCVNICqALL#pR7191} zjY@bW2w3+>N&M8uX#D+CB?)WQH%N#9^^hcT91P?x*U$7@^sIq-^diOvD8l)AESr*s=!1Pa zx-!8@CBp}!!pvV23{#qSYXZypP<5LC2^fu(B81IcNbzhGEpuTU3X~`zY?7VRHB9W; zrso^6ZavF{4IIeev5;_i%<{g@`K?rIlINxz{m>-SxeMbq(TUb5)#ETzzH4R04J)5T zAZ-$LM*a!zYK-sSGd|UIayupW)8eluPPFKK9KxbIEFs|gYC~r3Jca^!}VDzn#HUP%49&@85pkqxlTt<}h+e5H}3xz6(6Cq_04^RNRIs$yQm( zA4bEl7n~4r+0)%ef~|q8dOL8lxk!fL+p}FeEC=HP0k`#x&EQg7dky?jCenk16b4eK zwA~o@Ra{A;RnH}hmQnS5Ert$>LG!#XYP>>(}mIxlc35M z4ch1z%70Eh*&i=r)~u0?CT&k8ohelaNf>{16Rhs3W^k?H0n8@Oe&J&~$z`o~8<1NdUnVrGNsJ_;@i*wWZ1Qv*$05WqnY;Bw9S)Um=2JL0*82Ge|4_`m>X5Br2!iB(hofM=1rbRwW=kfm z(?^EiAKL1ldMC39i{OwDmYqr1>Mz79p~olHUznXeeW~TbW%luv^Kuhlxo58C*SdCQ z1$F@U+=a=cjz*&m;va>}*x6SjL(bsqQI>*7)|!EFP@^(*Bx?k;<)NaPF)8A+ zcMTwN0dGV?D;qrn69?}H{g0=E>^0LoZ`>rRW-w2N>{n+wo|L7UV1doKNQY~iMVWI}7u$)+`(gRen6s63Mb2-`F zJ2MY|7$_@Vq7L{+(pkrfcCw^ofhh@84K=14WCH&Sv8r6;z~-1nB&RyPb$80d9nplN z;Dbc@RQVFBBGwght&JPWgN`Q-Md`!lfN-+@zVaCJPhHOkRod`5r41|6PLMh-+d!Yxj&P+8K0lq^~zWh6@`|6aI+W`PLl1hVFP_BXjW z;%Klc|DV59_!<_e4k)g!t`)b_V>L2PJ5)Fs4VCv?G%9|TEcIqtNLl5bF;L%8LHy8) zdn*tRw6wH)6l#MkzNK4I?-hA+_Nb_+RyI+6`aZsQS7r^uTed7&a2|0?()gP3t02nSb7^vHoFsN^kUWIysUnF%{ zTA|~|7j!HOU7n9)kNYn>-u6sZM`~gDZz)n z>KXD=T~sobrprxK+N5f-%bcgMm;{-eO|@zxR3PNibS9|GBvMJx%6?4VU3|G!RnIze z767Wey1B(@VB^^f%m~AV6}2WC8ygg_?>+{#+ATKy?o(Us&gYMdGVpwF1ZR6AvDnOr z0*_XG0FTU-+!^24q=NxLA-Y{aN8`NpCIOpANr)bxdeb#HKh8>3`<@lbzr4= z7Tv7;s(QaoctxIRJd%vEuHZ&1l#2N*RcIxsIWY-8!$Y}B;TXJIE7W9x9Iuk9TxMeV zLsE>Ciu~_a1~vC%rD|2RQ|kT%2e=+MEsjP_d3i)l47g@DDL=1lp;gjD%F2owoA3+) z)=gRya;oG_oB;`Wo`(^cNd5A~4EL>0w-@UsOMM29MO6?hs7U}!C7`^`4cb+b;_A#Nwo-Q8pW43 zYx|#vu8lq+*L3Yl@G79Yejrs?)mZ82kQGYFW&pweXAHKt}!Tw*Tt%p+7Lm_ z1c3Spqg+^PqM}qxRL9iZ-0a|wQY0y>s%ff{mvvFroSD$@l6(|1p{>p&+`ZAO%jrCm z!Q2H`4dw{pQUNL1PWA(mrXocaIbH>uK;4{1icoHTw%H?Dq3j!At_ zL{-PMqS{T+szUZITHW>BKSpwDStRw187l1B^EBJZG z-HG)(E|$tFz|jCUPbNx^KQ3w!pUA$CLul#%C#%*n>FlREk<29=>d?9&;9@x)mvv|z zuhblM9;qrlk2l6LD@Gdme!g5A?EC}yQxBEwU+Hv7hncEk`oO?|q8%8>XqKvyM;&}R zp-oQCN}Ce!5ujM1tyZY&YyjDs1Z|i@uvMSXf`Sw06qX+IMWGXJ+}75%CKRkPG7zFP zu`F*8Q?oWui)8;^_n=-_2?=6+svl~l(OameAd9B5D&nR((!r#wmM5tkxhij>EFJJ; zxmfyVOBGqJaKb@$raWHh- z0B@{?i3D`W(`;4w3S3Qrs%J#`(Vhk<^PW(Fp(zZaIs)Z{17`!tufHfcx1GvhWHz5s z!N8VP8R@8$3;RmRRLNj6l_Fc_tKy16k0|kWhwT&cN^%@Z5yG1=YJlqZHi!WfO3}zj zo+!}?w3Vv1b_%-(#(-j5S_T5PMy!BQPc4j`sG91-1kj;W5wO#xEhjT3Z9Z$O6yY?M z5YtPZ*8#O3R|k+sM5O^ggMC&M2&|4FB5wnF1RibImP+vfW^ygxal_`~SOONC6gM|; z0TVOdYs>;9-BkVDql*KiTb2CXGD?z%G)|UmSmgR&z~H#d3H~g7^S9;(A15sI@dUT|%Wv`YA{W*UyV6*@C~k)9V3s zvoAr2&-WLanf<5v|CYJy2e!0he(v?X**kj%9W&E+>I8=3^wRG0y#2B;#txyK6IP0g zW4dEIi+^qLpA^5J%GZf0z$DaXwC1XbIpXVfi0k`s2J@Tr4916znSp~Vf*Mw+x7b1W z!)Ao_V`&}}BRBB>+~iHcz}&3>#1UieQq}d|RYn*-68_IC|9zab0RvmbotkT1F=w-S z?4MA7DPSTX5ot5%`)gUSLQ)rY2+4)9^ciOr0m;78qO>z(HHHk%hjJ;pu>i%7LJ3QG zN=MwEZ7e0M2PlcgTV^R?|CGsumo=Ag;v&&XSqDiK?_F4kbwCq*!h$pQag62*$EA{0 zVwv$BX&IK~=X92KfAVTns=(r~Y+r66KTKtO44v__C{F6ctAm9-g1 zra&(ALf4+dp&Z6(2-?8}c2Qm~t2-#{!ntHUF#IFViXW$`uo?gFXz3M6rT`pr#xI;n zDAzie&FAC1>^vF0e;gw7sN}^*3Ji#(zF*hQbTh$S=267D95}y?8kU(e4=gu_1s3%P z1{A=OU{p&jxHgWZ45o1!3Bx6p+`rjh$?v&1sx<6;1Q9Nu>YWZh(vH%QbK7mxcB$cJ;SPKE z4NP6ig9s{Vg_em&uAk`rcdx>3%Btse{>Q4kYpkYqCu=LA@cZ8>zdC8#9|_EOEGqZ& z1iNHxE#W^5e+YwB=JVT|ZPx#y#G6vh1YSh-lgM*~@+fu_+iaweGpVq0svJ6(P=r6A z3A2XWMq>qd5z#08Yj{IY%Rf@aaW46-Tg$w$z5ggJKM$ph7tUqnxbJv|2dk+nuO5v{ zzQ_3pRuOtjO;j_fTt=?y`};m{FEIa#hJ*(*A4iVED)WKM&Tt&Uu+|6qWhV*V(EGnj zGgU8ygk3}eV{0V{BM0z+s(Fg@f$+lo8)7*sbm2ybshB-2AbdgQNpvj>tEo+?8sCrf ziAyPD0#2Y?0(n#3%jZ0eb}&t-Zc8ZyO1NM#`jqx03N~rd@kH}2<(cQ=0O!R%OCx`X z+miKze&~XT;La3e*9lc+7)x<1bxB&5-wFEYYg`z^v%?563SN>wiq-02g zS(Z_m0BXWocPVQe@20L^%ai|N-20LCWcXZ&OM zLC%!9lE+_fnAJ@*7zW&HoE6q(%me_lWfB&YqiboAs{_M2jjTyC{z=$qb^RX9)?~)I z8MeY*%5sD&&bBm#{r4z&*HPVUIS5A$!9i4AO^R<-z($FU7kaFb%k*rEiw3L~4w~N( z`d9}$g0?<=VNf6@!l~!)h6JWn7OkU$A>$cn!$6WRs*@Z8)(2^Ep4BvTyy0I8k_L(2 zrKaIZxj^2@g-s*X-j`r1K#BpdTKmLY_Qc%3pH_Fbaoa%#gr1t)xmTPn{lCSyAOWbG zxhuKz7yYFXKco^uG1pDIkpcOWOJo*Gm-elft25a0-G2;LZ8vAns^5~nRH4k2wc`d( z@H|e^@xKb}*{s9#?d~=ffbgJOa6&;ELCQpN5*`_3qRE5cwM1-z!3AHZC}f=K?=CBO zkz0c=A9ed{d6QMvkTsL$qI;Si&wa0p&mK%}Tl}@}OU$-iey;GGtZkR7DAg zZJHynvW62*d_n@H5Rq1M#x#~#PF-iz(y&F^x;oeBr~x*4Nc0#l{8-npdye?5i^T>! zE56>|`u<3fx~X00>!VvyN3tmqy%#nx{(HY3cOj@7lh4c;KhFf!E?yWU6|}Cex~Z@! zTEVlfH@L2_W$v0QN7F~>B|pG0+{hAembJ>7>i)Hru0w~FvD#_9%)L}@)dth#@fo|x0qfg4X z4$Jb_${8B!x*=80#vpR6f_M|d+BryEg<2)JMpSU7?i(@Sg~!R-jLM2L)&PU2v(k(( zsLDAf07)|=J0EG3;=4MC;gGd?6#xLRPEtfrMWS9d#)|#=2Lv$oFbms5!Yd*6l7nf; zWGo9{A@VJYQ>tB+mWwi&;2wH}maqXdGG#C`^Qd-hWnC~cl(}sq?*6PKVL=nOESOd% zXFpOOjq`Yb`puWHJPI~imUjO!d4?lq?Apybu%6?L05JNx3D2Z_7C}73eDMVSPu?u? z=0FfRmqgzI57)Et6OZ#S4s>)-R(Lbq%X-*%JwyIv{<8B7ypHt;;U5^CMfpiA0ag!F zX-(J)SoWoK&_v!$aVOX2H+4{T2M{5>;J#jwJrg&czmwI@;?GDW?V1L0(Ez7r{Wz=b z05Jd7Lqr2q9YYW1og7f*%n=}f1ow0y>N~cpa^#>2+&!^71%E?80@;cBD#u1KIL>YG z>;6hpg2Rep{nt)_01_mC&_)FGX%1c&Az}mv^uOjlQbY+(k8wl!x&Ji@AaEf3+6WR~ z|6jV_!U!OAkOEVJ({WxvaQ~Cle@!5Pz`r(dBv1JNZ+366=$C7ZW~Hm90Kb={sGLZx IkU`M@0E0FWGynhq literal 0 HcmV?d00001 diff --git a/docs/source/_static/theme_overrides.css b/docs/source/_static/theme_overrides.css new file mode 100644 index 0000000..7b52706 --- /dev/null +++ b/docs/source/_static/theme_overrides.css @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* Adapted from Apache Arrow */ +/* https://github.com/apache/arrow/blob/main/docs/source/_static/theme_overrides.css */ + +/* Customizing with theme CSS variables */ + +:root { + /* Make headings more bold */ + --pst-font-weight-heading: 600; +} + +/* Contributing landing page overview cards */ + +.contrib-card { + border-radius: 0; + padding: 30px 10px 20px 10px; + margin: 10px 0px; +} + +.contrib-card p.card-text { + margin: 0px; +} + +.contrib-card .sd-card-img-top { + margin: 2px; + height: 75px; + background: none !important; +} + +.contrib-card .sd-card-title { + color: var(--pst-color-primary); + font-size: var(--pst-font-size-h3); + padding: 1rem 0rem 0.5rem 0rem; +} + +.contrib-card .sd-card-footer { + border: none; +} + +/* This is the bootstrap CSS style for "table-striped". Since the theme does +not yet provide an easy way to configure this globally, it easier to simply +include this snippet here than updating each table in all rst files to +add ":class: table-striped" */ + +.table tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +/* Improve the vertical spacing in the C++ API docs +(ideally this should be upstreamed to the pydata-sphinx-theme */ + +dl.cpp dd p { + margin-bottom: .4rem; +} + +dl.cpp.enumerator { + margin-bottom: 0.2rem; +} + +p.breathe-sectiondef-title { + margin-top: 1rem; +} + +/* Keep social icons arranged horizontally in sidebar */ + +.sidebar-header-items__end { + flex-wrap: wrap; +} diff --git a/docs/source/_static/versions.json b/docs/source/_static/versions.json new file mode 100644 index 0000000..9980db1 --- /dev/null +++ b/docs/source/_static/versions.json @@ -0,0 +1,7 @@ +[ + { + "name": "0.10.0 (dev)", + "version": "dev/", + "url": "#" + } +] diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 0000000..2f6538a --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,40 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +************* +API Reference +************* + +.. toctree:: + :maxdepth: 3 + + api/catalog + api/write + api/commit + api/scan + api/read + api/predicate + api/file_format + api/file_system + api/io + api/data_types + api/file_index + api/global_index + api/clean + api/defs + api/executor + api/memory diff --git a/docs/source/api/catalog.rst b/docs/source/api/catalog.rst new file mode 100644 index 0000000..c3956d3 --- /dev/null +++ b/docs/source/api/catalog.rst @@ -0,0 +1,32 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Catalog +=========== + +.. _cpp-api-catalog: + +Interface +========= + +.. doxygenclass:: paimon::Catalog + :members: + +.. doxygenclass:: paimon::Identifier + :members: + :undoc-members: diff --git a/docs/source/api/clean.rst b/docs/source/api/clean.rst new file mode 100644 index 0000000..b1611cd --- /dev/null +++ b/docs/source/api/clean.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +================== +Orphan Files Clean +================== + +.. _cpp-api-clean: + +Interface +========= + +.. doxygenclass:: paimon::OrphanFilesCleaner + :members: + :undoc-members: + +.. doxygenclass:: paimon::CleanContextBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::CleanContext + :members: + :undoc-members: diff --git a/docs/source/api/commit.rst b/docs/source/api/commit.rst new file mode 100644 index 0000000..3f03dde --- /dev/null +++ b/docs/source/api/commit.rst @@ -0,0 +1,41 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Commit +=========== + +.. _cpp-api-commit: + +Interface +========= + +.. doxygenclass:: paimon::FileStoreCommit + :members: + :undoc-members: + +.. doxygenclass:: paimon::CommitContextBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::CommitContext + :members: + :undoc-members: + +.. doxygenclass:: paimon::CommitMessage + :members: + :undoc-members: diff --git a/docs/source/api/data_types.rst b/docs/source/api/data_types.rst new file mode 100644 index 0000000..1876d78 --- /dev/null +++ b/docs/source/api/data_types.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Data Types +=========== + +.. _cpp-api-data-types: + +Interface +========= + +.. doxygenclass:: paimon::Blob + :members: + :undoc-members: + +.. doxygenclass:: paimon::Decimal + :members: + :undoc-members: + +.. doxygenclass:: paimon::Timestamp + :members: + :undoc-members: diff --git a/docs/source/api/defs.rst b/docs/source/api/defs.rst new file mode 100644 index 0000000..ee28685 --- /dev/null +++ b/docs/source/api/defs.rst @@ -0,0 +1,31 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Options +=========== + +.. _cpp-api-options: + +Interface +========= + +.. doxygenenum:: paimon::FieldType + +.. doxygenstruct:: paimon::Options + :members: + :undoc-members: diff --git a/docs/source/api/executor.rst b/docs/source/api/executor.rst new file mode 100644 index 0000000..d5daed5 --- /dev/null +++ b/docs/source/api/executor.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Executor +=========== + +.. _cpp-api-executor: + +Interface +========= + +.. doxygenvariable:: paimon::DEFAULT_EXECUTOR_THREAD_COUNT + +.. doxygenfunction:: paimon::GetGlobalDefaultExecutor() + +.. doxygenfunction:: paimon::CreateDefaultExecutor() + +.. doxygenfunction:: paimon::CreateDefaultExecutor(uint32_t) + +.. doxygenclass:: paimon::Executor + :members: + :undoc-members: diff --git a/docs/source/api/file_format.rst b/docs/source/api/file_format.rst new file mode 100644 index 0000000..57b9eee --- /dev/null +++ b/docs/source/api/file_format.rst @@ -0,0 +1,57 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +File Format +=========== + +.. _cpp-api-file-format: + +Interface +========= + +.. doxygenclass:: paimon::FileFormat + :members: + :undoc-members: + +.. doxygenclass:: paimon::FormatWriter + :members: + :undoc-members: + +.. doxygenclass:: paimon::WriterBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::BatchReader + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileBatchReader + :members: + :undoc-members: + +.. doxygenclass:: paimon::ReaderBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileFormatFactory + :members: + :undoc-members: + +.. doxygenclass:: paimon::FormatStatsExtractor + :members: + :undoc-members: diff --git a/docs/source/api/file_index.rst b/docs/source/api/file_index.rst new file mode 100644 index 0000000..e95cd26 --- /dev/null +++ b/docs/source/api/file_index.rst @@ -0,0 +1,53 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +File Index +=========== + +.. _cpp-api-file-index: + +Interface +========= + +.. doxygenclass:: paimon::FileIndexer + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileIndexerFactory + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileIndexWriter + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileIndexReader + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileIndexFormat + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileIndexResult + :members: + :undoc-members: + +.. doxygenclass:: paimon::BitmapIndexResult + :members: + :undoc-members: diff --git a/docs/source/api/file_system.rst b/docs/source/api/file_system.rst new file mode 100644 index 0000000..0313d0b --- /dev/null +++ b/docs/source/api/file_system.rst @@ -0,0 +1,51 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +File System +=========== + +.. _cpp-api-file-system-factory: + +Interface +========= + +.. doxygenclass:: paimon::FileSystem + :members: + :undoc-members: + +.. doxygenclass:: paimon::FileSystemFactory + :members: + :undoc-members: + +.. doxygenclass:: paimon::InputStream + :members: + :undoc-members: + +.. doxygenclass:: paimon::OutputStream + :members: + :undoc-members: + +.. doxygenenum:: paimon::SeekOrigin + +.. doxygenclass:: paimon::FileStatus + :members: + :undoc-members: + +.. doxygenclass:: paimon::BasicFileStatus + :members: + :undoc-members: diff --git a/docs/source/api/global_index.rst b/docs/source/api/global_index.rst new file mode 100644 index 0000000..8be63a7 --- /dev/null +++ b/docs/source/api/global_index.rst @@ -0,0 +1,77 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +============ +Global Index +============ + +.. _cpp-api-global-index: + +Interface +========= + +.. doxygenclass:: paimon::GlobalIndexerFactory + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexer + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexReader + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexFileReader + :members: + :undoc-members: + +.. doxygenstruct:: paimon::GlobalIndexIOMeta + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexResult + :members: + :undoc-members: + +.. doxygenclass:: paimon::BitmapGlobalIndexResult + :members: + :undoc-members: + +.. doxygenclass:: paimon::BitmapScoredGlobalIndexResult + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexScan + :members: + :undoc-members: + +.. doxygenclass:: paimon::IndexedSplit + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexWriteTask + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexWriter + :members: + :undoc-members: + +.. doxygenclass:: paimon::GlobalIndexFileWriter + :members: + :undoc-members: diff --git a/docs/source/api/io.rst b/docs/source/api/io.rst new file mode 100644 index 0000000..9687fc1 --- /dev/null +++ b/docs/source/api/io.rst @@ -0,0 +1,41 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=== +IO +=== + +.. _cpp-api-io: + +Interface +========= + +.. doxygenenum:: paimon::ByteOrder + +.. doxygenfunction:: paimon::SystemByteOrder() + +.. doxygenclass:: paimon::BufferedInputStream + :members: + :undoc-members: + +.. doxygenclass:: paimon::ByteArrayInputStream + :members: + :undoc-members: + +.. doxygenclass:: paimon::DataInputStream + :members: + :undoc-members: diff --git a/docs/source/api/memory.rst b/docs/source/api/memory.rst new file mode 100644 index 0000000..e498dfb --- /dev/null +++ b/docs/source/api/memory.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +======== +Memory +======== + +.. _cpp-api-memory: + +Interface +========= + +.. doxygenfunction:: paimon::GetMemoryPool() + +.. doxygenfunction:: paimon::GetDefaultPool() + +.. doxygenclass:: paimon::MemoryPool + :members: + :undoc-members: + +.. doxygenclass:: paimon::Bytes + :members: + :undoc-members: diff --git a/docs/source/api/predicate.rst b/docs/source/api/predicate.rst new file mode 100644 index 0000000..e79f6ca --- /dev/null +++ b/docs/source/api/predicate.rst @@ -0,0 +1,52 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Predicate +=================================== + +.. _cpp-api-predicate: + +Interface +========= + +.. doxygenclass:: paimon::Predicate + :members: + :undoc-members: + +.. doxygenclass:: paimon::LeafPredicate + :members: + :undoc-members: + +.. doxygenclass:: paimon::CompoundPredicate + :members: + :undoc-members: + +.. doxygenclass:: paimon::Function + :members: + :undoc-members: + +.. doxygenclass:: paimon::FunctionVisitor + :members: + :undoc-members: + +.. doxygenclass:: paimon::Literal + :members: + :undoc-members: + +.. doxygenclass:: paimon::PredicateBuilder + :members: + :undoc-members: diff --git a/docs/source/api/read.rst b/docs/source/api/read.rst new file mode 100644 index 0000000..3c437d7 --- /dev/null +++ b/docs/source/api/read.rst @@ -0,0 +1,41 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Read +=========== + +.. _cpp-api-read: + +Interface +========= + +.. doxygenclass:: paimon::TableRead + :members: + :undoc-members: + +.. doxygenclass:: paimon::ReadContextBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::ReadContext + :members: + :undoc-members: + +.. doxygenclass:: paimon::BatchReader + :members: + :undoc-members: diff --git a/docs/source/api/scan.rst b/docs/source/api/scan.rst new file mode 100644 index 0000000..142a516 --- /dev/null +++ b/docs/source/api/scan.rst @@ -0,0 +1,53 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Scan +=========== + +.. _cpp-api-scan: + +Interface +========= + +.. doxygenclass:: paimon::TableScan + :members: + :undoc-members: + +.. doxygenclass:: paimon::ScanContextBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::ScanContext + :members: + :undoc-members: + +.. doxygenclass:: paimon::Plan + :members: + :undoc-members: + +.. doxygenclass:: paimon::Split + :members: + :undoc-members: + +.. doxygenclass:: paimon::DataSplit + :members: + :undoc-members: + +.. doxygenclass:: paimon::ScanFilter + :members: + :undoc-members: diff --git a/docs/source/api/write.rst b/docs/source/api/write.rst new file mode 100644 index 0000000..7abeb5b --- /dev/null +++ b/docs/source/api/write.rst @@ -0,0 +1,45 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +=========== +Write +=========== + +.. _cpp-api-write: + +Interface +========= + +.. doxygenclass:: paimon::FileStoreWrite + :members: + :undoc-members: + +.. doxygenclass:: paimon::WriteContextBuilder + :members: + :undoc-members: + +.. doxygenclass:: paimon::WriteContext + :members: + :undoc-members: + +.. doxygenclass:: paimon::RecordBatch + :members: + :undoc-members: + +.. doxygenclass:: paimon::RecordBatchBuilder + :members: + :undoc-members: diff --git a/docs/source/basic_concepts.rst b/docs/source/basic_concepts.rst new file mode 100644 index 0000000..10143e9 --- /dev/null +++ b/docs/source/basic_concepts.rst @@ -0,0 +1,89 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/concepts/basic-concepts.md + +Basic Concepts +======================== + +File Layouts +------------------------ +All files of a table are stored under one base directory. Paimon files are +organized in a layered style. The following image illustrates the file layout. +Starting from a snapshot file, Paimon readers can recursively access all records +from the table. + +.. image:: _static/file-layout.png + :alt: File Layout + :align: center + :width: 100% + +Snapshot +------------------- +All snapshot files are stored in the snapshot directory. + +A snapshot file is a JSON file containing information about this snapshot, +including the schema file in use the manifest list containing all changes of +this snapshot. A snapshot captures the state of a table at some point in time. +Users can access the latest data of a table through the latest snapshot. +By time traveling, users can also access the previous state of a table through +an earlier snapshot. + +Manifest Files +------------------- +All manifest lists and manifest files are stored in the manifest directory. A +manifest list is a list of manifest file names. +A manifest file is a file containing changes about LSM data files and changelog +files. For example, which LSM data file is created and which file is deleted in +the corresponding snapshot. + +Data Files +--------------------------- +Data files are grouped by partitions. Currently, Paimon supports using parquet +(default), orc and lance as data file’s format. + +.. note:: + avro write as a data file format is not supported yet. + +Partition +--------------------------- +Paimon adopts the same partitioning concept as Apache Hive to separate data. + +Partitioning is an optional way of dividing a table into related parts based on +the values of particular columns like date, city, and department. Each table can +have one or more partition keys to identify a particular partition. + +By partitioning, users can efficiently operate on a slice of records in the +table. + +Consistency Guarantees +--------------------------- +Paimon writers use two-phase commit protocol to atomically commit a batch of +records to the table. Each commit produces at most two snapshots at commit time. +It depends on the incremental write and compaction strategy. If only incremental +writes are performed without triggering a compaction operation, only an +incremental snapshot will be created. If a compaction operation is triggered, an +incremental snapshot and a compacted snapshot will be created. + +For any two writers modifying a table at the same time, as long as they do not +modify the same partition, their commits can occur in parallel. If they modify +the same partition, only snapshot isolation is guaranteed. That is, the final +table state may be a mix of the two commits, but no changes are lost. + +.. note:: + Paimon C++ currently does not support compaction. diff --git a/docs/source/build_system.rst b/docs/source/build_system.rst new file mode 100644 index 0000000..1f3f325 --- /dev/null +++ b/docs/source/build_system.rst @@ -0,0 +1,99 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. default-domain:: cpp +.. highlight:: cpp + +====================== +Integrating Paimon C++ +====================== + +This section assumes that you have already built and installed the Paimon C++ +libraries on your system after :ref:`building them yourself `. +Additionally, you will need `Apache Arrow for C++ `_ +as in-memory data format interface. Please ensure that Arrow C++ is installed +and available to your build system + +The recommended way to integrate the Paimon C++ libraries into your C++ project +is to use CMake’s `find_package `_ +function to locate and integrate dependencies. + +CMake +===== + +Quick Start +----------- + +This ``CMakeLists.txt`` compiles the ``my_example.cc`` source file into +an executable and links it with the Paimon C++ shared library and its plugins +for data format and file system. + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.16) + + project(MyExample) + + find_package(Arrow REQUIRED) + find_package(Paimon REQUIRED) + + add_executable(my_example my_example.cc) + target_link_libraries(my_example PRIVATE arrow_shared + paimon_shared + paimon_parquet_file_format_shared + paimon_local_file_system_shared) + +Available variables and targets +------------------------------- + +The directive ``find_package(Paimon REQUIRED)`` instructs CMake to locate a +Paimon C++ installation on your system. If successful, it sets ``Paimon_FOUND`` +to true if the Paimon C++ libraries were found. + +It also defines the following linkable targets (plain strings, not variables): + +* ``paimon_shared`` links to the Paimon shared libraries +* ``paimon_static`` links to the Paimon static libraries + +In most cases, it is recommended to use the Paimon shared libraries. + +Optional plugins (built-in file formats, file systems, and index) +----------------------------------------------------------------- + +Paimon provides a set of built-in optional plugins that you can link to as needed: + +- File format plugins: + + - ``paimon_parquet_file_format_shared`` / ``paimon_parquet_file_format_static`` + - ``paimon_orc_file_format_shared`` / ``paimon_orc_file_format_static`` + - ``paimon_avro_file_format_shared`` / ``paimon_avro_file_format_static`` + - ``paimon_blob_file_format_shared`` / ``paimon_blob_file_format_static`` + - ``paimon_lance_file_format_shared`` / ``paimon_lance_file_format_static`` + +- File system plugins: + + - ``paimon_local_file_system_shared`` / ``paimon_local_file_system_static`` + - ``paimon_jindo_file_system_shared`` / ``paimon_jindo_file_system_static`` + +- Index plugins: + + - ``paimon_file_index_shared`` / ``paimon_file_index_static`` + - ``paimon_lumina_index_shared`` / ``paimon_lumina_index_static`` + +.. note:: + + In most cases, it is recommended to use the shared variants of these plugins. diff --git a/docs/source/building.rst b/docs/source/building.rst new file mode 100644 index 0000000..b694dd0 --- /dev/null +++ b/docs/source/building.rst @@ -0,0 +1,291 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. highlight:: console + +.. _building-paimon-cpp: + +=================== +Building Paimon C++ +=================== + +System setup +============ + +Paimon uses CMake as a build configuration system. We recommend building +out-of-source. For example, you could create ``paimon-cpp/build-release`` +and invoke ``cmake $CMAKE_ARGS ..`` from this directory. + +Building requires: + +* A C++17-enabled compiler. On Linux, gcc 8 and higher should be + sufficient. Windows and MacOS are not supported for now. +* At least 2GB of RAM for a minimal build, 8GB for a minimal + debug build with tests and 16GB for a full build. + +On Ubuntu/Debian you can install the requirements with: + +.. code-block:: shell + + sudo apt-get install \ + build-essential \ + cmake + +We also provide a docker template to help you get started quickly. See in +``.devcontainer`` folder for more details. + +.. _cpp-building-building: + +Building +======== + +All the instructions below assume that you have cloned the paimon-cpp git +repository: + +.. code-block:: + + $ git clone https://github.com/alibaba/paimon-cpp.git + $ cd paimon-cpp + $ git lfs pull + +Manual configuration +-------------------- + +The build system uses ``CMAKE_BUILD_TYPE=Release`` by default, so if this +argument is omitted then a release build will be produced. + +Two build types are possible: + +* ``Debug``: doesn't apply any compiler optimizations and adds debugging + information in the binary. +* ``Release``: applies compiler optimizations and removes debug information + from the binary. + +.. note:: + + You can also run default build with flag ``-DPAIMON_EXTRA_ERROR_CONTEXT=ON`` + for more error msg context. + +Minimal release build (2GB of RAM for building or more recommended): + +.. code-block:: + + $ mkdir build-release + $ cd build-release + $ cmake .. + $ make -j8 # if you have 8 CPU cores, otherwise adjust + $ make install + +Minimal debug build with unit tests (4GB of RAM for building or more recommended): + +.. code-block:: + + $ mkdir build-debug + $ cd build-debug + $ cmake -DCMAKE_BUILD_TYPE=Debug -DPAIMON_BUILD_TESTS=ON .. + $ make -j8 # if you have 8 CPU cores, otherwise adjust + $ make unittest # to run the tests + $ make install + +The unit tests are not built by default. After building, one can also invoke +the unit tests using the ``ctest`` tool provided by CMake. + +Faster builds with Ninja +~~~~~~~~~~~~~~~~~~~~~~~~ + +Many contributors use the `Ninja build system `_ to +get faster builds. It especially speeds up incremental builds. To use +``ninja``, pass ``-GNinja`` when calling ``cmake`` and then use the ``ninja`` +command instead of ``make``. + +.. _cpp_build_optional_components: + +Optional Components +~~~~~~~~~~~~~~~~~~~ + +By default, the C++ build system creates a fairly minimal build. We have +several optional system components which you can opt into building by passing +boolean flags to ``cmake``. + +* ``-DPAIMON_ENABLE_ORC=ON``: Paimon integration with Apache ORC +* ``-DPAIMON_ENABLE_LANCE=ON``: Paimon integration with Lance +* ``-DPAIMON_ENABLE_AVRO=ON``: Apache Avro libraries and Paimon integration +* ``-DPAIMON_ENABLE_JINDO=ON``: Support for Alibaba Jindo filesystems +* ``-DPAIMON_ENABLE_LUMINA=ON``: Support for Lumina vector index, lumina is only supported on gcc9 or higher. + +Third-party dependency source +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Paimon C++ can either build selected third-party dependencies from bundled +sources or use libraries already installed on the system. The default mode is +``AUTO``, which tries system packages first and falls back to bundled sources +when they are not found. + +.. code-block:: shell + + cmake -B build -DPAIMON_DEPENDENCY_SOURCE=AUTO + +The supported dependency source values are: + +* ``AUTO``: use a system package when available, otherwise build bundled sources. +* ``BUNDLED``: always build bundled sources. +* ``SYSTEM``: require system packages and fail if they are not found. + +You can override individual dependencies with ``_SOURCE``. The +supported dependency set includes Arrow/Parquet, ORC, Protobuf, Avro, RE2, fmt, +RapidJSON, TBB, glog, GoogleTest, and compression libraries. Arrow and ORC +require project-specific patches, so their supported source values are +``AUTO`` and ``BUNDLED``; ``AUTO`` resolves to bundled sources for them. + +.. code-block:: shell + + cmake -B build \ + -DPAIMON_DEPENDENCY_SOURCE=AUTO \ + -Dfmt_SOURCE=SYSTEM \ + -Dfmt_ROOT=/opt/fmt \ + -Dzstd_SOURCE=BUNDLED + +Use ``PAIMON_PACKAGE_PREFIX`` to provide one common prefix for dependencies +whose own ``_ROOT`` variable is not set. + +.. code-block:: shell + + cmake -B build \ + -DPAIMON_DEPENDENCY_SOURCE=SYSTEM \ + -DPAIMON_PACKAGE_PREFIX=/opt/paimon-deps + +Package-manager-specific modes are intentionally out of scope for this first +dependency source interface. They can still be used through standard CMake +mechanisms such as ``CMAKE_PREFIX_PATH`` or ``CMAKE_TOOLCHAIN_FILE``, while +Paimon keeps the dependency source values limited to ``AUTO``, ``BUNDLED``, and +``SYSTEM``. + +When ``Arrow_SOURCE`` is explicitly set to ``BUNDLED`` or left as ``AUTO``, the +compression dependencies default to bundled sources unless individually +overridden. When ``ORC_SOURCE`` is explicitly set to ``BUNDLED`` or left as +``AUTO``, ``Protobuf_SOURCE`` defaults to bundled sources unless individually +overridden. + +During configuration, CMake prints a dependency resolution summary showing the +requested source, actual source, compatibility target, and search root for each +resolved dependency. + +Optional Targets +~~~~~~~~~~~~~~~~ + +For development builds, you will often want to enable additional targets in +enable to exercise your changes, using the following ``cmake`` options. + +* ``-DPAIMON_BUILD_TESTS=ON``: Build executable unit tests. + +Optional Checks +~~~~~~~~~~~~~~~ + +The following special checks are available as well. They instrument the +generated code in various ways so as to detect select classes of problems +at runtime (for example when executing unit tests). + +* ``-DPAIMON_USE_ASAN=ON``: Enable Address Sanitizer to check for memory leaks, + buffer overflows or other kinds of memory management issues. +* ``-DPAIMON_USE_UBSAN=ON``: Enable Undefined Behavior Sanitizer to check for + situations which trigger C++ undefined behavior. + +Some of those options are mutually incompatible, so you may have to build +several times with different options if you want to exercise all of them. + +CMake version requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We support CMake 3.16 and higher. + +LLVM and Clang Tools +~~~~~~~~~~~~~~~~~~~~ + +We currently use LLVM for library builds and for developer tools such as code +formatting with clang-format. LLVM can be installed via most modern package +managers (apt, yum, etc.). + +Environment variables +~~~~~~~~~~~~~~~~~~~~~ + +The build system and helper scripts accept several environment variables that +can alter fetch and build behaviour without changing CMake flags. These are +especially useful when you want to use a local or corporate mirror for +third-party archives, or to override a specific dependency's download URL. + +Common environment variables +---------------------------- + +* ``PAIMON_THIRDPARTY_MIRROR_URL`` + + When set, this string is used as a prefix for the default third-party + download URLs. For example, if a dependency would normally be downloaded + from + + ``https://github.com/fmtlib/fmt/archive/refs/tags/${PAIMON_FMT_BUILD_VERSION}.tar.gz`` + + and ``PAIMON_THIRDPARTY_MIRROR_URL`` is set to + + ``https://mirror.example.com/paimon/thirdparty/``, the build system will + attempt to download from + + ``https://mirror.example.com/paimon/thirdparty/https://github.com/fmtlib/fmt/archive/refs/tags/${PAIMON_FMT_BUILD_VERSION}.tar.gz`` + + (the exact concatenation semantics follow the third-party fetch helpers + defined in ``cmake_modules/ThirdpartyToolchain.cmake``). If you set a + mirror URL, prefer including a trailing slash to avoid accidental URL + concatenation issues. + +* Per-dependency override variables (examples) + + Many dependencies support overriding their download URL via a dedicated + environment variable. Examples implemented in the CMake helper include: + + - ``PAIMON_FMT_URL`` to override the fmt archive URL + - ``PAIMON_RAPIDJSON_URL`` to override RapidJSON download URL + - ``PAIMON_ZLIB_URL``, ``PAIMON_ZSTD_URL``, ``PAIMON_LZ4_URL`` etc. + + If one of these per-dependency environment variables is defined, it will + take precedence over the mirror prefix. Use these variables to precisely + control where a given dependency is fetched from. + +Usage examples +-------------- + +Use a mirror for all third-party downloads: + +.. code-block:: shell + + export PAIMON_THIRDPARTY_MIRROR_URL="https://mirror.example.com/paimon/thirdparty/" + mkdir build + cd build + cmake -DPAIMON_BUILD_TESTS=ON .. + +Override only a single dependency (fmt): + +.. code-block:: shell + + export PAIMON_FMT_URL="https://internal.example.com/archives/fmt-8.1.1.tar.gz" + mkdir build + cd build + cmake .. + +.. note:: + + The exact fetch behaviour (how the mirror prefix is concatenated, or whether the helper expects a full URL vs. a prefix) + is implemented in ``cmake_modules/ThirdpartyToolchain.cmake``. Consult that file when you need a custom setup. + Unset an environment variable to revert to the default upstream download locations: ``unset PAIMON_THIRDPARTY_MIRROR_URL`` diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..572a857 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Adapted from Apache Arrow +# https://github.com/apache/arrow/blob/main/docs/source/conf.py + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import os + +project = "C++ Paimon" +copyright = "2026, The Apache Software Foundation" +author = "The Apache Software Foundation" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "breathe", + "myst_parser", + "sphinx_design", + "sphinx_copybutton", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.ifconfig", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", + "sphinxcontrib.mermaid", +] + +# Show members for classes in .. autosummary +autodoc_default_options = { + "members": None, + "special-members": "__dataframe__", + "undoc-members": None, + "show-inheritance": None, + "inherited-members": None, +} + +# Breathe configuration +breathe_projects = { + "paimon_cpp": os.environ.get("PAIMON_CPP_DOXYGEN_XML", "../../apidoc/xml"), +} +breathe_default_project = "paimon_cpp" + +# Overridden conditionally below +autodoc_mock_imports = [] + +# copybutton configuration +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: " +copybutton_prompt_is_regexp = True +copybutton_line_continuation_character = "\\" + +# MyST-Parser configuration +myst_enable_extensions = [ + "amsmath", + "attrs_inline", + "deflist", + "dollarmath", + "fieldlist", + "html_admonition", + "html_image", + "linkify", + "strikethrough", + "substitution", + "tasklist", +] + +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] + +# Custom fixes to the RTD theme +html_css_files = ["theme_overrides.css"] + +# Hide the primary sidebar (section navigation) for these pages +html_sidebars = { + "implementations": [], + "status": [], +} + +# The master toctree document. +master_doc = "index" + +version = "0.2.0" + +html_theme_options = { + "show_toc_level": 2, + "show_nav_level": 2, + "use_edit_page_button": True, + "header_links_before_dropdown": 4, + "navbar_align": "left", + "navbar_end": ["theme-switcher", "navbar-icon-links"], + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/alibaba/paimon-cpp", + "icon": "fa-brands fa-square-github", + }, + ], + "logo": { + "text": "Paimon C++", + }, + "show_version_warning_banner": True, +} + +html_context = { + "github_user": "alibaba", + "github_repo": "paimon-cpp", + "github_version": "main", + "doc_path": "docs/source", +} + +html_title = f"C++ Paimon" + +html_show_sourcelink = False diff --git a/docs/source/documentations.rst b/docs/source/documentations.rst new file mode 100644 index 0000000..5dcc368 --- /dev/null +++ b/docs/source/documentations.rst @@ -0,0 +1,68 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. _building-docs: + +Building the Documentation +========================== + +Prerequisites +------------- + +The documentation build relies on `Doxygen `_ and +`Sphinx `_ along with a few extensions. + +First, install `Doxygen `_ +yourself (for example from your distribution's official repositories, if +using Linux). Then install the Python-based requirements with the +following command: + +.. code-block:: shell + + pip install -r paimon-cpp/docs/requirements.txt + +Building +-------- + +.. note:: + + If you are building the documentation on Windows, not all sections + may build properly. + +These two steps are mandatory and must be executed in order. + +#. Process the C++ API using Doxygen + + .. code-block:: shell + + cd paimon-cpp/apidoc + doxygen + cd - + +#. Build the complete documentation using Sphinx. + + .. code-block:: shell + + cd paimon-cpp/docs + make html + cd - + + +After these steps are completed, the documentation is rendered in HTML +format in ``paimon-cpp/docs/_build/html``. In particular, you can point your +browser at ``paimon-cpp/docs/_build/html/index.html`` to read the docs and +review any changes you made. diff --git a/docs/source/examples/clean.rst b/docs/source/examples/clean.rst new file mode 100644 index 0000000..89de5ea --- /dev/null +++ b/docs/source/examples/clean.rst @@ -0,0 +1,26 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. default-domain:: cpp +.. highlight:: cpp + +============== +Clean Example +============== + +The file ``examples/clean_demo.cpp`` located inside the source tree, it is an +example of building and using Paimon from a third-party project. diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst new file mode 100644 index 0000000..c26b749 --- /dev/null +++ b/docs/source/examples/index.rst @@ -0,0 +1,25 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Examples +======== + +.. toctree:: + :maxdepth: 1 + + write_commit_scan_read + clean diff --git a/docs/source/examples/write_commit_scan_read.rst b/docs/source/examples/write_commit_scan_read.rst new file mode 100644 index 0000000..1549bb1 --- /dev/null +++ b/docs/source/examples/write_commit_scan_read.rst @@ -0,0 +1,26 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. default-domain:: cpp +.. highlight:: cpp + +============================== +Write Commit Scan Read Example +============================== + +The file ``examples/read_write_demo.cpp`` located inside the source tree, it is +an example of building and using Paimon from a third-party project. diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst new file mode 100644 index 0000000..4c5d1a3 --- /dev/null +++ b/docs/source/getting_started.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. default-domain:: cpp +.. highlight:: cpp + +Getting Started +=============== + +The following articles demonstrate installation, usage, and a basic +understanding of C++ Paimon. These articles will get you set up quickly with C++ +Paimon and give you a taste of what the library is capable of. + +Start here to gain a basic understanding of Paimon, and move on to the +:doc:`/user_guide` to explore more specific topics and +underlying concepts, or the :doc:`/api` to explore Paimon's API. + +.. toctree:: + + building + build_system + basic_concepts + documentations diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..c595d45 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,108 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. _implementations: + +C++ Paimon Documentation +======================== + +Paimon C++ is a high-performance C++ implementation of Apache Paimon. We aim to +provide a native, high-performance and extensible implementation that allows +native engines to access the Paimon datalake format with maximum efficiency. + +.. grid:: 1 2 2 2 + :gutter: 4 + :padding: 2 2 0 0 + :class-container: sd-text-center + + .. grid-item-card:: Getting started + :class-card: contrib-card + :shadow: none + + Start here to gain a basic understanding of Paimon with + an installation and linking guide, basic concepts etc. + + +++ + + .. button-link:: getting_started.html + :click-parent: + :color: primary + :expand: + + To Getting started + + .. grid-item-card:: User Guide + :class-card: contrib-card + :shadow: none + + Explore more specific topics and underlying concepts + of Paimon C++ + + +++ + + .. button-link:: user_guide.html + :click-parent: + :color: primary + :expand: + + To the User Guide + +.. grid:: 1 2 2 2 + :gutter: 4 + :padding: 2 2 0 0 + :class-container: sd-text-center + + .. grid-item-card:: Examples + :class-card: contrib-card + :shadow: none + + Find the description and location of the examples + using Paimon C++ library + + +++ + + .. button-link:: examples/index.html + :click-parent: + :color: primary + :expand: + + To the Examples + + .. grid-item-card:: API Reference + :class-card: contrib-card + :shadow: none + + Explore Paimon‘s API reference documentation + + +++ + + .. button-link:: api.html + :click-parent: + :color: primary + :expand: + + To the API Reference + + +.. toctree:: + :maxdepth: 2 + :hidden: + + getting_started + user_guide + Examples + api diff --git a/docs/source/user_guide.rst b/docs/source/user_guide.rst new file mode 100644 index 0000000..5b14faa --- /dev/null +++ b/docs/source/user_guide.rst @@ -0,0 +1,40 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. default-domain:: cpp +.. highlight:: cpp + +User Guide +========== + +.. toctree:: + + user_guide/catalog + user_guide/schema + user_guide/snapshot + user_guide/manifest + user_guide/data_types + user_guide/primary_key_table + user_guide/append_only_table + user_guide/write + user_guide/commit + user_guide/compaction + user_guide/read + user_guide/clean + user_guide/prefetch + user_guide/arrow + user_guide/global_index diff --git a/docs/source/user_guide/append_only_table.rst b/docs/source/user_guide/append_only_table.rst new file mode 100644 index 0000000..128053f --- /dev/null +++ b/docs/source/user_guide/append_only_table.rst @@ -0,0 +1,26 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/append-table/overview.md + +Append Only Table +================= +If a table does not have a primary key defined, it is an append table. Compared +to the primary key table, it does not have the ability to directly receive changelogs. +It cannot be directly updated with data through upsert. It can only receive +incoming data from append data. diff --git a/docs/source/user_guide/arrow.rst b/docs/source/user_guide/arrow.rst new file mode 100644 index 0000000..084b156 --- /dev/null +++ b/docs/source/user_guide/arrow.rst @@ -0,0 +1,125 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. _memory-format: + +Memory Format +============= + +`Paimon Java `_ uses a row-level +abstraction, ``InternalRow``, as the default data format interface. +Row-level interfaces are generally more intuitive for programming and can be +easily integrated into data processing pipelines such as filtering and merge sorting. +However, this approach introduces conversion and access overhead, +making it difficult to fully leverage the additional performance benefits provided +by modern CPU SIMD vectorization. + +Considering that the C++ implementation focuses more on end-to-end performance, +and that underlying data file formats (e.g., ORC, Parquet) are predominantly +columnar, providing a columnar-centric data abstraction can minimize overhead +during data flow, integrate more naturally with vectorized execution engines, +and ultimately deliver superior overall performance. + +Why Apache Arrow +---------------- + +Apache Arrow is currently the most widely adopted in-memory columnar format and +has strong native support for Parquet and ORC. It is well-integrated across +open-source engines including Spark, Pandas, Drill, Impala, and Velox. + +Overall, using Apache Arrow as the in-memory format for Paimon C++ allows us to: + +- Maximize columnar performance and efficient data access patterns. +- Seamlessly integrate with the Apache Arrow ecosystem and tooling. +- Benefit from mature interoperability with popular data systems and formats. + +Versioning and Dependency Concerns +---------------------------------- + +One important consideration is that Arrow is an active open-source project with +a broad and evolving C++ surface area that spans data formats, compute kernels, +and file I/O. Due to frequent releases and a large API surface, different Arrow +C++ SDK versions can introduce API incompatibilities. + +If Paimon C++ directly depends on the full Arrow C++ SDK, it may conflict with +existing Arrow C++ dependencies in other compute engines, raising integration +costs and increasing long-term maintenance complexity. + +Adopting the Arrow C Data Interface +----------------------------------- + +To leverage Arrow’s performance and ecosystem benefits while avoiding tight +coupling to specific Arrow C++ SDK versions, we use the Arrow C Data +Interface as the default in-memory format for Paimon C++. + +Key advantages: + +- Version-neutral: The C Data Interface is designed to be stable and forward-compatible + across Arrow versions. +- Compiler-neutral: C language interfaces avoid ABI friction commonly seen with + C++ compilers and standard libraries. +- Broad interoperability: The C Data Interface is supported by Arrow-based + systems and enables zero-copy or minimal-copy interchange of columnar data. + +Design Principles +----------------- + +- Columnar-first abstraction: + Paimon C++ will represent in-memory data using columnar buffers and schemas + compatible with the Arrow C Data Interface, minimizing transformation overhead. + +- Minimal dependency footprint: + Prefer stable C interfaces and lightweight utility layers; avoid linking + against the full Arrow C++ SDK unless strictly necessary and well-isolated. + +- Vectorization-aware execution: + Structure data layouts to align with SIMD processing (e.g., contiguous + buffers, clear null bitmaps and type-specific arrays), enabling efficient + filtering, projection, and aggregation. + +- Interoperability: + Ensure that data produced and consumed by Paimon C++ can be handed off to + Arrow-compatible engines and libraries without expensive conversions. + +- Compatibility with columnar storage: + Maintain efficient paths from columnar file formats (Parquet, ORC) to in-memory + columnar representations, minimizing decoding and marshaling overhead. + +Implementation Outline +---------------------- + +Schema and buffers +~~~~~~~~~~~~~~~~~~~ +- Represent schemas and arrays using Arrow C Data Interface types (e.g., ``ArrowSchema``, ``ArrowArray``) with clear ownership and lifecycle. +- Support nested types (structs, lists, maps) and common primitives (integers, floats, decimals, timestamps). + +Memory management +~~~~~~~~~~~~~~~~~~~~ +- Define consistent ownership semantics for buffers and child arrays. +- Employ reference counting or explicit release callbacks aligned with the Arrow C conventions. + +Nullability and validity +~~~~~~~~~~~~~~~~~~~~~~~~ +- Use standard validity bitmaps for nullability and adhere to Arrow’s canonical buffer organization (validity, offsets, data, etc.). + +Conversion boundaries +~~~~~~~~~~~~~~~~~~~~~~ +- Provide adapters to + * Read from columnar file formats (Parquet/ORC) into Arrow-compatible ``ArrowArray`` structures. + * Export/import data to other Arrow-compatible engines with zero or minimal copies. + +- Keep these adapters independent from heavy Arrow C++ SDK dependencies. diff --git a/docs/source/user_guide/catalog.rst b/docs/source/user_guide/catalog.rst new file mode 100644 index 0000000..8c435c1 --- /dev/null +++ b/docs/source/user_guide/catalog.rst @@ -0,0 +1,37 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. _catalog: + +Catalog +========================== +C++ Paimon provides a :ref:`Catalog abstraction ` to manage the table of contents and metadata. The Catalog +abstraction provides a series of ways to help you better integrate with computing engines. We always +recommend that you use Catalog to access the Paimon table. + +Filesystem Catalog +~~~~~~~~~~~~~~~~~~ +C++ Paimon catalog currently support one types of metastores filesystem metastore (default), +which stores both metadata and table files in filesystems. + +.. note:: + + Current C++ Paimon only supports filesystem catalog. In the future, we will + support REST catalog. + By using the Paimon REST catalog, changes to the catalog will be directly stored + in a remote catalog server which exposed through REST API. See `Java Paimon REST + Catalog `_. diff --git a/docs/source/user_guide/clean.rst b/docs/source/user_guide/clean.rst new file mode 100644 index 0000000..05e2645 --- /dev/null +++ b/docs/source/user_guide/clean.rst @@ -0,0 +1,162 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Data Cleanup +=================================== +.. note:: + + The entire data cleanup feature supports only append tables, and cleanup of index manifests, changelog, statistics are not supported. Do not use this feature with primary key tables. + +This document describes three cleanup capabilities: + +- :ref:`orphan-file-cleanup` +- :ref:`expiring-partitions` +- :ref:`expiring-snapshots` + +.. _orphan-file-cleanup: + +Orphan File Cleanup +------------------- + +Description +~~~~~~~~~~~~~~~~~~~ + +- Orphan file cleanup currently supports only append tables. +- It runs as an independent task: construct a ``CleanContext`` and + launch an ``OrphanFilesCleaner``. + +Detailed Steps +~~~~~~~~~~~~~~ + +1. List all Paimon-specific subdirectories under the table directory + (e.g., ``manifest/``, ``snapshot/``, ``f1=10/bucket-0``, ...). +2. Based on the subdirectories from step 1, enumerate all Paimon files + in the table directory. +3. Using snapshot information, determine all in-use ``manifest`` files + and data files. +4. Compute the set of files that appear in step 2 but not in step 3 + (i.e., orphan files). Among those, delete files whose modification + time is earlier than ``older_than_ms``. + +Performance Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Steps 1, 2, and 4 use an executor to parallelize I/O operations. +- Orphan file cleanup may take a long time; you can pass an executor + with more threads to accelerate the process. + +.. admonition:: TODO + :class: tip + + - Support cleanup of index manifests, changelog, statistics, etc. + +.. _expiring-partitions: + +Expiring Partitions +------------------- + +Description +~~~~~~~~~~~~~~~~~~~ + +- Executed within the Commit task. First construct a ``FileStoreCommit``. +- ``DropPartition`` uses a mark-delete strategy. Calling ``DropPartition`` + will send an ``Overwrite`` message, marking all data files in the + specified partition as ``DELETE``. +- A new snapshot is committed afterward. Actual deletion of data files + occurs as snapshots expire. + +Detailed Steps +~~~~~~~~~~~~~~ + +1. Build a ``ScanFilter`` for the partition and use the latest snapshot + to scan the partition. +2. Iterate over the scanned data file list (``ManifestEntries``) and + rewrite each entry’s type to ``DELETE``. +3. Commit using the rewritten ``ManifestEntries``. If the commit fails, + retry a limited number of times. + +.. _expiring-snapshots: + +Expiring Snapshots +------------------ + +Description +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Executed within the Commit task. First construct a ``FileStoreCommit``. +- The following optional configuration parameters control snapshot + expiration: + + - ``snapshot.num-retained.min``: The minimum number of completed + snapshots to retain (>= 1). Default: 10. + - ``snapshot.num-retained.max``: The maximum number of completed + snapshots to retain (>= ``snapshot.num-retained.min``). Default: + ``int32`` max value. + - ``snapshot.time-retained``: The maximum age of completed snapshots + to retain. Default: 1 hour. + - ``snapshot.expire.limit``: The maximum number of snapshots allowed + to expire at a time. Default: 10. + +- The snapshot expiration interface deletes data files according to the + expiration policy and returns the number of snapshots deleted. + +Detailed Steps +~~~~~~~~~~~~~~ + +1. Use ``snapshot_manager`` to find ``earliest_snapshot_id`` and + ``latest_snapshot_id``. +2. Based on ``earliest/latest_snapshot_id`` and the expire config, + determine the range of snapshots to clean. + +.. note:: + + Consumer subscription management (consumer manager) is not + currently supported. Users must ensure that snapshots to be expired + are not in use. + +3. Verify that the snapshot range is continuous. Normally, it is + continuous. If a snapshot is missing, assume earlier snapshots were + already cleaned and the missing files are orphaned remnants due to + I/O exceptions; they are out of scope for this cleanup. +4. Clean data files for the updated expiration range. + + - To decide whether a file from a snapshot should be deleted, check if it was marked ``DELETE`` in the delta of the subsequent snapshot. + - For an expiration range ``[begin, end)``, iterate over ``(begin,end]`` and delete data files whose type is ``DELETE`` in each ``snapshot.DeltaManifestList()``. + - If a file underwent multiple ``ADD`` and ``DELETE`` operations, deletion follows the operation order: + + * ``ADD`` then ``DELETE`` → the file is deleted. + * ``DELETE`` then ``ADD`` → the initial ``DELETE`` does not apply (file did not exist yet); the subsequent ``ADD`` ensures the file remains. + +5. Clean meta files: + - Preserve manifests used by the last snapshot in the cleanup range (``end_exclusive_id``). + - Delete manifest files used by snapshots from ``begin_inclusive_id`` to ``end_exclusive_id`` (exclusive) and delete the snapshot files themselves. +6. Rewrite ``EarliestHint`` to ``end_exclusive_id``. +7. Return the number of snapshots deleted, i.e., ``end_exclusive_id - begin_inclusive_id``. + +Performance Considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Step 4 uses an executor to parallelize file deletions. +- If deletion is slow, pass an executor with more threads to accelerate + the process. + +.. admonition:: TODO + :class: tip + + - Preserve tag (savepoint) data via ``tagManager``. + - Delete changelog files. + - Remove empty directories. diff --git a/docs/source/user_guide/commit.rst b/docs/source/user_guide/commit.rst new file mode 100644 index 0000000..e833991 --- /dev/null +++ b/docs/source/user_guide/commit.rst @@ -0,0 +1,126 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Commit +========================================== + +Commit is a critical stage in Paimon’s write path. It is responsible for generating +Snapshot files that describe the current state of a Paimon table. This document +provides a detailed analysis of the Paimon Commit process. + +Commit Process Overview +-------------------------- + +The input to Commit is a ``CommitMessage``, which is produced by the write +operation through ``PrepareCommit``. It records all data files generated during +the write phase. + +The Commit process consists of the following steps: + +1. Collect file changes +2. Compact (merge) Manifest files +3. Generate the Base Manifest List +4. Generate new Manifest files and the Delta Manifest List +5. Generate the Snapshot and HINT file + +Detailed Process +------------------- + +Collect File Changes +~~~~~~~~~~~~~~~~~~~~~~~~ + +During Commit, the system extracts key information from ``CommitMessages``—such as +file name, operation type (``ADD`` or ``DELETE``), the file’s Partition and Bucket— +and converts them into ``ManifestEntry`` records. + +A ``ManifestEntry`` represents a single operation record in a manifest file and +corresponds to a change to one file. + +Paimon snapshots track two manifest list files: + +- Base Manifest List: Describes the data that existed prior to the current Snapshot. + Because there may be multiple manifest files, the base manifest list records + metadata for all original manifest files. +- Delta Manifest List: Records the changes (adds/deletes) produced by the current Commit. + +Compact (Merge) Manifest Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To control the number and size of manifest files, the system determines whether +existing manifest files should be compacted prior to generating a new Snapshot. + +Compaction starts by fetching the latest Snapshot and using its base and delta +manifest lists to locate all relevant manifest files. + +Two compaction strategies are used: Full Compaction and Minor Compaction. + +Full Compaction +^^^^^^^^^^^^^^^ + +Full Compaction is attempted first. The system iterates over candidate files and +classifies them as follows: + +- Base files: If a file has no ``DELETE`` operations and its size exceeds the + target file size (default 8 MB), the file is categorized as base. +- Delta files: Remaining files are categorized as delta. The system computes the + total size of delta files; if the total exceeds the Full Compaction threshold + (default 16 MB), the delta files are merged. + +Minor Compaction +^^^^^^^^^^^^^^^^ + +If Full Compaction’s conditions are not met, Minor Compaction is attempted: + +- The system iterates over all files, skipping any file larger than the target file size. +- Whenever the accumulated size of selected files exceeds the target file size, those files are merged. +- If there are still unmerged files and their count exceeds the minimum compaction trigger + threshold (default 30 files), a merge is triggered. + +Compaction Rules +^^^^^^^^^^^^^^^^ + +1. If duplicate ``ADD`` operations for the same file are discovered, an error is raised. +2. ``ADD`` and ``DELETE`` for the same file neutralize each other. + +Generate the Base Manifest List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After compaction (which may or may not be triggered every time), the system obtains +a consolidated set of manifest file metadata. This metadata is written into a new +manifest list file, forming the Snapshot’s base manifest list. + +Generate New Manifest Files and the Delta Manifest List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The initially collected file change information is written into new manifest files. +Metadata for these newly created manifest files is then written into the delta +manifest list. + +Generate the Snapshot and HINT File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After the above steps are completed, the system generates a new Snapshot and performs +the following operations: + +1. Determine the new Snapshot ID based on the latest ``SnapshotId + 1``. +2. Record metadata such as ``schema id``, ``commit time``, and ``total record count``. +3. Atomicity guarantee: Generating a Snapshot is an atomic operation. If an exception + (e.g., an I/O error) occurs during the process, the manifest files and manifest list files + generated in Steps 2–4 are cleaned up and removed. +4. The Snapshot is written via a rename operation to ensure atomicity. +5. After the Snapshot is successfully written, the system writes the ``LATEST`` hint file + to reduce list operations when fetching the latest Snapshot. diff --git a/docs/source/user_guide/compaction.rst b/docs/source/user_guide/compaction.rst new file mode 100644 index 0000000..c2404cd --- /dev/null +++ b/docs/source/user_guide/compaction.rst @@ -0,0 +1,216 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Compaction +========== +Compaction is the process of merging multiple small data files into fewer, larger +files. It is a resource intensive procedure which consumes CPU time and disk IO, +so too frequent compaction may result in slower writes. However, without +compaction, the accumulation of small files degrades query performance. Tuning +compaction is therefore a trade-off between write throughput and read efficiency. + +.. note:: + - There can only be one job working on the same partition's compaction, + otherwise it will cause conflicts. + - C++ Paimon does not support producing changelog for now. + - Compaction is disabled when ``write-only`` is set to ``true``, or when the + table uses dynamic bucketing (``bucket = -1``) for append-only tables. + - For a complete list of compaction-related configurations, see the + :ref:`Options API Reference `. + +Append-Only Table Compaction +---------------------------- +In append-only table, data files are simply appended in sequence order. +Over time, many small files accumulate, which degrades read performance due to the +overhead of opening and scanning numerous files. + +Append-only table compaction merges multiple small files into fewer, larger files +to improve read efficiency. The compaction is performed asynchronously and does +not block writes. + +.. note:: + Append-only table compaction is only available for fixed-bucket mode + (``bucket > 0``). Dynamic bucketing (``bucket = -1``) does not support + compaction. Tables with blob columns also skip compaction. + +Auto Compaction +~~~~~~~~~~~~~~~ +During each flush, the writer triggers a best-effort auto compaction. The +compaction picker scans the file queue ordered by sequence number and selects a +contiguous window of files for merging when the number of candidate files reaches +the ``compaction.min.file-num`` threshold. + +Full Compaction +~~~~~~~~~~~~~~~ +Full compaction rewrites all eligible files in the bucket. During full +compaction: + +- Files whose size is already at or above ``compaction.file-size`` (and have no + associated deletion vectors) are skipped to avoid unnecessary rewrites. +- When deletion vectors are enabled, all files are always eligible for + compaction regardless of size, because deletion vectors must be applied. +- When ``compaction.force-rewrite-all-files`` is ``true``, all files are + rewritten unconditionally. +- Without deletion vectors, full compaction only proceeds when the number of + small files exceeds the number of large files and the total file count is at + least 3. + +After compaction, if the last output file is still smaller than +``compaction.file-size``, it is placed back into the compaction queue for future +merging. + +Append-Only Table Compaction Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :header-rows: 1 + :widths: 30 10 10 10 40 + + * - Option + - Required + - Default + - Type + - Description + * - ``compaction.min.file-num`` + - No + - 5 + - Integer + - The minimum number of files to trigger an auto compaction for + append-only tables. + + +Primary Key Table Compaction +---------------------------- +Primary key tables use an LSM tree (log-structured merge-tree) for file storage. +When more and more records are written, the number of sorted runs increases. +Because querying an LSM tree requires all sorted runs to be combined, too many +sorted runs will result in poor query performance, or even out of memory. + +To limit the number of sorted runs, several sorted runs are merged into one big +sorted run once in a while. Paimon currently adopts a compaction strategy similar +to RocksDB's `universal compaction +`_. + +Primary key table compaction solves: + +- Reduce Level 0 files to avoid poor query performance. +- Produce deletion vectors for MOW mode. + +Full Compaction +~~~~~~~~~~~~~~~ +Paimon uses Universal Compaction. By default, when there is too much incremental +data, Full Compaction will be automatically performed. You don't usually have to +worry about it. + +Paimon also provides configurations that allow for regular execution of Full +Compaction: + +- ``compaction.optimization-interval``: Implying how often to perform an + optimization full compaction. This configuration is used to ensure the query + timeliness of the read-optimized system table. +- ``compaction.total-size-threshold``: Full compaction will be constantly triggered + when total size is smaller than this threshold. +- ``compaction.incremental-size-threshold``: Full compaction will be constantly + triggered when incremental size is bigger than this threshold. + +Lookup Compaction +~~~~~~~~~~~~~~~~~ +When a primary key table is configured with ``lookup`` changelog producer or +``first-row`` merge engine or has enabled deletion vectors for MOW mode, Paimon +will use a radical compaction strategy to force compacting level 0 files to +higher levels for every compaction trigger. + +Paimon also provides configurations to optimize the frequency of this +compaction: + +- ``lookup-compact``: compact mode used for lookup compaction. Possible values: + + * ``radical``: will use ``ForceUpLevel0Compaction`` strategy to radically + compact new files. + * ``gentle``: will use ``UniversalCompaction`` strategy to gently compact new + files. + +- ``lookup-compact.max-interval``: The max interval for a forced L0 lookup + compaction to be triggered in ``gentle`` mode. This option is only valid when + ``lookup-compact`` mode is ``gentle``. + +By configuring ``lookup-compact`` as ``gentle``, new files in L0 will not be +compacted immediately. This may greatly reduce the overall resource usage at the +expense of worse data freshness in certain cases. + +Primary Key Table Compaction Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Number of Sorted Runs to Pause Writing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When the number of sorted runs is small, Paimon writers will perform compaction +asynchronously in separated threads, so records can be continuously written into +the table. However, to avoid unbounded growth of sorted runs, writers will pause +writing when the number of sorted runs hits the threshold. + +.. list-table:: + :header-rows: 1 + :widths: 30 10 10 10 40 + + * - Option + - Required + - Default + - Type + - Description + * - ``num-sorted-run.stop-trigger`` + - No + - (none) + - Integer + - The number of sorted runs that trigger the stopping of writes. The + default value is ``num-sorted-run.compaction-trigger + 3``. + +Write stalls will become less frequent when ``num-sorted-run.stop-trigger`` +becomes larger, thus improving writing performance. However, if this value +becomes too large, more memory and CPU time will be needed when querying the +table. + +Number of Sorted Runs to Trigger Compaction +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Paimon uses LSM tree which supports a large number of updates. LSM organizes +files in several sorted runs. When querying records from an LSM tree, all sorted +runs must be combined to produce a complete view of all records. + +One can easily see that too many sorted runs will result in poor query +performance. To keep the number of sorted runs in a reasonable range, Paimon +writers will automatically perform compactions. The following table property +determines the minimum number of sorted runs to trigger a compaction. + +.. list-table:: + :header-rows: 1 + :widths: 30 10 10 10 40 + + * - Option + - Required + - Default + - Type + - Description + * - ``num-sorted-run.compaction-trigger`` + - No + - 5 + - Integer + - The sorted run number to trigger compaction. Includes level 0 files (one + file one sorted run) and high-level runs (one level one sorted run). + +Compaction will become less frequent when ``num-sorted-run.compaction-trigger`` +becomes larger, thus improving writing performance. However, if this value +becomes too large, more memory and CPU time will be needed when querying the +table. This is a trade-off between writing and query performance. diff --git a/docs/source/user_guide/data_types.rst b/docs/source/user_guide/data_types.rst new file mode 100644 index 0000000..5759119 --- /dev/null +++ b/docs/source/user_guide/data_types.rst @@ -0,0 +1,212 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Data Types +========== + +A data type describes the logical type of a value in the table ecosystem. It can +be used to declare input and/or output types of operations. + +All data types by Java Paimon are as follows, C++ Paimon uses Apache Arrow as +its schema representation. The following table shows the mapping between `Java +Paimon DataTypes `_ +and `Arrow DataTypes `_: + +.. list-table:: + :header-rows: 1 + :widths: 10 10 30 + + * - Java Paimon DataType + - Arrow DataType + - Description + + * - ``BOOLEAN`` + - Boolean + - Data type of a boolean with a (possibly) three-valued logic of TRUE, FALSE, and UNKNOWN. + + * - ``CHAR`` + + ``CHAR(n)`` + - Not Supported + - Data type of a fixed-length character string. + + The type can be declared using ``CHAR(n)`` where n is the number of code + points. n must have a value between 1 and 2,147,483,647 (both inclusive). + If no length is specified, n is equal to 1. + + * - ``VARCHAR`` + + ``VARCHAR(n)`` + + - Not Supported + - Data type of a variable-length character string. + + The type can be declared using ``VARCHAR(n)`` where n is the maximum + number of code points. n must have a value between 1 and 2,147,483,647 + (both inclusive). If no length is specified, n is equal to 1. + + * - ``STRING`` + - Utf8 + - Data type of a variable-length character string. ``STRING`` is a synonym for ``VARCHAR(2147483647)``. + + * - ``BINARY`` + + ``BINARY(n)`` + - Not Supported + - Data type of a fixed-length binary string (=a sequence of bytes). + + The type can be declared using ``BINARY(n)`` where n is the number of + bytes. n must have a value between 1 and 2,147,483,647 (both inclusive). + If no length is specified, n is equal to 1. + + * - ``VARBINARY`` + + ``VARBINARY(n)`` + - Not Supported + - Data type of a variable-length binary string (=a sequence of bytes). + + The type can be declared using ``VARBINARY(n)`` where n is the maximum + number of bytes. n must have a value between 1 and 2,147,483,647 + (both inclusive). If no length is specified, n is equal to 1. + + * - ``BYTES`` + - Binary + - ``BYTES`` is a synonym for ``VARBINARY(2147483647)``. + + * - ``DECIMAL`` + + ``DECIMAL(p)`` + + ``DECIMAL(p, s)`` + - Decimal128 + - Data type of a decimal number with fixed precision and scale. + + The type can be declared using ``DECIMAL(p, s)`` where p is the number of + digits in a number (precision) and s is the number of digits to the right + of the decimal point in a number (scale). p must have a value between 1 + and 38 (both inclusive). s must have a value between 0 and p + (both inclusive). The default value for p is 10. The default value for s is 0. + + * - ``TINYINT`` + - Int8 + - Data type of a 1-byte signed integer with values from -128 to 127. + + * - ``SMALLINT`` + - Int16 + - Data type of a 2-byte signed integer with values from -32,768 to 32,767. + + * - ``INT`` + - Int32 + - Data type of a 4-byte signed integer with values from -2,147,483,648 to 2,147,483,647. + + * - ``BIGINT`` + - Int64 + - Data type of an 8-byte signed integer with values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. + + * - ``FLOAT`` + - Float + - Data type of a 4-byte single precision floating point number. + + Compared to the SQL standard, the type does not take parameters. + + * - ``DOUBLE`` + - Double + - Data type of an 8-byte double precision floating point number. + + * - ``DATE`` + - Date32 + - Data type of a date consisting of year-month-day with values ranging from 0000-01-01 to 9999-12-31. + + Compared to the SQL standard, the range starts at year 0000. + + * - ``TIME`` + + ``TIME(p)`` + - Not Supported + - Data type of a time without time zone consisting of hour:minute:second[.fractional] with up to nanosecond precision and values ranging from 00:00:00.000000000 to 23:59:59.999999999. + + The type can be declared using ``TIME(p)`` where p is the number of digits of + fractional seconds (precision). p must have a value between 0 and 9 + (both inclusive). If no precision is specified, p is equal to 0. + + * - ``TIMESTAMP`` + + ``TIMESTAMP(p)`` + - Timestamp + - Data type of a timestamp without time zone consisting of year-month-day hour:minute:second[.fractional] with up to nanosecond precision and values ranging from 0000-01-01 00:00:00.000000000 to 9999-12-31 23:59:59.999999999. + + The type can be declared using ``TIMESTAMP(p)`` where p is the number of + digits of fractional seconds (precision). p must have a value between 0 + and 9 (both inclusive). If no precision is specified, p is equal to 6. + + * - ``TIMESTAMP WITH LOCAL TIME ZONE`` + + ``TIMESTAMP(p) WITH LOCAL TIME ZONE`` + - Timestamp + - Data type of a timestamp with local time zone consisting of year-month-day hour:minute:second[.fractional] zone with up to nanosecond precision and values ranging from 0000-01-01 00:00:00.000000000 +14:59 to 9999-12-31 23:59:59.999999999 -14:59. + + This type fills the gap between time zone free and time zone mandatory + timestamp types by allowing the interpretation of UTC timestamps according + to the configured session time zone. A conversion from and to int describes + the number of seconds since epoch. A conversion from and to long describes the number of milliseconds since epoch. + + * - ``ARRAY`` + - List + - Data type of an array of elements with same subtype. + + Compared to the SQL standard, the maximum cardinality of an array cannot be specified but is fixed at 2,147,483,647. Also, any valid type is supported as a subtype. + + The type can be declared using ``ARRAY`` where t is the data type of the contained elements. + + * - ``MAP`` + - Map + - Data type of an associative array that maps keys to values (including NULL). A map cannot contain duplicate keys; each key can map to at most one value. + + There is no restriction of element types; it is the responsibility of the user to ensure uniqueness. + + The type can be declared using ``MAP`` where kt is the data type of the key elements and vt is the data type of the value elements. + + **Note:** In C++ Paimon, map keys must be explicitly marked as ``NOT NULL``. + Apache Arrow does not support nullable map keys. If the key type is not + marked as ``NOT NULL`` in the schema, parsing will fail with an error. + + * - ``MULTISET`` + - Not Supported + - Data type of a multiset (=bag). Unlike a set, it allows for multiple instances for each of its elements with a common subtype. Each unique value (including NULL) is mapped to some multiplicity. + + There is no restriction of element types; it is the responsibility of the user to ensure uniqueness. + + The type can be declared using ``MULTISET`` where t is the data type of the contained elements. + + * - ``ROW`` + + ``ROW`` + - Struct + - Data type of a sequence of fields. + + A field consists of a field name, field type, and an optional description. + The most specific type of a row of a table is a row type. In this case, + each column of the row corresponds to the field of the row type that has + the same ordinal position as the column. + + Compared to the SQL standard, an optional field description simplifies + the handling with complex structures. + + A row type is similar to the ``STRUCT`` type known from other non-standard-compliant frameworks. + + The type can be declared using ``ROW`` where n + is the unique name of a field, t is the logical type of a field, d is the description of a field. diff --git a/docs/source/user_guide/global_index.rst b/docs/source/user_guide/global_index.rst new file mode 100644 index 0000000..5cb057a --- /dev/null +++ b/docs/source/user_guide/global_index.rst @@ -0,0 +1,95 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Global Index +============ + +Global Index is a powerful indexing mechanism for append-only tables. +It enables efficient row-level lookups and filtering without full-table scans. +Paimon supports multiple global index types: + +- **Bitmap Index**: A bitmap-based index. Each distinct value is mapped to a compressed bitmap (RoaringBitmap) that records which rows contain that value, enabling extremely fast set membership tests. +- **BTree Index**: An efficient index based on multi-level SST files for scalar column lookups. +- **Range Bitmap Index**: A range bitmap index optimized for range predicates on ordered scalar columns. Extends the bitmap approach by encoding value ordering, enabling efficient less-than, greater-than, and range conditions. +- **Lucene Index**: A full-text search index powered by Lucene++. Supports tokenized text search with multiple modes including match-all, match-any, phrase, prefix, and wildcard queries. +- **Vector Index (Lumina)**: An approximate nearest neighbor (ANN) index powered by Lumina for vector similarity search with configurable distance metrics. + +Global indexes work on top of Data Evolution tables. To use global indexes, your table must have: + +- ``'bucket' = '-1'`` (unaware-bucket mode) +- ``'row-tracking.enabled' = 'true'`` +- ``'data-evolution.enabled' = 'true'`` + +Bitmap Index +------------ + +A bitmap-based index for Equal and In predicates. Each distinct value in the indexed column +is mapped to a compressed bitmap (RoaringBitmap) that records which rows contain that value. +This allows extremely fast set membership tests. + +BTree Index +----------- + +BTree is an efficient index based on multi-level SST files, supporting rich predicate pushdown, block cache, file-level min/max key pruning, lazy loading, and block compression. + +**Special Configuration:** + +- **Option**: ``btree-index.read-buffer-size`` + + - **Description**: Optional. Specifies the read buffer size for the B-tree index. This setting can be tuned based on query patterns: + + - For **range queries** (e.g., ``VisitLessThan``, ``VisitGreaterOrEqual``), increasing the buffer size (e.g., to 1MB) may improve I/O bandwidth and sequential read performance. + - For **point queries** (e.g., ``VisitEqual``), buffering can introduce negative effects due to read amplification; it is recommended to leave this option unset. + +Range Bitmap Index +------------------ + +A range bitmap index optimized for range predicates on ordered scalar columns. It extends the +bitmap approach by encoding value ordering information, enabling efficient evaluation of +less-than, greater-than, and range conditions without scanning all bitmaps. + + +Lucene Index +------------ + +A full-text search index powered by Lucene++. It supports tokenized text search with multiple +search modes including match-all, match-any, phrase, prefix, and wildcard queries. + +**Supported search types:** + +- ``MATCH_ALL``: All terms in the query must be present (AND semantics). +- ``MATCH_ANY``: Any term in the query can match (OR semantics). +- ``PHRASE``: Matches the exact sequence of words (with proximity). +- ``PREFIX``: Matches terms starting with the given string (e.g., "run*" → running, runner). +- ``WILDCARD``: Supports wildcards ``*`` and ``?`` (e.g., "ap*e", "app?e" → "apple"). + +**Special Configuration:** + +- **Option**: ``lucene-fts.write.tmp.directory`` + + - **Description**: Specifies the temporary directory used during Lucene index writing. No default value; must be explicitly set. + +- **Environment Variable**: ``PAIMON_JIEBA_DICT_DIR`` + + - **Description**: Specifies the directory containing Jieba dictionary files for Chinese text tokenization. At runtime, the system first checks this environment variable; if not set, it falls back to the compile-time ``JIEBA_TEST_DICT_DIR`` macro (only available in test builds). If neither is available, will fail with an error. + +Vector Index (Lumina) +--------------------- + +An approximate nearest neighbor (ANN) index powered by Lumina for vector similarity search. +Supports high-dimensional vector search with configurable distance metrics and encoding strategies. +For more configurations, please refer to the third_party/lumina/reference directory. diff --git a/docs/source/user_guide/manifest.rst b/docs/source/user_guide/manifest.rst new file mode 100644 index 0000000..9b69ff5 --- /dev/null +++ b/docs/source/user_guide/manifest.rst @@ -0,0 +1,132 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/concepts/spec/manifest.md + +Manifest +======== + +Manifest List +------------- + +.. code-block:: shell + + ├── manifest + └── manifest-list-51c16f7b-421c-4bc0-80a0-17677f343358-1 + +Manifest List includes metadata of several manifest files. Its name contains a UUID. It is an Avro file with the following schema: + +1. ``_FILE_NAME``: STRING — manifest file name. +2. ``_FILE_SIZE``: BIGINT — manifest file size. +3. ``_NUM_ADDED_FILES``: BIGINT — number of added files in the manifest. +4. ``_NUM_DELETED_FILES``: BIGINT — number of deleted files in the manifest. +5. ``_PARTITION_STATS``: SimpleStats — partition stats. The minimum and maximum values of partition fields in this manifest are beneficial for skipping certain manifest files during queries. +6. ``_SCHEMA_ID``: BIGINT — schema id used when writing this manifest file. + +Manifest +-------- + +Manifest includes metadata of several data files, changelog files, or table-index files. Its name contains a UUID, and it is an Avro file. + +The changes of the file are saved in the manifest, and a file can be added or deleted. Manifests should be in an orderly manner, and the same file may be added or deleted multiple times. The last version should be read. This design makes commit lighter to support file deletion generated by compaction. + +Data Manifest +------------- + +Data Manifest includes metadata of several data files or changelog files. + +.. code-block:: shell + + ├── manifest + └── manifest-6758823b-2010-4d06-aef0-3b1b597723d6-0 + +Schema: + +1. ``_KIND``: TINYINT — ``ADD`` or ``DELETE``. +2. ``_PARTITION``: BYTES — partition spec, a BinaryRow. +3. ``_BUCKET``: INT — bucket of this file. +4. ``_TOTAL_BUCKETS``: INT — total buckets when writing this file; used for verification after bucket changes. +5. ``_FILE``: data file metadata. + +Data file metadata: + +1. ``_FILE_NAME``: STRING — file name. +2. ``_FILE_SIZE``: BIGINT — file size. +3. ``_ROW_COUNT``: BIGINT — total number of rows (including add & delete) in this file. +4. ``_MIN_KEY``: STRING — minimum key of this file. +5. ``_MAX_KEY``: STRING — maximum key of this file. +6. ``_KEY_STATS``: SimpleStats — statistics of the key. +7. ``_VALUE_STATS``: SimpleStats — statistics of the value. +8. ``_MIN_SEQUENCE_NUMBER``: BIGINT — minimum sequence number. +9. ``_MAX_SEQUENCE_NUMBER``: BIGINT — maximum sequence number. +10. ``_SCHEMA_ID``: BIGINT — schema id when writing this file. +11. ``_LEVEL``: INT — level of this file in LSM. +12. ``_EXTRA_FILES``: ARRAY — extra files for this file (e.g., data file index file). +13. ``_CREATION_TIME``: TIMESTAMP_MILLIS — creation time of this file. +14. ``_DELETE_ROW_COUNT``: BIGINT — rowCount = addRowCount + deleteRowCount. +15. ``_EMBEDDED_FILE_INDEX``: BYTES — if the data file index is small, store the index in the manifest. +16. ``_FILE_SOURCE``: TINYINT — indicates whether this file is generated as an ``APPEND`` or ``COMPACT`` file. +17. ``_VALUE_STATS_COLS``: ARRAY — statistical columns in metadata. +18. ``_EXTERNAL_PATH``: STRING — external path of this file; ``null`` if it is in the warehouse. + +Index Manifest +-------------- + +Index Manifest includes metadata of several table-index files. + +.. code-block:: shell + + ├── manifest + └── index-manifest-5d670043-da25-4265-9a26-e31affc98039-0 + +Schema: + +1. ``_KIND``: TINYINT — ``ADD`` or ``DELETE``. +2. ``_PARTITION``: BYTES — partition spec, a BinaryRow. +3. ``_BUCKET``: INT — bucket of this file. +4. ``_INDEX_TYPE``: STRING — ``HASH`` or ``DELETION_VECTORS``. +5. ``_FILE_NAME``: STRING — file name. +6. ``_FILE_SIZE``: BIGINT — file size. +7. ``_ROW_COUNT``: BIGINT — total number of rows. +8. ``_DELETIONS_VECTORS_RANGES``: Metadata only used by ``DELETION_VECTORS``; an array of deletion vector metadata. Each deletion vector metadata has: + - ``f0``: the data file name corresponding to this deletion vector. + - ``f1``: the starting offset of this deletion vector in the index file. + - ``f2``: the length of this deletion vector in the index file. + - ``_CARDINALITY``: the number of deleted rows. + +Appendix +-------- + +SimpleStats +~~~~~~~~~~~ + +SimpleStats is a nested row with the following schema: + +1. ``_MIN_VALUES``: BYTES — BinaryRow; the minimum values of the columns. +2. ``_MAX_VALUES``: BYTES — BinaryRow; the maximum values of the columns. +3. ``_NULL_COUNTS``: ARRAY — the number of nulls in the columns. + +BinaryRow +~~~~~~~~~ + +BinaryRow is backed by bytes instead of ``Object``. It can significantly reduce the serialization/deserialization of Java objects. + +A row has two parts: fixed-length part and variable-length part. + +- Fixed-length part contains a 1-byte header, null bit set, and field values. The null bit set is used for null tracking and is aligned to 8-byte word boundaries. +- Field values hold fixed-length primitive types and variable-length values that can be stored in 8 bytes. If the variable-length field does not fit in 8 bytes, then the fixed-length part stores the length and the offset of the variable-length part. diff --git a/docs/source/user_guide/prefetch.rst b/docs/source/user_guide/prefetch.rst new file mode 100644 index 0000000..15ae9d7 --- /dev/null +++ b/docs/source/user_guide/prefetch.rst @@ -0,0 +1,48 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Prefetch +======== + +.. image:: ../_static/prefetch.svg + :alt: File Layout + :align: center + :width: 100% + +In C++ Paimon, we use a multi-producer, single-consumer model to optimize file reading. The core idea is to split a file into line-based ReadRanges and assign them to multiple reader threads (producers). Each reader thread owns an independent result queue that holds its processed RecordBatches. In the main reader thread (the consumer), we sort the heads of all queues by the ReadRange start offset in ascending order and select the RecordBatch with the smallest start offset to ensure globally ordered results. + +Read Range Splitting Strategy +============================= + +Designing an efficient ReadRange splitting strategy requires balancing two key objectives: + +- Minimize read amplification: Ensure the data fetched from storage is used effectively, avoiding unnecessary I/O overhead. +- Reduce ReadRange span: Ideally, the size of a ReadRange should match a single read batch size to enable fine-grained parallel control. + +Below we detail how these strategies are applied to formats Parquet. + +Parquet +======== + +Parquet files are organized into RowGroups and Pages. Since C++ Parquet does not support row-level seeking, prefetching can only be done at the RowGroup level. This naturally avoids read amplification, but introduces a new challenge: if a file contains only a small number of RowGroups, parallelism is severely limited. Therefore, we recommend users reduce RowGroup size when writing Parquet files to increase opportunities for parallel processing. + +Another critical difference is the read behavior compared to Orc. Orc strictly returns RecordBatches aligned to Stripe boundaries, whereas C++ Parquet may return a RecordBatch containing data from multiple RowGroups. This can lead to output order confusion during parallel reads. We modified C++ Parquet internals to return results strictly aligned to RowGroup boundaries, matching Orc’s behavior. With this change, parallel reading no longer requires complex seek operations, improving overall read efficiency. + +.. admonition:: TODO + :class: tip + + Support prefetch for Orc. diff --git a/docs/source/user_guide/primary_key_table.rst b/docs/source/user_guide/primary_key_table.rst new file mode 100644 index 0000000..7a7e25a --- /dev/null +++ b/docs/source/user_guide/primary_key_table.rst @@ -0,0 +1,82 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/primary-key-table/overview.md + +Primary Key Table +================= +If you define a table with primary key, you can insert, update or delete records +in the table. + +Primary keys consist of a set of columns that contain unique values for each +record. Paimon enforces data ordering by sorting the primary key within each +bucket, allowing users to achieve high performance by applying filtering +conditions on the primary key. + + +Bucket +------- +Unpartitioned tables, or partitions in partitioned tables, are sub-divided into +buckets, to provide extra structure to the data that may be used for more +efficient querying. + +Each bucket directory contains an LSM tree and its changelog files. + +.. note:: + Changelog is not supported yet for C++ Paimon primary key table write. + +The range for a bucket is determined by the hash value of one or more columns in +the records. Users can specify bucketing columns by providing the bucket-key option. +If no bucket-key option is specified, the primary key (if defined) or the complete +record will be used as the bucket key. + +A bucket is the smallest storage unit for reads and writes, so the number of +buckets limits the maximum processing parallelism. This number should not be too +big, though, as it will result in lots of small files and low read performance. +In general, the recommended data size in each bucket is about 200MB - 1GB. + +Also, see rescale bucket if you want to adjust the number of buckets after a +table is created. + + +LSM Trees +------------- +Paimon adopts the LSM tree (log-structured merge-tree) as the data structure for +file storage. This documentation briefly introduces the concepts about LSM trees. + +Sorted Runs +~~~~~~~~~~~~~~ +LSM tree organizes files into several sorted runs. A sorted run consists of one +or multiple data files and each data file belongs to exactly one sorted run. + +Records within a data file are sorted by their primary keys. Within a sorted run, +ranges of primary keys of data files never overlap. + +.. image:: ../_static/sorted-runs.png + :alt: Sorted Runs + :align: center + :width: 100% + +As you can see, different sorted runs may have overlapped primary key ranges, +and may even contain the same primary key. When querying the LSM tree, all +sorted runs must be combined and all records with the same primary key must be +merged according to the user-specified merge engine and the timestamp of each record. + +New records written into the LSM tree will be first buffered in memory. When the +memory buffer is full, all records in memory will be sorted and flushed to disk. +A new sorted run is now created. diff --git a/docs/source/user_guide/read.rst b/docs/source/user_guide/read.rst new file mode 100644 index 0000000..9440773 --- /dev/null +++ b/docs/source/user_guide/read.rst @@ -0,0 +1,302 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Read +==== +Paimon by functionality can be divided into two layers: + +- Control Plane: Responsible for accessing and managing Meta (snapshot, manifest, etc.), including: + - Catalog / Database access + - Table retrieval + - Collection and resolution of data files + +- Data Plane: Responsible for accessing actual data files, including: + - Readers for various file formats + - Coordinated reading of file collections + +The control plane and data plane interact primarily via DataSplit (the query plan). C++ Paimon currently supports a standard +DataSplit protocol which includes the necessary meta information to access data files. With DataSplit, a high-performance +data access path can be integrated. + +At compute time, the execution engine (reader) does not need to be aware of the concrete table type or its metadata details. +It only needs to follow the instructions within the DataSplit (query plan) to perform data reading operations. + +With the layered abstraction of the control plane and data plane, and the use of DataSplit as a stable protocol interface, +the two layers can evolve their functionality and optimize code relatively independently. This design also enables +cross-language task scheduling and interaction (e.g., Java and C++), substantially reducing engineering maintenance costs +across the two language ecosystems. + + +Schema Evolution +----------------------- +Scope and Compatibility +~~~~~~~~~~~~~~~~~~~~~~~~ + +C++ Paimon supports all evolution kinds available in Java Paimon for non-nested types: + +- Add column +- Drop column +- Reorder columns +- Rename column +- Change column type + +.. note:: + + - Only non-nested type evolution is supported. Nested columns (struct, array, map) are not supported. + - Partition keys: Only column reordering is supported; other operations are not supported (consistent with Java Paimon). + - Primary key: + + - Adding or dropping columns is not supported. + - Other operations are supported (consistent with Java Paimon). + +Per-File Schema via Field IDs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In DataSplit, each file may have a completely different data schema. Paimon uses field IDs to uniquely identify fields. + +Overflow Behavior Disclaimer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Overflow behavior is undefined for C++ and Java Paimon. Results in overflow scenarios may: + +- Be incorrect values, +- Return an error status, +- Or be null. + +C++ Paimon does not guarantee identical results to Java Paimon in overflow scenarios. Users should not rely on identical +return values between implementations. + +Type Change Support Matrix +~~~~~~~~~~~~~~~~~~~~~~~~~~ +The table below indicates support for changing a column type from ``source`` to ``target``. Refer to the numbered notes below the table +for caveats. + +.. list-table:: + :header-rows: 1 + :widths: 12 10 10 10 10 10 10 8 12 10 8 18 10 + + * - src \\ target + - tinyint + - smallint + - int + - bigint + - float + - double + - bool + - string + - binary + - date + - timestamp (without tz) + - decimal + * - tinyint + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ❌ + - ❌ + - ❌ + - ✅ + * - smallint + - ✅ 1️⃣ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ❌ + - ❌ + - ❌ + - ✅ + * - int + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ❌ + - ❌ + - ✅ 1️⃣ + - ✅ + * - bigint + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ❌ + - ❌ + - ✅ 6️⃣ + - ✅ + * - float + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ + - ✅ + - ✅ + - ✅ 3️⃣ 4️⃣ + - ❌ + - ❌ + - ❌ + - ✅ + * - double + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ 2️⃣ + - ✅ + - ✅ + - ✅ 3️⃣ 4️⃣ + - ❌ + - ❌ + - ❌ + - ✅ + * - bool + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ + - ❌ + - ❌ + - ❌ + - ✅ + * - string + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ 3️⃣ + - ✅ 3️⃣ + - ✅ + - ✅ + - ✅ + - ✅ + - ✅ 5️⃣ + - ✅ 7️⃣ + * - binary + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ✅ + - ✅ + - ❌ + - ❌ + - ❌ + * - date + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ❌ + - ✅ + - ❌ + - ✅ + - ✅ 5️⃣ + - ❌ + * - timestamp (without tz) + - ❌ + - ❌ + - ✅ 1️⃣ + - ✅ + - ❌ + - ❌ + - ❌ + - ✅ + - ❌ + - ✅ + - ✅ + - ❌ + * - decimal + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ 1️⃣ + - ✅ + - ✅ + - ❌ + - ✅ + - ❌ + - ❌ + - ❌ + - ✅ + +.. admonition:: Overflow Behavior Notes + :class: note + + 1️⃣ Integer downcast overflow behavior matches Java in specific cases. + Example: smallint -> tinyint, 32767 becomes -1; int -> smallint, -2147483648 becomes 0. + + 2️⃣ Floating-point overflow behavior is partially consistent with Java and partially different. + Example: float -> tinyint + - Java: MAX_FLOAT -> -1, INFINITY -> -1 + - C++: MAX_FLOAT -> 0, INFINITY -> 0 + + 3️⃣ Keyword differences for special float/double values: + - Java: Infinity, -Infinity, NaN + - C++: inf, -inf, nan + + 4️⃣ Printing difference: + - C++ Paimon prints 1.0 as ``1`` + - Java Paimon prints 1.0 as ``1.0`` + + 5️⃣ Timestamp precision and range differences: + - Java Paimon: 0000-01-01 00:00:00.000000000 to 9999-12-31 23:59:59.999999999 + - C++ Paimon: 1677-09-21 00:12:43.145224192 to 2262-04-11 23:47:16.854775807 + - C++ only supports nanosecond precision; range is smaller. + + 6️⃣ bigint -> timestamp range differences: + - Java Paimon (ms): ``[MIN_INT64/1000, MAX_INT64/1000]`` seconds + - C++ Paimon (ns): ``[MIN_INT64/1e9, MAX_INT64/1e9]`` seconds + + 7️⃣ string -> decimal with precision > 38: + - C++ returns ``null`` if parsing would overflow 128-bit arithmetic. + - Java may rescale and return a value based on the rescaled precision. + - Example input: ``1111111111111111111111111111111111111.15``, Java returns: ``1111111111111111111111111111111111111.2``, C++ returns: ``null`` + +Implementation Guidance +~~~~~~~~~~~~~~~~~~~~~~~ + +- Use DataSplit as the sole interface between control and data planes. Treat it as the canonical query plan contract. +- Resolve field types and IDs per file; prefer inline data file metadata, fallback to table schema files when necessary. +- Expect per-file schema variability; design readers to align by field IDs rather than positional indices. +- Do not assume identical overflow semantics across C++ and Java; tests should validate acceptable ranges and nullability. +- For timestamp handling, consider precision/range constraints in C++ when interoperating with Java-produced data splits. diff --git a/docs/source/user_guide/schema.rst b/docs/source/user_guide/schema.rst new file mode 100644 index 0000000..6c716af --- /dev/null +++ b/docs/source/user_guide/schema.rst @@ -0,0 +1,135 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/concepts/spec/schema.md + +Schema +====== + +The version of the schema file starts from 0 and currently retains all versions of the schema. +There may be old files that rely on the old schema version, so its deletion should be done with caution. + +Schema File is JSON, it includes: + +1. ``fields``: data field list. Each data field contains ``id``, ``name``, ``type``. Field ``id`` is used to support schema evolution. +2. ``partitionKeys``: field name list. Partition definition of the table; it cannot be modified. +3. ``primaryKeys``: field name list. Primary key definition of the table; it cannot be modified. +4. ``options``: ``map``, unordered. Options of the table, including a lot of capabilities and optimizations. + +Example +------- + +.. code-block:: json + + { + "version" : 3, + "id" : 0, + "fields" : [ { + "id" : 0, + "name" : "order_id", + "type" : "BIGINT NOT NULL" + }, { + "id" : 1, + "name" : "order_name", + "type" : "STRING" + }, { + "id" : 2, + "name" : "order_user_id", + "type" : "BIGINT" + }, { + "id" : 3, + "name" : "order_shop_id", + "type" : "BIGINT" + } ], + "highestFieldId" : 3, + "partitionKeys" : [ ], + "primaryKeys" : [ "order_id" ], + "options" : { + "bucket" : "5" + }, + "comment" : "", + "timeMillis" : 1720496663041 + } + +Compatibility +------------- + +For old versions: + +- Version 1: should put ``bucket -> 1`` to ``options`` if there is no ``bucket`` key. +- Versions 1 & 2: should put ``file.format -> orc`` to ``options`` if there is no ``file.format`` key. + +DataField +--------- + +DataField represents a column of the table. + +1. ``id``: int, column id, automatic increment; it is used for schema evolution. +2. ``name``: string, column name. +3. ``type``: data type, very similar to SQL type string. +4. ``description``: string. + +Limitations +----------- + +MAP Key Must Be NOT NULL +^^^^^^^^^^^^^^^^^^^^^^^^ + +Apache Arrow does not support nullable map keys. When defining a ``MAP`` type in the schema, +the key must be explicitly marked as ``NOT NULL``. If the key is not marked as ``NOT NULL``, +schema parsing will fail with an error. + +For example, the following is **valid**: + +.. code-block:: json + + { + "type": "MAP", + "key": "TINYINT NOT NULL", + "value": "SMALLINT" + } + +The following is **invalid** and will be rejected: + +.. code-block:: json + + { + "type": "MAP", + "key": "TINYINT", + "value": "SMALLINT" + } + +Update Schema +------------- + +Updating the schema should generate a new schema file. + +.. code-block:: text + + warehouse + └── default.db + └── my_table + ├── schema + ├── schema-0 + ├── schema-1 + └── schema-2 + +There is a reference to schema in the snapshot. The schema file with the highest numerical value is usually the latest schema file. + +Old schema files cannot be directly deleted because there may be old data files that reference old schema files. When +reading the table, it is necessary to rely on them for schema evolution reading. diff --git a/docs/source/user_guide/snapshot.rst b/docs/source/user_guide/snapshot.rst new file mode 100644 index 0000000..a2aa1e7 --- /dev/null +++ b/docs/source/user_guide/snapshot.rst @@ -0,0 +1,67 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +.. Borrowed the file from Apache Paimon: +.. https://github.com/apache/paimon/blob/master/docs/content/concepts/spec/snapshot.md + +Snapshot +======== + +Each commit generates a snapshot file, and the version of the snapshot file starts from 1 and must be continuous. +``EARLIEST`` and ``LATEST`` are hint files at the beginning and end of the snapshot list, and they can be inaccurate. +When hint files are inaccurate, the reader will scan all snapshot files to determine the beginning and end. + +Directory Layout +---------------- + +.. code-block:: shell + + warehouse + └── default.db + └── my_table + ├── snapshot + ├── EARLIEST + ├── LATEST + ├── snapshot-1 + ├── snapshot-2 + └── snapshot-3 + +Writing commit will preempt the next snapshot id, and once the snapshot file is successfully written, this commit will +become visible. + +Snapshot File +------------- + +Snapshot file is JSON and includes: + +1. ``version``: Snapshot file version, current is 3. +2. ``id``: Snapshot id, same as the file name. +3. ``schemaId``: The corresponding schema version for this commit. +4. ``baseManifestList``: A manifest list recording all changes from the previous snapshots. +5. ``deltaManifestList``: A manifest list recording all new changes occurred in this snapshot. +6. ``changelogManifestList``: A manifest list recording all changelog produced in this snapshot; ``null`` if no changelog is produced. +7. ``indexManifest``: A manifest recording all index files of this table; ``null`` if no table index file exists. +8. ``commitUser``: Usually generated by UUID; used for recovery of streaming writes—one stream write job with one user. +9. ``commitIdentifier``: Transaction id corresponding to streaming write; each transaction may result in multiple commits for different ``commitKind`` values. +10. ``commitKind``: Type of changes in this snapshot, including ``append``, ``compact``, ``overwrite`` and ``analyze``. +11. ``timeMillis``: Commit time in milliseconds. +12. ``logOffsets``: Commit log offsets. +13. ``totalRecordCount``: Record count of all changes occurred in this snapshot. +14. ``deltaRecordCount``: Record count of all new changes occurred in this snapshot. +15. ``changelogRecordCount``: Record count of all changelog produced in this snapshot. +16. ``watermark``: Watermark for input records, from Flink watermark mechanism; ``Long.MIN_VALUE`` if there is no watermark. +17. ``statistics``: Stats file name for statistics of this table. diff --git a/docs/source/user_guide/write.rst b/docs/source/user_guide/write.rst new file mode 100644 index 0000000..da2120b --- /dev/null +++ b/docs/source/user_guide/write.rst @@ -0,0 +1,166 @@ +.. Licensed to the Apache Software Foundation (ASF) under one +.. or more contributor license agreements. See the NOTICE file +.. distributed with this work for additional information +.. regarding copyright ownership. The ASF licenses this file +.. to you under the Apache License, Version 2.0 (the +.. "License"); you may not use this file except in compliance +.. with the License. You may obtain a copy of the License at + +.. http://www.apache.org/licenses/LICENSE-2.0 + +.. Unless required by applicable law or agreed to in writing, +.. software distributed under the License is distributed on an +.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +.. KIND, either express or implied. See the License for the +.. specific language governing permissions and limitations +.. under the License. + +Write +===== +Batch writing requires the compute engine to pre-bucket data (bucket), using the +same bucketing strategy as Paimon to ensure correct ``Scan`` behavior, and to +specify the target ``partition``. Data should be accumulated into ``RecordBatch`` +and written to Paimon. + +Paimon C++ uses Apache Arrow as the :ref:`in-memory columnar format` +to more efficiently support writing to disk columnar formats such as ORC and +Parquet, thereby improving write throughput. + +.. note:: + Currently supported table types: + - Append table + - Primary Key table + + Not supported in the current scope: + - Changelog + - Indexes + +Bucketing Modes +--------------- + +- Append tables: + + * Support ``bucket = -1`` (dynamic bucket mode) + * Support ``bucket > 0`` (fixed bucket mode) + +- PK tables: + + * Support ``bucket = -2`` (postpone bucket mode) + * Support ``bucket > 0`` (fixed bucket mode) + +.. note:: + PK tables do not support dynamic bucketing (``bucket = -1``). + +RecordBatch Construction +------------------------ + +- The compute engine must: + + - Apply the Paimon-consistent bucketing function to each row prior to batching. + - Assign the correct ``partition`` for each row. + - Group rows into Arrow ``RecordBatch`` per partition-bucket combination to minimize writer state changes and I/O overhead. + +- Recommended practices: + + - Use schema-aligned Arrow arrays with explicit validity bitmaps and offsets. + - Prefer batch sizes tuned for I/O throughput (e.g., tens to hundreds of MB per flush, depending on filesystem and cluster configuration). + - Maintain stable sort orders within a batch only if required by downstream merge or compaction logic; otherwise avoid unnecessary ordering costs. + +Prepare Commit +---------------- + +The compute engine is responsible for triggering the writer nodes' ``PrepareCommit``. +Triggering conditions depend on the engine’s business needs and can follow either: + +- Streaming mode: time-based or periodic triggers (e.g., every N seconds). +- Batch mode: trigger after all data in the batch has been written. + +Once the compute engine collects ``CommitMessages`` from all writer nodes, it +can issue a ``Commit`` request to the control plane (management path) to create +a new ``Snapshot``. + +Compatibility Goals +~~~~~~~~~~~~~~~~~~~ + +To ensure interoperability, the ``PrepareCommit`` result produced by Paimon C++ +must be consumable by Paimon Java. Therefore: + +- The structure and semantics of ``CommitMessage`` must remain consistent with + Java Paimon. +- Any evolution of the Java-side ``CommitMessage`` schema must be tracked and + validated on the C++ side to maintain cross-language compatibility. + +Interface Design in Paimon C++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unlike Java Paimon, Paimon C++ does not expose ``BinaryRow``-like types in its +public interfaces. To preserve compatibility without leaking internal row +representations, Paimon C++ provides ``CommitMessage`` only through: + +- Serialization: convert the internal commit state into a well-defined binary + representation that matches Java Paimon’s expectations. +- Deserialization: parse the Java-compatible binary representation back into + C++ commit structures for validation, replay, or tooling needs. + +This design ensures that: + +- Public APIs are independent of Java-specific row abstractions. +- Cross-language commit payloads remain stable and versionable. +- Internal data layouts can evolve without breaking external consumers. + +CommitMessage Contract +~~~~~~~~~~~~~~~~~~~~~~ + +The ``CommitMessage`` must encode all information required by the coordinator to +produce a correct ``Snapshot``, which commonly includes (but is not limited to): + +- Partition and bucket identifiers associated with written data. +- New data files, delete files (as applicable to the table type). +- File-level metadata required for manifest and index updates (e.g., row counts, min/max statistics where applicable). +- Transactional markers and sequence numbers as required by table semantics. +- Any per-writer state necessary for deduplication or idempotent commits. + +.. note:: + + Current C++ scope supports Append and PK tables. Changelog is out of + scope and should not be emitted in ``CommitMessage`` until + explicitly supported. + +Serialization and Deserialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Binary Format: + - The binary payload must strictly conform to Java Paimon’s ``CommitMessage`` encoding. + - Version tags or schema identifiers should be included to enable forwards/backwards compatibility and safe upgrades. + +- Serialization API: + - Provide a function to serialize the writer’s commit state into a byte buffer (or stream) consumable by Java Paimon. + +- Deserialization API: + - Provide a function to parse a Java-produced ``CommitMessage`` binary payload back into C++ commit structures for verification, replay, and testing. + +- Validation: + - Include conformance tests to assert that C++ serialized payloads are accepted by Java Paimon. + - Include round-trip tests to ensure C++ can parse Java-produced payloads and vice versa for supported message versions. + +Operational Flow +~~~~~~~~~~~~~~~~~~~~~~~ + +1. Writer nodes perform data ingestion and produce Arrow ``RecordBatch`` + organized by partition and bucket. + +2. Writers flush batches into ORC/Parquet files via registered ``file.format`` + and ``file-system`` backends, producing file-level metadata and per-batch + commit state. + +3. Each writer invokes ``PrepareCommit``, which: + - Aggregates per-writer state into a ``CommitMessage``. + - Serializes the message into a Java-compatible binary payload. + +4. The compute engine gathers ``CommitMessages`` from all writers. + +5. The compute engine issues a ``Commit`` request to the control plane with the + collected messages, resulting in a new ``Snapshot``. + +6. The coordinator validates the messages, updates manifests/metadata, and + finalizes the snapshot atomically. diff --git a/third_party/download_dependencies.sh b/third_party/download_dependencies.sh new file mode 100755 index 0000000..55a0103 --- /dev/null +++ b/third_party/download_dependencies.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Adapted from Apache Arrow +# https://github.com/apache/arrow/blob/main/cpp/thirdparty/download_dependencies.sh + +# This script downloads all the thirdparty dependencies as a series of tarballs +# that can be used for offline builds, etc. + +set -eu + +SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [ "$#" -ne 1 ]; then + orig_destdir=${SOURCE_DIR} +else + orig_destdir=$1 +fi + +# Try to canonicalize. Not all platforms support `readlink -f` or `realpath`. +# This only matters if there are symlinks you need to resolve before downloading +DESTDIR=$(readlink -f "${orig_destdir}" 2> /dev/null) || DESTDIR="${orig_destdir}" + +download_dependency() { + local url=$1 + local out=$2 + local expected_checksum=$3 + + # Determine which checksum command is available + local checksum_cmd="" + if command -v sha256sum >/dev/null 2>&1; then + checksum_cmd=(sha256sum) + checksum_field=1 + elif command -v shasum >/dev/null 2>&1; then + checksum_cmd=(shasum -a 256) + checksum_field=1 + elif command -v openssl >/dev/null 2>&1; then + checksum_cmd=(openssl dgst -sha256) + checksum_field=2 + else + echo "Error: No checksum command available (sha256sum, shasum, or openssl)" 1>&2 + exit 1 + fi + + # Function to calculate checksum + calculate_checksum() { + local file=$1 + "${checksum_cmd[@]}" "${file}" | cut -d' ' -f"${checksum_field}" + } + + # Check if the file already exists + if [ -f "${out}" ]; then + echo "File ${out} already exists, verifying checksum..." + # Calculate checksum of existing file + local actual_checksum + actual_checksum=$(calculate_checksum "${out}") + + # Compare checksums + if [ "${actual_checksum}" = "${expected_checksum}" ]; then + echo "Checksum matches, skipping download ${out}" + return 0 + else + echo "Checksum mismatch (expected: ${expected_checksum}, actual: ${actual_checksum}), re-downloading..." + rm -f "${out}" + fi + fi + + echo "Downloading ${url} to ${out}..." + wget --continue --output-document="${out}" "${url}" || \ + (echo "Failed downloading ${url}" 1>&2; exit 1) + + # Verify checksum after download + echo "Verifying checksum of downloaded file..." + local actual_checksum + actual_checksum=$(calculate_checksum "${out}") + if [ "${actual_checksum}" != "${expected_checksum}" ]; then + echo "Error: Checksum mismatch (expected: ${expected_checksum}, actual: ${actual_checksum})" 1>&2 + rm -f "${out}" + exit 1 + fi + echo "Checksum verification passed" +} + +main() { + mkdir -p "${DESTDIR}" + + # Load `DEPENDENCIES` variable. + source "${SOURCE_DIR}"/versions.txt + + echo "# Environment variables for offline Paimon build" + for ((i = 0; i < ${#DEPENDENCIES[@]}; i++)); do + local dep_packed=${DEPENDENCIES[$i]} + + # Unpack each entry of the form "$home_var $tar_out $dep_url" + IFS=" " read -r dep_url_var dep_tar_name dep_url <<< "${dep_packed}" + + # Get dependency name for finding checksum + local dep_name=${dep_url_var%_URL} + local checksum_var="${dep_name}_BUILD_SHA256_CHECKSUM" + local expected_checksum=${!checksum_var} + + local out=${DESTDIR}/${dep_tar_name} + download_dependency "${dep_url}" "${out}" "${expected_checksum}" + + echo "export ${dep_url_var}=${out}" + done +} + +main diff --git a/third_party/roaring_bitmap/CMakeLists.txt b/third_party/roaring_bitmap/CMakeLists.txt new file mode 100644 index 0000000..29158df --- /dev/null +++ b/third_party/roaring_bitmap/CMakeLists.txt @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +cmake_minimum_required(VERSION 3.10) + +add_compile_options(-Wno-missing-field-initializers) +add_library(roaring_bitmap STATIC roaring.cpp) +target_include_directories(roaring_bitmap PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/third_party/roaring_bitmap/roaring.cpp b/third_party/roaring_bitmap/roaring.cpp new file mode 100644 index 0000000..c7bdaa0 --- /dev/null +++ b/third_party/roaring_bitmap/roaring.cpp @@ -0,0 +1,20315 @@ +// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!! +// Created by amalgamation.sh on 2023-12-16T02:52:00Z + +/* + * The CRoaring project is under a dual license (Apache/MIT). + * Users of the library may choose one or the other license. + */ +/* + * Copyright 2016-2022 The CRoaring authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * MIT License + * + * Copyright 2016-2022 The CRoaring authors + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without + * limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +#include "roaring.h" + +/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */ +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include "roaring.h" /* include public API definitions */ +/* begin file include/roaring/isadetection.h */ +#ifndef ROARING_ISADETECTION_H +#define ROARING_ISADETECTION_H +#if defined(__x86_64__) || defined(_M_AMD64) // x64 + +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#ifdef __has_include +// We want to make sure that the AVX-512 functions are only built on compilers +// fully supporting AVX-512. +#if __has_include() +#define CROARING_COMPILER_SUPPORTS_AVX512 1 +#endif // #if __has_include() +#endif // #ifdef __has_include + +// Visual Studio 2019 and up support AVX-512 +#ifdef _MSC_VER +#if _MSC_VER >= 1920 +#define CROARING_COMPILER_SUPPORTS_AVX512 1 +#endif // #if _MSC_VER >= 1920 +#endif // #ifdef _MSC_VER + +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#define CROARING_COMPILER_SUPPORTS_AVX512 0 +#endif // #ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#endif // #ifndef CROARING_COMPILER_SUPPORTS_AVX512 + +namespace paimon::roaring { +namespace internal { +enum { + ROARING_SUPPORTS_AVX2 = 1, + ROARING_SUPPORTS_AVX512 = 2, +}; +int croaring_hardware_support(void); + +} // namespace internal +} // namespace paimon::roaring +#endif // x64 +#endif // ROARING_ISADETECTION_H +/* end file include/roaring/isadetection.h */ +/* begin file include/roaring/containers/perfparameters.h */ +#ifndef PERFPARAMETERS_H_ +#define PERFPARAMETERS_H_ + +#include + +namespace paimon::roaring { +namespace internal { +/** +During lazy computations, we can transform array containers into bitset +containers as +long as we can expect them to have ARRAY_LAZY_LOWERBOUND values. +*/ +enum { ARRAY_LAZY_LOWERBOUND = 1024 }; + +/* default initial size of a run container + setting it to zero delays the malloc.*/ +enum { RUN_DEFAULT_INIT_SIZE = 0 }; + +/* default initial size of an array container + setting it to zero delays the malloc */ +enum { ARRAY_DEFAULT_INIT_SIZE = 0 }; + +/* automatic bitset conversion during lazy or */ +#ifndef LAZY_OR_BITSET_CONVERSION +#define LAZY_OR_BITSET_CONVERSION true +#endif + +/* automatically attempt to convert a bitset to a full run during lazy + * evaluation */ +#ifndef LAZY_OR_BITSET_CONVERSION_TO_FULL +#define LAZY_OR_BITSET_CONVERSION_TO_FULL true +#endif + +/* automatically attempt to convert a bitset to a full run */ +#ifndef OR_BITSET_CONVERSION_TO_FULL +#define OR_BITSET_CONVERSION_TO_FULL true +#endif + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/containers/perfparameters.h */ +/* begin file include/roaring/containers/container_defs.h */ +/* + * container_defs.h + * + * Unlike containers.h (which is a file aggregating all the container includes, + * like array.h, bitset.h, and run.h) this is a file included BY those headers + * to do things like define the container base class `container_t`. + */ + +#ifndef INCLUDE_CONTAINERS_CONTAINER_DEFS_H_ +#define INCLUDE_CONTAINERS_CONTAINER_DEFS_H_ + +#ifdef __cplusplus +#include // used by casting helper for compile-time check +#endif + +// The preferences are a separate file to separate out tweakable parameters + +#ifdef __cplusplus +namespace paimon::roaring { +namespace internal { // No extern "C" (contains template) +#endif + +/* + * Since roaring_array_t's definition is not opaque, the container type is + * part of the API. If it's not going to be `void*` then it needs a name, and + * expectations are to prefix C library-exported names with `roaring_` etc. + * + * Rather than force the whole codebase to use the name `roaring_container_t`, + * the few API appearances use the macro ROARING_CONTAINER_T. Those includes + * are prior to containers.h, so make a short private alias of `container_t`. + * Then undefine the awkward macro so it's not used any more than it has to be. + */ +typedef ROARING_CONTAINER_T container_t; +#undef ROARING_CONTAINER_T + +/* + * See ROARING_CONTAINER_T for notes on using container_t as a base class. + * This macro helps make the following pattern look nicer: + * + * #ifdef __cplusplus + * struct roaring_array_s : public container_t { + * #else + * struct roaring_array_s { + * #endif + * int32_t cardinality; + * int32_t capacity; + * uint16_t *array; + * } + */ +#if defined(__cplusplus) +#define STRUCT_CONTAINER(name) struct name : public container_t /* { ... } */ +#else +#define STRUCT_CONTAINER(name) struct name /* { ... } */ +#endif + +/** + * Since container_t* is not void* in C++, "dangerous" casts are not needed to + * downcast; only a static_cast<> is needed. Define a macro for static casting + * which helps make casts more visible, and catches problems at compile-time + * when building the C sources in C++ mode: + * + * void some_func(container_t **c, ...) { // double pointer, not single + * array_container_t *ac1 = (array_container_t *)(c); // uncaught!! + * + * array_container_t *ac2 = CAST(array_container_t *, c) // C++ errors + * array_container_t *ac3 = CAST_array(c); // shorthand for #2, errors + * } + * + * Trickier to do is a cast from `container**` to `array_container_t**`. This + * needs a reinterpret_cast<>, which sacrifices safety...so a template is used + * leveraging to make sure it's legal in the C++ build. + */ +#ifdef __cplusplus +#define CAST(type, value) static_cast(value) +#define movable_CAST(type, value) movable_CAST_HELPER(value) + +template +PPDerived movable_CAST_HELPER(Base **ptr_to_ptr) { + typedef typename std::remove_pointer::type PDerived; + typedef typename std::remove_pointer::type Derived; + static_assert(std::is_base_of::value, + "use movable_CAST() for container_t** => xxx_container_t**"); + return reinterpret_cast(ptr_to_ptr); +} +#else +#define CAST(type, value) ((type)value) +#define movable_CAST(type, value) ((type)value) +#endif + +// Use for converting e.g. an `array_container_t**` to a `container_t**` +// +#define movable_CAST_base(c) movable_CAST(container_t **, c) + +#ifdef __cplusplus +} +} // namespace paimon::roaring { namespace internal { +#endif + +#endif /* INCLUDE_CONTAINERS_CONTAINER_DEFS_H_ */ +/* end file include/roaring/containers/container_defs.h */ +/* begin file include/roaring/array_util.h */ +#ifndef ARRAY_UTIL_H +#define ARRAY_UTIL_H + +#include // for size_t +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { +/* + * Good old binary search. + * Assumes that array is sorted, has logarithmic complexity. + * if the result is x, then: + * if ( x>0 ) you have array[x] = ikey + * if ( x<0 ) then inserting ikey at position -x-1 in array (insuring that array[-x-1]=ikey) + * keys the array sorted. + */ +inline int32_t binarySearch(const uint16_t *array, int32_t lenarray, uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t middleValue = array[middleIndex]; + if (middleValue < ikey) { + low = middleIndex + 1; + } else if (middleValue > ikey) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + +/** + * Galloping search + * Assumes that array is sorted, has logarithmic complexity. + * if the result is x, then if x = length, you have that all values in array between pos and length + * are smaller than min. + * otherwise returns the first index x such that array[x] >= min. + */ +static inline int32_t advanceUntil(const uint16_t *array, int32_t pos, int32_t length, + uint16_t min) { + int32_t lower = pos + 1; + + if ((lower >= length) || (array[lower] >= min)) { + return lower; + } + + int32_t spansize = 1; + + while ((lower + spansize < length) && (array[lower + spansize] < min)) { + spansize <<= 1; + } + int32_t upper = (lower + spansize < length) ? lower + spansize : length - 1; + + if (array[upper] == min) { + return upper; + } + if (array[upper] < min) { + // means + // array + // has no + // item + // >= min + // pos = array.length; + return length; + } + + // we know that the next-smallest span was too small + lower += (spansize >> 1); + + int32_t mid = 0; + while (lower + 1 != upper) { + mid = (lower + upper) >> 1; + if (array[mid] == min) { + return mid; + } else if (array[mid] < min) { + lower = mid; + } else { + upper = mid; + } + } + return upper; +} + +/** + * Returns number of elements which are less than ikey. + * Array elements must be unique and sorted. + */ +static inline int32_t count_less(const uint16_t *array, int32_t lenarray, uint16_t ikey) { + if (lenarray == 0) return 0; + int32_t pos = binarySearch(array, lenarray, ikey); + return pos >= 0 ? pos : -(pos + 1); +} + +/** + * Returns number of elements which are greater than ikey. + * Array elements must be unique and sorted. + */ +static inline int32_t count_greater(const uint16_t *array, int32_t lenarray, uint16_t ikey) { + if (lenarray == 0) return 0; + int32_t pos = binarySearch(array, lenarray, ikey); + if (pos >= 0) { + return lenarray - (pos + 1); + } else { + return lenarray - (-pos - 1); + } +} + +/** + * From Schlegel et al., Fast Sorted-Set Intersection using SIMD Instructions + * Optimized by D. Lemire on May 3rd 2013 + * + * C should have capacity greater than the minimum of s_1 and s_b + 8 + * where 8 is sizeof(__m128i)/sizeof(uint16_t). + */ +int32_t intersect_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, uint16_t *C); + +int32_t intersect_vector16_inplace(uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b); + +/** + * Take an array container and write it out to a 32-bit array, using base + * as the offset. + */ +int array_container_to_uint32_array_vector16(void *vout, const uint16_t *array, size_t cardinality, + uint32_t base); +#if CROARING_COMPILER_SUPPORTS_AVX512 +int avx512_array_container_to_uint32_array(void *vout, const uint16_t *array, size_t cardinality, + uint32_t base); +#endif +/** + * Compute the cardinality of the intersection using SSE4 instructions + */ +int32_t intersect_vector16_cardinality(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b); + +/* Computes the intersection between one small and one large set of uint16_t. + * Stores the result into buffer and return the number of elements. */ +int32_t intersect_skewed_uint16(const uint16_t *smallarray, size_t size_s, + const uint16_t *largearray, size_t size_l, uint16_t *buffer); + +/* Computes the size of the intersection between one small and one large set of + * uint16_t. */ +int32_t intersect_skewed_uint16_cardinality(const uint16_t *smallarray, size_t size_s, + const uint16_t *largearray, size_t size_l); + +/* Check whether the size of the intersection between one small and one large set of uint16_t is + * non-zero. */ +bool intersect_skewed_uint16_nonempty(const uint16_t *smallarray, size_t size_s, + const uint16_t *largearray, size_t size_l); +/** + * Generic intersection function. + */ +int32_t intersect_uint16(const uint16_t *A, const size_t lenA, const uint16_t *B, const size_t lenB, + uint16_t *out); +/** + * Compute the size of the intersection (generic). + */ +int32_t intersect_uint16_cardinality(const uint16_t *A, const size_t lenA, const uint16_t *B, + const size_t lenB); + +/** + * Checking whether the size of the intersection is non-zero. + */ +bool intersect_uint16_nonempty(const uint16_t *A, const size_t lenA, const uint16_t *B, + const size_t lenB); +/** + * Generic union function. + */ +size_t union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, size_t size_2, + uint16_t *buffer); + +/** + * Generic XOR function. + */ +int32_t xor_uint16(const uint16_t *array_1, int32_t card_1, const uint16_t *array_2, int32_t card_2, + uint16_t *out); + +/** + * Generic difference function (ANDNOT). + */ +int difference_uint16(const uint16_t *a1, int length1, const uint16_t *a2, int length2, + uint16_t *a_out); + +/** + * Generic intersection function. + */ +size_t intersection_uint32(const uint32_t *A, const size_t lenA, const uint32_t *B, + const size_t lenB, uint32_t *out); + +/** + * Generic intersection function, returns just the cardinality. + */ +size_t intersection_uint32_card(const uint32_t *A, const size_t lenA, const uint32_t *B, + const size_t lenB); + +/** + * Generic union function. + */ +size_t union_uint32(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, size_t size_2, + uint32_t *buffer); + +/** + * A fast SSE-based union function. + */ +uint32_t union_vector16(const uint16_t *__restrict__ set_1, uint32_t size_1, + const uint16_t *__restrict__ set_2, uint32_t size_2, + uint16_t *__restrict__ buffer); +/** + * A fast SSE-based XOR function. + */ +uint32_t xor_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output); + +/** + * A fast SSE-based difference function. + */ +int32_t difference_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, uint16_t *C); + +/** + * Generic union function, returns just the cardinality. + */ +size_t union_uint32_card(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, + size_t size_2); + +/** + * combines union_uint16 and union_vector16 optimally + */ +size_t fast_union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, size_t size_2, + uint16_t *buffer); + +bool memequals(const void *s1, const void *s2, size_t n); + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/array_util.h */ +/* begin file include/roaring/utilasm.h */ +/* + * utilasm.h + * + */ + +#ifndef INCLUDE_UTILASM_H_ +#define INCLUDE_UTILASM_H_ + +namespace paimon::roaring { +#if defined(CROARING_INLINE_ASM) +#define CROARING_ASMBITMANIPOPTIMIZATION // optimization flag + +#define ASM_SHIFT_RIGHT(srcReg, bitsReg, destReg) \ + __asm volatile("shrx %1, %2, %0" \ + : "=r"(destReg) \ + : /* write */ \ + "r"(bitsReg), /* read only */ \ + "r"(srcReg) /* read only */ \ + ) + +#define ASM_INPLACESHIFT_RIGHT(srcReg, bitsReg) \ + __asm volatile("shrx %1, %0, %0" \ + : "+r"(srcReg) \ + : /* read/write */ \ + "r"(bitsReg) /* read only */ \ + ) + +#define ASM_SHIFT_LEFT(srcReg, bitsReg, destReg) \ + __asm volatile("shlx %1, %2, %0" \ + : "=r"(destReg) \ + : /* write */ \ + "r"(bitsReg), /* read only */ \ + "r"(srcReg) /* read only */ \ + ) +// set bit at position testBit within testByte to 1 and +// copy cmovDst to cmovSrc if that bit was previously clear +#define ASM_SET_BIT_INC_WAS_CLEAR(testByte, testBit, count) \ + __asm volatile( \ + "bts %2, %0\n" \ + "sbb $-1, %1\n" \ + : "+r"(testByte), /* read/write */ \ + "+r"(count) \ + : /* read/write */ \ + "r"(testBit) /* read only */ \ + ) + +#define ASM_CLEAR_BIT_DEC_WAS_SET(testByte, testBit, count) \ + __asm volatile( \ + "btr %2, %0\n" \ + "sbb $0, %1\n" \ + : "+r"(testByte), /* read/write */ \ + "+r"(count) \ + : /* read/write */ \ + "r"(testBit) /* read only */ \ + ) + +#define ASM_BT64(testByte, testBit, count) \ + __asm volatile( \ + "bt %2,%1\n" \ + "sbb %0,%0" /*could use setb */ \ + : "=r"(count) \ + : /* write */ \ + "r"(testByte), /* read only */ \ + "r"(testBit) /* read only */ \ + ) + +#endif + +} // namespace paimon::roaring + +#endif /* INCLUDE_UTILASM_H_ */ +/* end file include/roaring/utilasm.h */ +/* begin file include/roaring/bitset_util.h */ +#ifndef BITSET_UTIL_H +#define BITSET_UTIL_H + +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { +/* + * Set all bits in indexes [begin,end) to true. + */ +static inline void bitset_set_range(uint64_t *words, uint32_t start, uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + if (firstword == endword) { + words[firstword] |= + ((~UINT64_C(0)) << (start % 64)) & ((~UINT64_C(0)) >> ((~end + 1) % 64)); + return; + } + words[firstword] |= (~UINT64_C(0)) << (start % 64); + for (uint32_t i = firstword + 1; i < endword; i++) { + words[i] = ~UINT64_C(0); + } + words[endword] |= (~UINT64_C(0)) >> ((~end + 1) % 64); +} + +/* + * Find the cardinality of the bitset in [begin,begin+lenminusone] + */ +static inline int bitset_lenrange_cardinality(const uint64_t *words, uint32_t start, + uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + return roaring_hamming(words[firstword] & ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) + << (start % 64)); + } + int answer = roaring_hamming(words[firstword] & ((~UINT64_C(0)) << (start % 64))); + for (uint32_t i = firstword + 1; i < endword; i++) { + answer += roaring_hamming(words[i]); + } + answer += + roaring_hamming(words[endword] & (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64)); + return answer; +} + +/* + * Check whether the cardinality of the bitset in [begin,begin+lenminusone] is 0 + */ +static inline bool bitset_lenrange_empty(const uint64_t *words, uint32_t start, + uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + return (words[firstword] & ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) << (start % 64)) == + 0; + } + if (((words[firstword] & ((~UINT64_C(0)) << (start % 64)))) != 0) { + return false; + } + for (uint32_t i = firstword + 1; i < endword; i++) { + if (words[i] != 0) { + return false; + } + } + if ((words[endword] & (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64)) != 0) { + return false; + } + return true; +} + +/* + * Set all bits in indexes [begin,begin+lenminusone] to true. + */ +static inline void bitset_set_lenrange(uint64_t *words, uint32_t start, uint32_t lenminusone) { + uint32_t firstword = start / 64; + uint32_t endword = (start + lenminusone) / 64; + if (firstword == endword) { + words[firstword] |= ((~UINT64_C(0)) >> ((63 - lenminusone) % 64)) << (start % 64); + return; + } + uint64_t temp = words[endword]; + words[firstword] |= (~UINT64_C(0)) << (start % 64); + for (uint32_t i = firstword + 1; i < endword; i += 2) words[i] = words[i + 1] = ~UINT64_C(0); + words[endword] = temp | (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64); +} + +/* + * Flip all the bits in indexes [begin,end). + */ +static inline void bitset_flip_range(uint64_t *words, uint32_t start, uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + words[firstword] ^= ~((~UINT64_C(0)) << (start % 64)); + for (uint32_t i = firstword; i < endword; i++) { + words[i] = ~words[i]; + } + words[endword] ^= ((~UINT64_C(0)) >> ((~end + 1) % 64)); +} + +/* + * Set all bits in indexes [begin,end) to false. + */ +static inline void bitset_reset_range(uint64_t *words, uint32_t start, uint32_t end) { + if (start == end) return; + uint32_t firstword = start / 64; + uint32_t endword = (end - 1) / 64; + if (firstword == endword) { + words[firstword] &= + ~(((~UINT64_C(0)) << (start % 64)) & ((~UINT64_C(0)) >> ((~end + 1) % 64))); + return; + } + words[firstword] &= ~((~UINT64_C(0)) << (start % 64)); + for (uint32_t i = firstword + 1; i < endword; i++) { + words[i] = UINT64_C(0); + } + words[endword] &= ~((~UINT64_C(0)) >> ((~end + 1) % 64)); +} + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base". + * + * The "out" pointer should be sufficient to store the actual number of bits + * set. + * + * Returns how many values were actually decoded. + * + * This function should only be expected to be faster than + * bitset_extract_setbits + * when the density of the bitset is high. + * + * This function uses AVX2 decoding. + */ +size_t bitset_extract_setbits_avx2(const uint64_t *words, size_t length, uint32_t *out, + size_t outcapacity, uint32_t base); + +size_t bitset_extract_setbits_avx512(const uint64_t *words, size_t length, uint32_t *out, + size_t outcapacity, uint32_t base); +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base". + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits(const uint64_t *words, size_t length, uint32_t *out, uint32_t base); + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out" as 16-bit integers, values start at "base" (can + *be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + * + * This function should only be expected to be faster than + *bitset_extract_setbits_uint16 + * when the density of the bitset is high. + * + * This function uses SSE decoding. + */ +size_t bitset_extract_setbits_sse_uint16(const uint64_t *words, size_t length, uint16_t *out, + size_t outcapacity, uint16_t base); + +size_t bitset_extract_setbits_avx512_uint16(const uint64_t *words, size_t length, uint16_t *out, + size_t outcapacity, uint16_t base); + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base" + * (can be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits_uint16(const uint64_t *words, size_t length, uint16_t *out, + uint16_t base); + +/* + * Given two bitsets containing "length" 64-bit words, write out the position + * of all the common set bits to "out", values start at "base" + * (can be set to zero) + * + * The "out" pointer should be sufficient to store the actual number of bits + * set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_intersection_setbits_uint16(const uint64_t *__restrict__ words1, + const uint64_t *__restrict__ words2, + size_t length, uint16_t *out, uint16_t base); + +/* + * Given a bitset having cardinality card, set all bit values in the list (there + * are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ +uint64_t bitset_set_list_withcard(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length); +/* + * Given a bitset, set all bit values in the list (there + * are length of them). + */ +void bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length); + +/* + * Given a bitset having cardinality card, unset all bit values in the list + * (there are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ +uint64_t bitset_clear_list(uint64_t *words, uint64_t card, const uint16_t *list, uint64_t length); + +/* + * Given a bitset having cardinality card, toggle all bit values in the list + * (there are length of them) + * and return the updated cardinality. This evidently assumes that the bitset + * already contained data. + */ + +uint64_t bitset_flip_list_withcard(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length); + +void bitset_flip_list(uint64_t *words, const uint16_t *list, uint64_t length); + +#if CROARING_IS_X64 +/*** + * BEGIN Harley-Seal popcount functions. + */ +CROARING_TARGET_AVX2 +/** + * Compute the population count of a 256-bit word + * This is not especially fast, but it is convenient as part of other functions. + */ +static inline __m256i popcount256(__m256i v) { + const __m256i lookuppos = _mm256_setr_epi8( + /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2, + /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3, + /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3, + /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4, + + /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2, + /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3, + /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3, + /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4); + const __m256i lookupneg = _mm256_setr_epi8( + /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2, + /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3, + /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3, + /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4, + + /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2, + /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3, + /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3, + /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4); + const __m256i low_mask = _mm256_set1_epi8(0x0f); + + const __m256i lo = _mm256_and_si256(v, low_mask); + const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), low_mask); + const __m256i popcnt1 = _mm256_shuffle_epi8(lookuppos, lo); + const __m256i popcnt2 = _mm256_shuffle_epi8(lookupneg, hi); + return _mm256_sad_epu8(popcnt1, popcnt2); +} +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +/** + * Simple CSA over 256 bits + */ +static inline void CSA(__m256i *h, __m256i *l, __m256i a, __m256i b, __m256i c) { + const __m256i u = _mm256_xor_si256(a, b); + *h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c)); + *l = _mm256_xor_si256(u, c); +} +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +/** + * Fast Harley-Seal AVX population count function + */ +inline static uint64_t avx2_harley_seal_popcount256(const __m256i *data, const uint64_t size) { + __m256i total = _mm256_setzero_si256(); + __m256i ones = _mm256_setzero_si256(); + __m256i twos = _mm256_setzero_si256(); + __m256i fours = _mm256_setzero_si256(); + __m256i eights = _mm256_setzero_si256(); + __m256i sixteens = _mm256_setzero_si256(); + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; + + const uint64_t limit = size - size % 16; + uint64_t i = 0; + + for (; i < limit; i += 16) { + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i), _mm256_lddqu_si256(data + i + 1)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 2), + _mm256_lddqu_si256(data + i + 3)); + CSA(&foursA, &twos, twos, twosA, twosB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 4), + _mm256_lddqu_si256(data + i + 5)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 6), + _mm256_lddqu_si256(data + i + 7)); + CSA(&foursB, &twos, twos, twosA, twosB); + CSA(&eightsA, &fours, fours, foursA, foursB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 8), + _mm256_lddqu_si256(data + i + 9)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 10), + _mm256_lddqu_si256(data + i + 11)); + CSA(&foursA, &twos, twos, twosA, twosB); + CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 12), + _mm256_lddqu_si256(data + i + 13)); + CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 14), + _mm256_lddqu_si256(data + i + 15)); + CSA(&foursB, &twos, twos, twosA, twosB); + CSA(&eightsB, &fours, fours, foursA, foursB); + CSA(&sixteens, &eights, eights, eightsA, eightsB); + + total = _mm256_add_epi64(total, popcount256(sixteens)); + } + + total = _mm256_slli_epi64(total, 4); // * 16 + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(eights), 3)); // += 8 * ... + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); // += 4 * ... + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1)); // += 2 * ... + total = _mm256_add_epi64(total, popcount256(ones)); + for (; i < size; i++) + total = _mm256_add_epi64(total, popcount256(_mm256_lddqu_si256(data + i))); + + return (uint64_t)(_mm256_extract_epi64(total, 0)) + (uint64_t)(_mm256_extract_epi64(total, 1)) + + (uint64_t)(_mm256_extract_epi64(total, 2)) + (uint64_t)(_mm256_extract_epi64(total, 3)); +} +CROARING_UNTARGET_AVX2 + +#define AVXPOPCNTFNC(opname, avx_intrinsic) \ + static inline uint64_t avx2_harley_seal_popcount256_##opname( \ + const __m256i *data1, const __m256i *data2, const uint64_t size) { \ + __m256i total = _mm256_setzero_si256(); \ + __m256i ones = _mm256_setzero_si256(); \ + __m256i twos = _mm256_setzero_si256(); \ + __m256i fours = _mm256_setzero_si256(); \ + __m256i eights = _mm256_setzero_si256(); \ + __m256i sixteens = _mm256_setzero_si256(); \ + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; \ + __m256i A1, A2; \ + const uint64_t limit = size - size % 16; \ + uint64_t i = 0; \ + for (; i < limit; i += 16) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), _mm256_lddqu_si256(data2 + i)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1), \ + _mm256_lddqu_si256(data2 + i + 1)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2), \ + _mm256_lddqu_si256(data2 + i + 2)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3), \ + _mm256_lddqu_si256(data2 + i + 3)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4), \ + _mm256_lddqu_si256(data2 + i + 4)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5), \ + _mm256_lddqu_si256(data2 + i + 5)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6), \ + _mm256_lddqu_si256(data2 + i + 6)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7), \ + _mm256_lddqu_si256(data2 + i + 7)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsA, &fours, fours, foursA, foursB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8), \ + _mm256_lddqu_si256(data2 + i + 8)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9), \ + _mm256_lddqu_si256(data2 + i + 9)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10), \ + _mm256_lddqu_si256(data2 + i + 10)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11), \ + _mm256_lddqu_si256(data2 + i + 11)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12), \ + _mm256_lddqu_si256(data2 + i + 12)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13), \ + _mm256_lddqu_si256(data2 + i + 13)); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14), \ + _mm256_lddqu_si256(data2 + i + 14)); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15), \ + _mm256_lddqu_si256(data2 + i + 15)); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsB, &fours, fours, foursA, foursB); \ + CSA(&sixteens, &eights, eights, eightsA, eightsB); \ + total = _mm256_add_epi64(total, popcount256(sixteens)); \ + } \ + total = _mm256_slli_epi64(total, 4); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(eights), 3)); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1)); \ + total = _mm256_add_epi64(total, popcount256(ones)); \ + for (; i < size; i++) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), _mm256_lddqu_si256(data2 + i)); \ + total = _mm256_add_epi64(total, popcount256(A1)); \ + } \ + return (uint64_t)(_mm256_extract_epi64(total, 0)) + \ + (uint64_t)(_mm256_extract_epi64(total, 1)) + \ + (uint64_t)(_mm256_extract_epi64(total, 2)) + \ + (uint64_t)(_mm256_extract_epi64(total, 3)); \ + } \ + static inline uint64_t avx2_harley_seal_popcount256andstore_##opname( \ + const __m256i *__restrict__ data1, const __m256i *__restrict__ data2, \ + __m256i *__restrict__ out, const uint64_t size) { \ + __m256i total = _mm256_setzero_si256(); \ + __m256i ones = _mm256_setzero_si256(); \ + __m256i twos = _mm256_setzero_si256(); \ + __m256i fours = _mm256_setzero_si256(); \ + __m256i eights = _mm256_setzero_si256(); \ + __m256i sixteens = _mm256_setzero_si256(); \ + __m256i twosA, twosB, foursA, foursB, eightsA, eightsB; \ + __m256i A1, A2; \ + const uint64_t limit = size - size % 16; \ + uint64_t i = 0; \ + for (; i < limit; i += 16) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), _mm256_lddqu_si256(data2 + i)); \ + _mm256_storeu_si256(out + i, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1), \ + _mm256_lddqu_si256(data2 + i + 1)); \ + _mm256_storeu_si256(out + i + 1, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2), \ + _mm256_lddqu_si256(data2 + i + 2)); \ + _mm256_storeu_si256(out + i + 2, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3), \ + _mm256_lddqu_si256(data2 + i + 3)); \ + _mm256_storeu_si256(out + i + 3, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4), \ + _mm256_lddqu_si256(data2 + i + 4)); \ + _mm256_storeu_si256(out + i + 4, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5), \ + _mm256_lddqu_si256(data2 + i + 5)); \ + _mm256_storeu_si256(out + i + 5, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6), \ + _mm256_lddqu_si256(data2 + i + 6)); \ + _mm256_storeu_si256(out + i + 6, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7), \ + _mm256_lddqu_si256(data2 + i + 7)); \ + _mm256_storeu_si256(out + i + 7, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsA, &fours, fours, foursA, foursB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8), \ + _mm256_lddqu_si256(data2 + i + 8)); \ + _mm256_storeu_si256(out + i + 8, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9), \ + _mm256_lddqu_si256(data2 + i + 9)); \ + _mm256_storeu_si256(out + i + 9, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10), \ + _mm256_lddqu_si256(data2 + i + 10)); \ + _mm256_storeu_si256(out + i + 10, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11), \ + _mm256_lddqu_si256(data2 + i + 11)); \ + _mm256_storeu_si256(out + i + 11, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursA, &twos, twos, twosA, twosB); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12), \ + _mm256_lddqu_si256(data2 + i + 12)); \ + _mm256_storeu_si256(out + i + 12, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13), \ + _mm256_lddqu_si256(data2 + i + 13)); \ + _mm256_storeu_si256(out + i + 13, A2); \ + CSA(&twosA, &ones, ones, A1, A2); \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14), \ + _mm256_lddqu_si256(data2 + i + 14)); \ + _mm256_storeu_si256(out + i + 14, A1); \ + A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15), \ + _mm256_lddqu_si256(data2 + i + 15)); \ + _mm256_storeu_si256(out + i + 15, A2); \ + CSA(&twosB, &ones, ones, A1, A2); \ + CSA(&foursB, &twos, twos, twosA, twosB); \ + CSA(&eightsB, &fours, fours, foursA, foursB); \ + CSA(&sixteens, &eights, eights, eightsA, eightsB); \ + total = _mm256_add_epi64(total, popcount256(sixteens)); \ + } \ + total = _mm256_slli_epi64(total, 4); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(eights), 3)); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \ + total = _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1)); \ + total = _mm256_add_epi64(total, popcount256(ones)); \ + for (; i < size; i++) { \ + A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i), _mm256_lddqu_si256(data2 + i)); \ + _mm256_storeu_si256(out + i, A1); \ + total = _mm256_add_epi64(total, popcount256(A1)); \ + } \ + return (uint64_t)(_mm256_extract_epi64(total, 0)) + \ + (uint64_t)(_mm256_extract_epi64(total, 1)) + \ + (uint64_t)(_mm256_extract_epi64(total, 2)) + \ + (uint64_t)(_mm256_extract_epi64(total, 3)); \ + } + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(or, _mm256_or_si256) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(union, _mm256_or_si256) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(and, _mm256_and_si256) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(intersection, _mm256_and_si256) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(xor, _mm256_xor_si256) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVXPOPCNTFNC(andnot, _mm256_andnot_si256) +CROARING_UNTARGET_AVX2 + +#define VPOPCNT_AND_ADD(ptr, i, accu) \ + const __m512i v##i = _mm512_loadu_si512((const __m512i *)ptr + i); \ + const __m512i p##i = _mm512_popcnt_epi64(v##i); \ + accu = _mm512_add_epi64(accu, p##i); + +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +static inline uint64_t sum_epu64_256(const __m256i v) { + return (uint64_t)(_mm256_extract_epi64(v, 0)) + (uint64_t)(_mm256_extract_epi64(v, 1)) + + (uint64_t)(_mm256_extract_epi64(v, 2)) + (uint64_t)(_mm256_extract_epi64(v, 3)); +} + +static inline uint64_t simd_sum_epu64(const __m512i v) { + __m256i lo = _mm512_extracti64x4_epi64(v, 0); + __m256i hi = _mm512_extracti64x4_epi64(v, 1); + + return sum_epu64_256(lo) + sum_epu64_256(hi); +} + +static inline uint64_t avx512_vpopcount(const __m512i *data, const uint64_t size) { + const uint64_t limit = size - size % 4; + __m512i total = _mm512_setzero_si512(); + uint64_t i = 0; + + for (; i < limit; i += 4) { + VPOPCNT_AND_ADD(data + i, 0, total); + VPOPCNT_AND_ADD(data + i, 1, total); + VPOPCNT_AND_ADD(data + i, 2, total); + VPOPCNT_AND_ADD(data + i, 3, total); + } + + for (; i < size; i++) { + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(_mm512_loadu_si512(data + i))); + } + + return simd_sum_epu64(total); +} +CROARING_UNTARGET_AVX512 +#endif + +#define AVXPOPCNTFNC512(opname, avx_intrinsic) \ + static inline uint64_t avx512_harley_seal_popcount512_##opname( \ + const __m512i *data1, const __m512i *data2, const uint64_t size) { \ + __m512i total = _mm512_setzero_si512(); \ + const uint64_t limit = size - size % 4; \ + uint64_t i = 0; \ + for (; i < limit; i += 4) { \ + __m512i a1 = \ + avx_intrinsic(_mm512_loadu_si512(data1 + i), _mm512_loadu_si512(data2 + i)); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a1)); \ + __m512i a2 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 1), \ + _mm512_loadu_si512(data2 + i + 1)); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a2)); \ + __m512i a3 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 2), \ + _mm512_loadu_si512(data2 + i + 2)); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a3)); \ + __m512i a4 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 3), \ + _mm512_loadu_si512(data2 + i + 3)); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a4)); \ + } \ + for (; i < size; i++) { \ + __m512i a = \ + avx_intrinsic(_mm512_loadu_si512(data1 + i), _mm512_loadu_si512(data2 + i)); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a)); \ + } \ + return simd_sum_epu64(total); \ + } \ + static inline uint64_t avx512_harley_seal_popcount512andstore_##opname( \ + const __m512i *__restrict__ data1, const __m512i *__restrict__ data2, \ + __m512i *__restrict__ out, const uint64_t size) { \ + __m512i total = _mm512_setzero_si512(); \ + const uint64_t limit = size - size % 4; \ + uint64_t i = 0; \ + for (; i < limit; i += 4) { \ + __m512i a1 = \ + avx_intrinsic(_mm512_loadu_si512(data1 + i), _mm512_loadu_si512(data2 + i)); \ + _mm512_storeu_si512(out + i, a1); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a1)); \ + __m512i a2 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 1), \ + _mm512_loadu_si512(data2 + i + 1)); \ + _mm512_storeu_si512(out + i + 1, a2); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a2)); \ + __m512i a3 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 2), \ + _mm512_loadu_si512(data2 + i + 2)); \ + _mm512_storeu_si512(out + i + 2, a3); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a3)); \ + __m512i a4 = avx_intrinsic(_mm512_loadu_si512(data1 + i + 3), \ + _mm512_loadu_si512(data2 + i + 3)); \ + _mm512_storeu_si512(out + i + 3, a4); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a4)); \ + } \ + for (; i < size; i++) { \ + __m512i a = \ + avx_intrinsic(_mm512_loadu_si512(data1 + i), _mm512_loadu_si512(data2 + i)); \ + _mm512_storeu_si512(out + i, a); \ + total = _mm512_add_epi64(total, _mm512_popcnt_epi64(a)); \ + } \ + return simd_sum_epu64(total); \ + } + +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +AVXPOPCNTFNC512(or, _mm512_or_si512) +AVXPOPCNTFNC512(union, _mm512_or_si512) +AVXPOPCNTFNC512(and, _mm512_and_si512) +AVXPOPCNTFNC512(intersection, _mm512_and_si512) +AVXPOPCNTFNC512(xor, _mm512_xor_si512) +AVXPOPCNTFNC512(andnot, _mm512_andnot_si512) +CROARING_UNTARGET_AVX512 +#endif +/*** + * END Harley-Seal popcount functions. + */ + +#endif // CROARING_IS_X64 + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/bitset_util.h */ +/* begin file include/roaring/containers/array.h */ +/* + * array.h + * + */ + +#ifndef INCLUDE_CONTAINERS_ARRAY_H_ +#define INCLUDE_CONTAINERS_ARRAY_H_ + +#include + +namespace paimon::roaring { + +// Note: in pure C++ code, you should avoid putting `using` in header files +using internal::roaring_iterator; +using internal::roaring_iterator64; + +namespace internal { +//#endif + +/* Containers with DEFAULT_MAX_SIZE or less integers should be arrays */ +enum { DEFAULT_MAX_SIZE = 4096 }; + +/* struct array_container - sparse representation of a bitmap + * + * @cardinality: number of indices in `array` (and the bitmap) + * @capacity: allocated size of `array` + * @array: sorted list of integers + */ +STRUCT_CONTAINER(array_container_s) { + int32_t cardinality; + int32_t capacity; + uint16_t *array; +}; + +typedef struct array_container_s array_container_t; + +#define CAST_array(c) CAST(array_container_t *, c) // safer downcast +#define const_CAST_array(c) CAST(const array_container_t *, c) +#define movable_CAST_array(c) movable_CAST(array_container_t **, c) + +/* Create a new array with default. Return NULL in case of failure. See also + * array_container_create_given_capacity. */ +array_container_t *array_container_create(void); + +/* Create a new array with a specified capacity size. Return NULL in case of + * failure. */ +array_container_t *array_container_create_given_capacity(int32_t size); + +/* Create a new array containing all values in [min,max). */ +array_container_t *array_container_create_range(uint32_t min, uint32_t max); + +/* + * Shrink the capacity to the actual size, return the number of bytes saved. + */ +int array_container_shrink_to_fit(array_container_t *src); + +/* Free memory owned by `array'. */ +void array_container_free(array_container_t *array); + +/* Duplicate container */ +array_container_t *array_container_clone(const array_container_t *src); + +/* Get the cardinality of `array'. */ +ALLOW_UNALIGNED +static inline int array_container_cardinality(const array_container_t *array) { + return array->cardinality; +} + +static inline bool array_container_nonzero_cardinality(const array_container_t *array) { + return array->cardinality > 0; +} + +/* Copy one container into another. We assume that they are distinct. */ +void array_container_copy(const array_container_t *src, array_container_t *dst); + +/* Add all the values in [min,max) (included) at a distance k*step from min. + The container must have a size less or equal to DEFAULT_MAX_SIZE after this + addition. */ +void array_container_add_from_range(array_container_t *arr, uint32_t min, uint32_t max, + uint16_t step); + +static inline bool array_container_empty(const array_container_t *array) { + return array->cardinality == 0; +} + +/* check whether the cardinality is equal to the capacity (this does not mean + * that it contains 1<<16 elements) */ +static inline bool array_container_full(const array_container_t *array) { + return array->cardinality == array->capacity; +} + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void array_container_union(const array_container_t *src_1, const array_container_t *src_2, + array_container_t *dst); + +/* symmetric difference, see array_container_union */ +void array_container_xor(const array_container_t *array_1, const array_container_t *array_2, + array_container_t *out); + +/* Computes the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void array_container_intersection(const array_container_t *src_1, const array_container_t *src_2, + array_container_t *dst); + +/* Check whether src_1 and src_2 intersect. */ +bool array_container_intersect(const array_container_t *src_1, const array_container_t *src_2); + +/* computers the size of the intersection between two arrays. + */ +int array_container_intersection_cardinality(const array_container_t *src_1, + const array_container_t *src_2); + +/* computes the intersection of array1 and array2 and write the result to + * array1. + * */ +void array_container_intersection_inplace(array_container_t *src_1, const array_container_t *src_2); + +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + */ +int array_container_to_uint32_array(void *vout, const array_container_t *cont, uint32_t base); + +/* Compute the number of runs */ +int32_t array_container_number_of_runs(const array_container_t *ac); + +/* + * Print this container using printf (useful for debugging). + */ +void array_container_printf(const array_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void array_container_printf_as_uint32_array(const array_container_t *v, uint32_t base); + +bool array_container_validate(const array_container_t *v, const char **reason); + +/** + * Return the serialized size in bytes of a container having cardinality "card". + */ +static inline int32_t array_container_serialized_size_in_bytes(int32_t card) { + return card * 2 + 2; +} + +/** + * Increase capacity to at least min. + * Whether the existing data needs to be copied over depends on the "preserve" + * parameter. If preserve is false, then the new content will be uninitialized, + * otherwise the old content is copied. + */ +void array_container_grow(array_container_t *container, int32_t min, bool preserve); + +bool array_container_iterate(const array_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr); +bool array_container_iterate64(const array_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * array_container_size_in_bytes(container). + * + */ +int32_t array_container_write(const array_container_t *container, char *buf); +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be array_container_size_in_bytes(container). + * You need to provide the (known) cardinality. + */ +int32_t array_container_read(int32_t cardinality, array_container_t *container, const char *buf); + +/** + * Return the serialized size in bytes of a container (see + * bitset_container_write) + * This is meant to be compatible with the Java and Go versions of Roaring and + * assumes + * that the cardinality of the container is already known. + * + */ +static inline int32_t array_container_size_in_bytes(const array_container_t *container) { + return container->cardinality * sizeof(uint16_t); +} + +/** + * Return true if the two arrays have the same content. + */ +ALLOW_UNALIGNED +static inline bool array_container_equals(const array_container_t *container1, + const array_container_t *container2) { + if (container1->cardinality != container2->cardinality) { + return false; + } + return memequals(container1->array, container2->array, container1->cardinality * 2); +} + +/** + * Return true if container1 is a subset of container2. + */ +bool array_container_is_subset(const array_container_t *container1, + const array_container_t *container2); + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +static inline bool array_container_select(const array_container_t *container, uint32_t *start_rank, + uint32_t rank, uint32_t *element) { + int card = array_container_cardinality(container); + if (*start_rank + card <= rank) { + *start_rank += card; + return false; + } else { + *element = container->array[rank - *start_rank]; + return true; + } +} + +/* Computes the difference of array1 and array2 and write the result + * to array out. + * Array out does not need to be distinct from array_1 + */ +void array_container_andnot(const array_container_t *array_1, const array_container_t *array_2, + array_container_t *out); + +/* Append x to the set. Assumes that the value is larger than any preceding + * values. */ +static inline void array_container_append(array_container_t *arr, uint16_t pos) { + const int32_t capacity = arr->capacity; + + if (array_container_full(arr)) { + array_container_grow(arr, capacity + 1, true); + } + + arr->array[arr->cardinality++] = pos; +} + +/** + * Add value to the set if final cardinality doesn't exceed max_cardinality. + * Return code: + * 1 -- value was added + * 0 -- value was already present + * -1 -- value was not added because cardinality would exceed max_cardinality + */ +static inline int array_container_try_add(array_container_t *arr, uint16_t value, + int32_t max_cardinality) { + const int32_t cardinality = arr->cardinality; + + // best case, we can append. + if ((array_container_empty(arr) || arr->array[cardinality - 1] < value) && + cardinality < max_cardinality) { + array_container_append(arr, value); + return 1; + } + + const int32_t loc = binarySearch(arr->array, cardinality, value); + + if (loc >= 0) { + return 0; + } else if (cardinality < max_cardinality) { + if (array_container_full(arr)) { + array_container_grow(arr, arr->capacity + 1, true); + } + const int32_t insert_idx = -loc - 1; + memmove(arr->array + insert_idx + 1, arr->array + insert_idx, + (cardinality - insert_idx) * sizeof(uint16_t)); + arr->array[insert_idx] = value; + arr->cardinality++; + return 1; + } else { + return -1; + } +} + +/* Add value to the set. Returns true if x was not already present. */ +static inline bool array_container_add(array_container_t *arr, uint16_t value) { + return array_container_try_add(arr, value, INT32_MAX) == 1; +} + +/* Remove x from the set. Returns true if x was present. */ +static inline bool array_container_remove(array_container_t *arr, uint16_t pos) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, pos); + const bool is_present = idx >= 0; + if (is_present) { + memmove(arr->array + idx, arr->array + idx + 1, + (arr->cardinality - idx - 1) * sizeof(uint16_t)); + arr->cardinality--; + } + + return is_present; +} + +/* Check whether x is present. */ +inline bool array_container_contains(const array_container_t *arr, uint16_t pos) { + // return binarySearch(arr->array, arr->cardinality, pos) >= 0; + // binary search with fallback to linear search for short ranges + int32_t low = 0; + const uint16_t *carr = (const uint16_t *)arr->array; + int32_t high = arr->cardinality - 1; + // while (high - low >= 0) { + while (high >= low + 16) { + int32_t middleIndex = (low + high) >> 1; + uint16_t middleValue = carr[middleIndex]; + if (middleValue < pos) { + low = middleIndex + 1; + } else if (middleValue > pos) { + high = middleIndex - 1; + } else { + return true; + } + } + + for (int i = low; i <= high; i++) { + uint16_t v = carr[i]; + if (v == pos) { + return true; + } + if (v > pos) return false; + } + return false; +} + +void array_container_offset(const array_container_t *c, container_t **loc, container_t **hic, + uint16_t offset); + +//* Check whether a range of values from range_start (included) to range_end (excluded) is present. +//*/ +static inline bool array_container_contains_range(const array_container_t *arr, + uint32_t range_start, uint32_t range_end) { + const int32_t range_count = range_end - range_start; + const uint16_t rs_included = (uint16_t)range_start; + const uint16_t re_included = (uint16_t)(range_end - 1); + + // Empty range is always included + if (range_count <= 0) { + return true; + } + if (range_count > arr->cardinality) { + return false; + } + + const int32_t start = binarySearch(arr->array, arr->cardinality, rs_included); + // If this sorted array contains all items in the range: + // * the start item must be found + // * the last item in range range_count must exist, and be the expected end value + return (start >= 0) && (arr->cardinality >= start + range_count) && + (arr->array[start + range_count - 1] == re_included); +} + +/* Returns the smallest value (assumes not empty) */ +inline uint16_t array_container_minimum(const array_container_t *arr) { + if (arr->cardinality == 0) return 0; + return arr->array[0]; +} + +/* Returns the largest value (assumes not empty) */ +inline uint16_t array_container_maximum(const array_container_t *arr) { + if (arr->cardinality == 0) return 0; + return arr->array[arr->cardinality - 1]; +} + +/* Returns the number of values equal or smaller than x */ +inline int array_container_rank(const array_container_t *arr, uint16_t x) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, x); + const bool is_present = idx >= 0; + if (is_present) { + return idx + 1; + } else { + return -idx - 1; + } +} + +/* bulk version of array_container_rank(); return number of consumed elements */ +inline uint32_t array_container_rank_many(const array_container_t *arr, uint64_t start_rank, + const uint32_t *begin, const uint32_t *end, + uint64_t *ans) { + const uint16_t high = (uint16_t)((*begin) >> 16); + uint32_t pos = 0; + const uint32_t *iter = begin; + for (; iter != end; iter++) { + uint32_t x = *iter; + uint16_t xhigh = (uint16_t)(x >> 16); + if (xhigh != high) return iter - begin; // stop at next container + + const int32_t idx = binarySearch(arr->array + pos, arr->cardinality - pos, (uint16_t)x); + const bool is_present = idx >= 0; + if (is_present) { + *(ans++) = start_rank + pos + (idx + 1); + pos = idx + 1; + } else { + *(ans++) = start_rank + pos + (-idx - 1); + } + } + return iter - begin; +} + +/* Returns the index of x , if not exsist return -1 */ +inline int array_container_get_index(const array_container_t *arr, uint16_t x) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, x); + const bool is_present = idx >= 0; + if (is_present) { + return idx; + } else { + return -1; + } +} + +/* Returns the index of the first value equal or larger than x, or -1 */ +inline int array_container_index_equalorlarger(const array_container_t *arr, uint16_t x) { + const int32_t idx = binarySearch(arr->array, arr->cardinality, x); + const bool is_present = idx >= 0; + if (is_present) { + return idx; + } else { + int32_t candidate = -idx - 1; + if (candidate < arr->cardinality) return candidate; + return -1; + } +} + +/* + * Adds all values in range [min,max] using hint: + * nvals_less is the number of array values less than $min + * nvals_greater is the number of array values greater than $max + */ +static inline void array_container_add_range_nvals(array_container_t *array, uint32_t min, + uint32_t max, int32_t nvals_less, + int32_t nvals_greater) { + int32_t union_cardinality = nvals_less + (max - min + 1) + nvals_greater; + if (union_cardinality > array->capacity) { + array_container_grow(array, union_cardinality, true); + } + memmove(&(array->array[union_cardinality - nvals_greater]), + &(array->array[array->cardinality - nvals_greater]), nvals_greater * sizeof(uint16_t)); + for (uint32_t i = 0; i <= max - min; i++) { + array->array[nvals_less + i] = (uint16_t)(min + i); + } + array->cardinality = union_cardinality; +} + +/** + * Adds all values in range [min,max]. This function is currently unused + * and left as a documentation. + */ +/*static inline void array_container_add_range(array_container_t *array, + uint32_t min, uint32_t max) { + int32_t nvals_greater = count_greater(array->array, array->cardinality, max); + int32_t nvals_less = count_less(array->array, array->cardinality - nvals_greater, min); + array_container_add_range_nvals(array, min, max, nvals_less, nvals_greater); +}*/ + +/* + * Removes all elements array[pos] .. array[pos+count-1] + */ +static inline void array_container_remove_range(array_container_t *array, uint32_t pos, + uint32_t count) { + if (count != 0) { + memmove(&(array->array[pos]), &(array->array[pos + count]), + (array->cardinality - pos - count) * sizeof(uint16_t)); + array->cardinality -= count; + } +} + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_ARRAY_H_ */ +/* end file include/roaring/containers/array.h */ +/* begin file include/roaring/containers/bitset.h */ +/* + * bitset.h + * + */ + +#ifndef INCLUDE_CONTAINERS_BITSET_H_ +#define INCLUDE_CONTAINERS_BITSET_H_ + +#include +#include + +namespace paimon::roaring { + +// Note: in pure C++ code, you should avoid putting `using` in header files +using internal::roaring_iterator; +using internal::roaring_iterator64; + +namespace internal { +//#endif + +enum { BITSET_CONTAINER_SIZE_IN_WORDS = (1 << 16) / 64, BITSET_UNKNOWN_CARDINALITY = -1 }; + +STRUCT_CONTAINER(bitset_container_s) { + int32_t cardinality; + uint64_t *words; +}; + +typedef struct bitset_container_s bitset_container_t; + +#define CAST_bitset(c) CAST(bitset_container_t *, c) // safer downcast +#define const_CAST_bitset(c) CAST(const bitset_container_t *, c) +#define movable_CAST_bitset(c) movable_CAST(bitset_container_t **, c) + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_container_t *bitset_container_create(void); + +/* Free memory. */ +void bitset_container_free(bitset_container_t *bitset); + +/* Clear bitset (sets bits to 0). */ +void bitset_container_clear(bitset_container_t *bitset); + +/* Set all bits to 1. */ +void bitset_container_set_all(bitset_container_t *bitset); + +/* Duplicate bitset */ +bitset_container_t *bitset_container_clone(const bitset_container_t *src); + +/* Set the bit in [begin,end). WARNING: as of April 2016, this method is slow + * and + * should not be used in performance-sensitive code. Ever. */ +void bitset_container_set_range(bitset_container_t *bitset, uint32_t begin, uint32_t end); + +#if defined(CROARING_ASMBITMANIPOPTIMIZATION) && defined(__AVX2__) +/* Set the ith bit. */ +static inline void bitset_container_set(bitset_container_t *bitset, uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->words[offset]; + ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality); + bitset->words[offset] = load; +} + +/* Unset the ith bit. Currently unused. Could be used for optimization. */ +/*static inline void bitset_container_unset(bitset_container_t *bitset, + uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->words[offset]; + ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality); + bitset->words[offset] = load; +}*/ + +/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower + * than bitset_container_set. */ +static inline bool bitset_container_add(bitset_container_t *bitset, uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->words[offset]; + // could be possibly slightly further optimized + const int32_t oldcard = bitset->cardinality; + ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality); + bitset->words[offset] = load; + return bitset->cardinality - oldcard; +} + +/* Remove `pos' from `bitset'. Returns true if `pos' was present. Might be + * slower than bitset_container_unset. */ +static inline bool bitset_container_remove(bitset_container_t *bitset, uint16_t pos) { + uint64_t shift = 6; + uint64_t offset; + uint64_t p = pos; + ASM_SHIFT_RIGHT(p, shift, offset); + uint64_t load = bitset->words[offset]; + // could be possibly slightly further optimized + const int32_t oldcard = bitset->cardinality; + ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality); + bitset->words[offset] = load; + return oldcard - bitset->cardinality; +} + +/* Get the value of the ith bit. */ +inline bool bitset_container_get(const bitset_container_t *bitset, uint16_t pos) { + uint64_t word = bitset->words[pos >> 6]; + const uint64_t p = pos; + ASM_INPLACESHIFT_RIGHT(word, p); + return word & 1; +} + +#else + +/* Set the ith bit. */ +static inline void bitset_container_set(bitset_container_t *bitset, uint16_t pos) { + const uint64_t old_word = bitset->words[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word | (UINT64_C(1) << index); + bitset->cardinality += (uint32_t)((old_word ^ new_word) >> index); + bitset->words[pos >> 6] = new_word; +} + +/* Unset the ith bit. Currently unused. */ +/*static inline void bitset_container_unset(bitset_container_t *bitset, + uint16_t pos) { + const uint64_t old_word = bitset->words[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word & (~(UINT64_C(1) << index)); + bitset->cardinality -= (uint32_t)((old_word ^ new_word) >> index); + bitset->words[pos >> 6] = new_word; +}*/ + +/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower + * than bitset_container_set. */ +static inline bool bitset_container_add(bitset_container_t *bitset, uint16_t pos) { + const uint64_t old_word = bitset->words[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word | (UINT64_C(1) << index); + const uint64_t increment = (old_word ^ new_word) >> index; + bitset->cardinality += (uint32_t)increment; + bitset->words[pos >> 6] = new_word; + return increment > 0; +} + +/* Remove `pos' from `bitset'. Returns true if `pos' was present. Might be + * slower than bitset_container_unset. */ +static inline bool bitset_container_remove(bitset_container_t *bitset, uint16_t pos) { + const uint64_t old_word = bitset->words[pos >> 6]; + const int index = pos & 63; + const uint64_t new_word = old_word & (~(UINT64_C(1) << index)); + const uint64_t increment = (old_word ^ new_word) >> index; + bitset->cardinality -= (uint32_t)increment; + bitset->words[pos >> 6] = new_word; + return increment > 0; +} + +/* Get the value of the ith bit. */ +inline bool bitset_container_get(const bitset_container_t *bitset, uint16_t pos) { + const uint64_t word = bitset->words[pos >> 6]; + return (word >> (pos & 63)) & 1; +} + +#endif + +/* + * Check if all bits are set in a range of positions from pos_start (included) to + * pos_end (excluded). + */ +static inline bool bitset_container_get_range(const bitset_container_t *bitset, uint32_t pos_start, + uint32_t pos_end) { + const uint32_t start = pos_start >> 6; + const uint32_t end = pos_end >> 6; + + const uint64_t first = ~((1ULL << (pos_start & 0x3F)) - 1); + const uint64_t last = (1ULL << (pos_end & 0x3F)) - 1; + + if (start == end) return ((bitset->words[end] & first & last) == (first & last)); + if ((bitset->words[start] & first) != first) return false; + + if ((end < BITSET_CONTAINER_SIZE_IN_WORDS) && ((bitset->words[end] & last) != last)) { + return false; + } + + for (uint32_t i = start + 1; (i < BITSET_CONTAINER_SIZE_IN_WORDS) && (i < end); ++i) { + if (bitset->words[i] != UINT64_C(0xFFFFFFFFFFFFFFFF)) return false; + } + + return true; +} + +/* Check whether `bitset' is present in `array'. Calls bitset_container_get. */ +inline bool bitset_container_contains(const bitset_container_t *bitset, uint16_t pos) { + return bitset_container_get(bitset, pos); +} + +/* + * Check whether a range of bits from position `pos_start' (included) to `pos_end' (excluded) + * is present in `bitset'. Calls bitset_container_get_all. + */ +static inline bool bitset_container_contains_range(const bitset_container_t *bitset, + uint32_t pos_start, uint32_t pos_end) { + return bitset_container_get_range(bitset, pos_start, pos_end); +} + +/* Get the number of bits set */ +ALLOW_UNALIGNED +static inline int bitset_container_cardinality(const bitset_container_t *bitset) { + return bitset->cardinality; +} + +/* Copy one container into another. We assume that they are distinct. */ +void bitset_container_copy(const bitset_container_t *source, bitset_container_t *dest); + +/* Add all the values [min,max) at a distance k*step from min: min, + * min+step,.... */ +void bitset_container_add_from_range(bitset_container_t *bitset, uint32_t min, uint32_t max, + uint16_t step); + +/* Get the number of bits set (force computation). This does not modify bitset. + * To update the cardinality, you should do + * bitset->cardinality = bitset_container_compute_cardinality(bitset).*/ +int bitset_container_compute_cardinality(const bitset_container_t *bitset); + +/* Check whether this bitset is empty, + * it never modifies the bitset struct. */ +static inline bool bitset_container_empty(const bitset_container_t *bitset) { + if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) { + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) { + if ((bitset->words[i]) != 0) return false; + } + return true; + } + return bitset->cardinality == 0; +} + +/* Get whether there is at least one bit set (see bitset_container_empty for the reverse), + the bitset is never modified */ +static inline bool bitset_container_const_nonzero_cardinality(const bitset_container_t *bitset) { + return !bitset_container_empty(bitset); +} + +/* + * Check whether the two bitsets intersect + */ +bool bitset_container_intersect(const bitset_container_t *src_1, const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. */ +int bitset_container_or(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' and return the cardinality. + */ +int bitset_container_or_justcard(const bitset_container_t *src_1, const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. Same as bitset_container_or. */ +int bitset_container_union(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' and return the + * cardinality. Same as bitset_container_or_justcard. */ +int bitset_container_union_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_union_nocard(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the union of bitsets `src_1' and `src_2' into `dst', but does not + * update the cardinality. Provided to optimize chained operations. */ +int bitset_container_or_nocard(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. */ +int bitset_container_and(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_and_justcard(const bitset_container_t *src_1, const bitset_container_t *src_2); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. Same as bitset_container_and. */ +int bitset_container_intersection(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' and return the + * cardinality. Same as bitset_container_and_justcard. */ +int bitset_container_intersection_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_intersection_nocard(const bitset_container_t *src_1, + const bitset_container_t *src_2, bitset_container_t *dst); + +/* Computes the intersection of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_and_nocard(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst' and + * return the cardinality. */ +int bitset_container_xor(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_xor_justcard(const bitset_container_t *src_1, const bitset_container_t *src_2); + +/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_xor_nocard(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the and not of bitsets `src_1' and `src_2' into `dst' and return the + * cardinality. */ +int bitset_container_andnot(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Computes the and not of bitsets `src_1' and `src_2' and return the + * cardinality. */ +int bitset_container_andnot_justcard(const bitset_container_t *src_1, + const bitset_container_t *src_2); + +/* Computes the and not or of bitsets `src_1' and `src_2' into `dst', but does + * not update the cardinality. Provided to optimize chained operations. */ +int bitset_container_andnot_nocard(const bitset_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +void bitset_container_offset(const bitset_container_t *c, container_t **loc, container_t **hic, + uint16_t offset); +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + * The out pointer should point to enough memory (the cardinality times 32 + * bits). + */ +int bitset_container_to_uint32_array(uint32_t *out, const bitset_container_t *bc, uint32_t base); + +/* + * Print this container using printf (useful for debugging). + */ +void bitset_container_printf(const bitset_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void bitset_container_printf_as_uint32_array(const bitset_container_t *v, uint32_t base); + +bool bitset_container_validate(const bitset_container_t *v, const char **reason); + +/** + * Return the serialized size in bytes of a container. + */ +static inline int32_t bitset_container_serialized_size_in_bytes(void) { + return BITSET_CONTAINER_SIZE_IN_WORDS * 8; +} + +/** + * Return the the number of runs. + */ +int bitset_container_number_of_runs(bitset_container_t *bc); + +bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr); +bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * bitset_container_size_in_bytes(container). + */ +int32_t bitset_container_write(const bitset_container_t *container, char *buf); + +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be bitset_container_size_in_bytes(container). + * You need to provide the (known) cardinality. + */ +int32_t bitset_container_read(int32_t cardinality, bitset_container_t *container, const char *buf); +/** + * Return the serialized size in bytes of a container (see + * bitset_container_write). + * This is meant to be compatible with the Java and Go versions of Roaring and + * assumes + * that the cardinality of the container is already known or can be computed. + */ +static inline int32_t bitset_container_size_in_bytes(const bitset_container_t *container) { + (void)container; + return BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); +} + +/** + * Return true if the two containers have the same content. + */ +bool bitset_container_equals(const bitset_container_t *container1, + const bitset_container_t *container2); + +/** + * Return true if container1 is a subset of container2. + */ +bool bitset_container_is_subset(const bitset_container_t *container1, + const bitset_container_t *container2); + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +bool bitset_container_select(const bitset_container_t *container, uint32_t *start_rank, + uint32_t rank, uint32_t *element); + +/* Returns the smallest value (assumes not empty) */ +uint16_t bitset_container_minimum(const bitset_container_t *container); + +/* Returns the largest value (assumes not empty) */ +uint16_t bitset_container_maximum(const bitset_container_t *container); + +/* Returns the number of values equal or smaller than x */ +int bitset_container_rank(const bitset_container_t *container, uint16_t x); + +/* bulk version of bitset_container_rank(); return number of consumed elements */ +uint32_t bitset_container_rank_many(const bitset_container_t *container, uint64_t start_rank, + const uint32_t *begin, const uint32_t *end, uint64_t *ans); + +/* Returns the index of x , if not exsist return -1 */ +int bitset_container_get_index(const bitset_container_t *container, uint16_t x); + +/* Returns the index of the first value equal or larger than x, or -1 */ +int bitset_container_index_equalorlarger(const bitset_container_t *container, uint16_t x); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_BITSET_H_ */ +/* end file include/roaring/containers/bitset.h */ +/* begin file include/roaring/containers/run.h */ +/* + * run.h + * + */ + +#ifndef INCLUDE_CONTAINERS_RUN_H_ +#define INCLUDE_CONTAINERS_RUN_H_ + +#include +#include +#include +#include + +namespace paimon::roaring { + +// Note: in pure C++ code, you should avoid putting `using` in header files +using internal::roaring_iterator; +using internal::roaring_iterator64; + +namespace internal { +//#endif + +/* struct rle16_s - run length pair + * + * @value: start position of the run + * @length: length of the run is `length + 1` + * + * An RLE pair {v, l} would represent the integers between the interval + * [v, v+l+1], e.g. {3, 2} = [3, 4, 5]. + */ +struct rle16_s { + uint16_t value; + uint16_t length; +}; + +typedef struct rle16_s rle16_t; + +#ifdef __cplusplus +#define MAKE_RLE16(val, len) \ + { (uint16_t)(val), (uint16_t)(len) } // no tagged structs until c++20 +#else +#define MAKE_RLE16(val, len) \ + (rle16_t) { \ + .value = (uint16_t)(val), .length = (uint16_t)(len) \ + } +#endif + +/* struct run_container_s - run container bitmap + * + * @n_runs: number of rle_t pairs in `runs`. + * @capacity: capacity in rle_t pairs `runs` can hold. + * @runs: pairs of rle_t. + */ +STRUCT_CONTAINER(run_container_s) { + int32_t n_runs; + int32_t capacity; + rle16_t *runs; +}; + +typedef struct run_container_s run_container_t; + +#define CAST_run(c) CAST(run_container_t *, c) // safer downcast +#define const_CAST_run(c) CAST(const run_container_t *, c) +#define movable_CAST_run(c) movable_CAST(run_container_t **, c) + +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create(void); + +/* Create a new run container with given capacity. Return NULL in case of + * failure. */ +run_container_t *run_container_create_given_capacity(int32_t size); + +/* + * Shrink the capacity to the actual size, return the number of bytes saved. + */ +int run_container_shrink_to_fit(run_container_t *src); + +/* Free memory owned by `run'. */ +void run_container_free(run_container_t *run); + +/* Duplicate container */ +run_container_t *run_container_clone(const run_container_t *src); + +/* + * Effectively deletes the value at index index, repacking data. + */ +static inline void recoverRoomAtIndex(run_container_t *run, uint16_t index) { + memmove(run->runs + index, run->runs + (1 + index), + (run->n_runs - index - 1) * sizeof(rle16_t)); + run->n_runs--; +} + +/** + * Good old binary search through rle data + */ +inline int32_t interleavedBinarySearch(const rle16_t *array, int32_t lenarray, uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t middleValue = array[middleIndex].value; + if (middleValue < ikey) { + low = middleIndex + 1; + } else if (middleValue > ikey) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + +/* + * Returns index of the run which contains $ikey + */ +static inline int32_t rle16_find_run(const rle16_t *array, int32_t lenarray, uint16_t ikey) { + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min = array[middleIndex].value; + uint16_t max = array[middleIndex].value + array[middleIndex].length; + if (ikey > max) { + low = middleIndex + 1; + } else if (ikey < min) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return -(low + 1); +} + +/** + * Returns number of runs which can'be be merged with the key because they + * are less than the key. + * Note that [5,6,7,8] can be merged with the key 9 and won't be counted. + */ +static inline int32_t rle16_count_less(const rle16_t *array, int32_t lenarray, uint16_t key) { + if (lenarray == 0) return 0; + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min_value = array[middleIndex].value; + uint16_t max_value = array[middleIndex].value + array[middleIndex].length; + if (max_value + UINT32_C(1) < key) { // uint32 arithmetic + low = middleIndex + 1; + } else if (key < min_value) { + high = middleIndex - 1; + } else { + return middleIndex; + } + } + return low; +} + +static inline int32_t rle16_count_greater(const rle16_t *array, int32_t lenarray, uint16_t key) { + if (lenarray == 0) return 0; + int32_t low = 0; + int32_t high = lenarray - 1; + while (low <= high) { + int32_t middleIndex = (low + high) >> 1; + uint16_t min_value = array[middleIndex].value; + uint16_t max_value = array[middleIndex].value + array[middleIndex].length; + if (max_value < key) { + low = middleIndex + 1; + } else if (key + UINT32_C(1) < min_value) { // uint32 arithmetic + high = middleIndex - 1; + } else { + return lenarray - (middleIndex + 1); + } + } + return lenarray - low; +} + +/** + * increase capacity to at least min. Whether the + * existing data needs to be copied over depends on copy. If "copy" is false, + * then the new content will be uninitialized, otherwise a copy is made. + */ +void run_container_grow(run_container_t *run, int32_t min, bool copy); + +/** + * Moves the data so that we can write data at index + */ +static inline void makeRoomAtIndex(run_container_t *run, uint16_t index) { + /* This function calls realloc + memmove sequentially to move by one index. + * Potentially copying twice the array. + */ + if (run->n_runs + 1 > run->capacity) run_container_grow(run, run->n_runs + 1, true); + memmove(run->runs + 1 + index, run->runs + index, (run->n_runs - index) * sizeof(rle16_t)); + run->n_runs++; +} + +/* Add `pos' to `run'. Returns true if `pos' was not present. */ +bool run_container_add(run_container_t *run, uint16_t pos); + +/* Remove `pos' from `run'. Returns true if `pos' was present. */ +static inline bool run_container_remove(run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) { + int32_t le = run->runs[index].length; + if (le == 0) { + recoverRoomAtIndex(run, (uint16_t)index); + } else { + run->runs[index].value++; + run->runs[index].length--; + } + return true; + } + index = -index - 2; // points to preceding value, possibly -1 + if (index >= 0) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset < le) { + // need to break in two + run->runs[index].length = (uint16_t)(offset - 1); + // need to insert + uint16_t newvalue = pos + 1; + int32_t newlength = le - offset - 1; + makeRoomAtIndex(run, (uint16_t)(index + 1)); + run->runs[index + 1].value = newvalue; + run->runs[index + 1].length = (uint16_t)newlength; + return true; + + } else if (offset == le) { + run->runs[index].length--; + return true; + } + } + // no match + return false; +} + +/* Check whether `pos' is present in `run'. */ +inline bool run_container_contains(const run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) return true; + index = -index - 2; // points to preceding value, possibly -1 + if (index != -1) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset <= le) return true; + } + return false; +} + +/* + * Check whether all positions in a range of positions from pos_start (included) + * to pos_end (excluded) is present in `run'. + */ +static inline bool run_container_contains_range(const run_container_t *run, uint32_t pos_start, + uint32_t pos_end) { + uint32_t count = 0; + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, (uint16_t)pos_start); + if (index < 0) { + index = -index - 2; + if ((index == -1) || ((pos_start - run->runs[index].value) > run->runs[index].length)) { + return false; + } + } + for (int32_t i = index; i < run->n_runs; ++i) { + const uint32_t stop = run->runs[i].value + run->runs[i].length; + if (run->runs[i].value >= pos_end) break; + if (stop >= pos_end) { + count += (((pos_end - run->runs[i].value) > 0) ? (pos_end - run->runs[i].value) : 0); + break; + } + const uint32_t min = (stop - pos_start) > 0 ? (stop - pos_start) : 0; + count += (min < run->runs[i].length) ? min : run->runs[i].length; + } + return count >= (pos_end - pos_start - 1); +} + +/* Get the cardinality of `run'. Requires an actual computation. */ +int run_container_cardinality(const run_container_t *run); + +/* Card > 0?, see run_container_empty for the reverse */ +static inline bool run_container_nonzero_cardinality(const run_container_t *run) { + return run->n_runs > 0; // runs never empty +} + +/* Card == 0?, see run_container_nonzero_cardinality for the reverse */ +static inline bool run_container_empty(const run_container_t *run) { + return run->n_runs == 0; // runs never empty +} + +/* Copy one container into another. We assume that they are distinct. */ +void run_container_copy(const run_container_t *src, run_container_t *dst); + +/** + * Append run described by vl to the run container, possibly merging. + * It is assumed that the run would be inserted at the end of the container, no + * check is made. + * It is assumed that the run container has the necessary capacity: caller is + * responsible for checking memory capacity. + * + * + * This is not a safe function, it is meant for performance: use with care. + */ +static inline void run_container_append(run_container_t *run, rle16_t vl, rle16_t *previousrl) { + const uint32_t previousend = previousrl->value + previousrl->length; + if (vl.value > previousend + 1) { // we add a new one + run->runs[run->n_runs] = vl; + run->n_runs++; + *previousrl = vl; + } else { + uint32_t newend = vl.value + vl.length + UINT32_C(1); + if (newend > previousend) { // we merge + previousrl->length = (uint16_t)(newend - 1 - previousrl->value); + run->runs[run->n_runs - 1] = *previousrl; + } + } +} + +/** + * Like run_container_append but it is assumed that the content of run is empty. + */ +static inline rle16_t run_container_append_first(run_container_t *run, rle16_t vl) { + run->runs[run->n_runs] = vl; + run->n_runs++; + return vl; +} + +/** + * append a single value given by val to the run container, possibly merging. + * It is assumed that the value would be inserted at the end of the container, + * no check is made. + * It is assumed that the run container has the necessary capacity: caller is + * responsible for checking memory capacity. + * + * This is not a safe function, it is meant for performance: use with care. + */ +static inline void run_container_append_value(run_container_t *run, uint16_t val, + rle16_t *previousrl) { + const uint32_t previousend = previousrl->value + previousrl->length; + if (val > previousend + 1) { // we add a new one + *previousrl = MAKE_RLE16(val, 0); + run->runs[run->n_runs] = *previousrl; + run->n_runs++; + } else if (val == previousend + 1) { // we merge + previousrl->length++; + run->runs[run->n_runs - 1] = *previousrl; + } +} + +/** + * Like run_container_append_value but it is assumed that the content of run is + * empty. + */ +static inline rle16_t run_container_append_value_first(run_container_t *run, uint16_t val) { + rle16_t newrle = MAKE_RLE16(val, 0); + run->runs[run->n_runs] = newrle; + run->n_runs++; + return newrle; +} + +/* Check whether the container spans the whole chunk (cardinality = 1<<16). + * This check can be done in constant time (inexpensive). */ +static inline bool run_container_is_full(const run_container_t *run) { + rle16_t vl = run->runs[0]; + return (run->n_runs == 1) && (vl.value == 0) && (vl.length == 0xFFFF); +} + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_union(const run_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +/* Compute the union of `src_1' and `src_2' and write the result to `src_1' */ +void run_container_union_inplace(run_container_t *src_1, const run_container_t *src_2); + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_intersection(const run_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +/* Compute the size of the intersection of src_1 and src_2 . */ +int run_container_intersection_cardinality(const run_container_t *src_1, + const run_container_t *src_2); + +/* Check whether src_1 and src_2 intersect. */ +bool run_container_intersect(const run_container_t *src_1, const run_container_t *src_2); + +/* Compute the symmetric difference of `src_1' and `src_2' and write the result + * to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_xor(const run_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +/* + * Write out the 16-bit integers contained in this container as a list of 32-bit + * integers using base + * as the starting value (it might be expected that base has zeros in its 16 + * least significant bits). + * The function returns the number of values written. + * The caller is responsible for allocating enough memory in out. + */ +int run_container_to_uint32_array(void *vout, const run_container_t *cont, uint32_t base); + +/* + * Print this container using printf (useful for debugging). + */ +void run_container_printf(const run_container_t *v); + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void run_container_printf_as_uint32_array(const run_container_t *v, uint32_t base); + +bool run_container_validate(const run_container_t *run, const char **reason); + +/** + * Return the serialized size in bytes of a container having "num_runs" runs. + */ +static inline int32_t run_container_serialized_size_in_bytes(int32_t num_runs) { + return sizeof(uint16_t) + sizeof(rle16_t) * num_runs; // each run requires 2 2-byte entries. +} + +bool run_container_iterate(const run_container_t *cont, uint32_t base, roaring_iterator iterator, + void *ptr); +bool run_container_iterate64(const run_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, void *ptr); + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be run_container_size_in_bytes(container). + */ +int32_t run_container_write(const run_container_t *container, char *buf); + +/** + * Reads the instance from buf, outputs how many bytes were read. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes read should be bitset_container_size_in_bytes(container). + * The cardinality parameter is provided for consistency with other containers, + * but + * it might be effectively ignored.. + */ +int32_t run_container_read(int32_t cardinality, run_container_t *container, const char *buf); + +/** + * Return the serialized size in bytes of a container (see run_container_write). + * This is meant to be compatible with the Java and Go versions of Roaring. + */ +static inline int32_t run_container_size_in_bytes(const run_container_t *container) { + return run_container_serialized_size_in_bytes(container->n_runs); +} + +/** + * Return true if the two containers have the same content. + */ +ALLOW_UNALIGNED +static inline bool run_container_equals(const run_container_t *container1, + const run_container_t *container2) { + if (container1->n_runs != container2->n_runs) { + return false; + } + return memequals(container1->runs, container2->runs, container1->n_runs * sizeof(rle16_t)); +} + +/** + * Return true if container1 is a subset of container2. + */ +bool run_container_is_subset(const run_container_t *container1, const run_container_t *container2); + +/** + * Used in a start-finish scan that appends segments, for XOR and NOT + */ + +void run_container_smart_append_exclusive(run_container_t *src, const uint16_t start, + const uint16_t length); + +/** + * The new container consists of a single run [start,stop). + * It is required that stop>start, the caller is responsability for this check. + * It is required that stop <= (1<<16), the caller is responsability for this check. + * The cardinality of the created container is stop - start. + * Returns NULL on failure + */ +static inline run_container_t *run_container_create_range(uint32_t start, uint32_t stop) { + run_container_t *rc = run_container_create_given_capacity(1); + if (rc) { + rle16_t r; + r.value = (uint16_t)start; + r.length = (uint16_t)(stop - start - 1); + run_container_append_first(rc, r); + } + return rc; +} + +/** + * If the element of given rank is in this container, supposing that the first + * element has rank start_rank, then the function returns true and sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +bool run_container_select(const run_container_t *container, uint32_t *start_rank, uint32_t rank, + uint32_t *element); + +/* Compute the difference of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ + +void run_container_andnot(const run_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +void run_container_offset(const run_container_t *c, container_t **loc, container_t **hic, + uint16_t offset); + +/* Returns the smallest value (assumes not empty) */ +inline uint16_t run_container_minimum(const run_container_t *run) { + if (run->n_runs == 0) return 0; + return run->runs[0].value; +} + +/* Returns the largest value (assumes not empty) */ +inline uint16_t run_container_maximum(const run_container_t *run) { + if (run->n_runs == 0) return 0; + return run->runs[run->n_runs - 1].value + run->runs[run->n_runs - 1].length; +} + +/* Returns the number of values equal or smaller than x */ +int run_container_rank(const run_container_t *arr, uint16_t x); + +/* bulk version of run_container_rank(); return number of consumed elements */ +uint32_t run_container_rank_many(const run_container_t *arr, uint64_t start_rank, + const uint32_t *begin, const uint32_t *end, uint64_t *ans); + +/* Returns the index of x, if not exsist return -1 */ +int run_container_get_index(const run_container_t *arr, uint16_t x); + +/* Returns the index of the first run containing a value at least as large as x, or -1 */ +inline int run_container_index_equalorlarger(const run_container_t *arr, uint16_t x) { + int32_t index = interleavedBinarySearch(arr->runs, arr->n_runs, x); + if (index >= 0) return index; + index = -index - 2; // points to preceding run, possibly -1 + if (index != -1) { // possible match + int32_t offset = x - arr->runs[index].value; + int32_t le = arr->runs[index].length; + if (offset <= le) return index; + } + index += 1; + if (index < arr->n_runs) { + return index; + } + return -1; +} + +/* + * Add all values in range [min, max] using hint. + */ +static inline void run_container_add_range_nruns(run_container_t *run, uint32_t min, uint32_t max, + int32_t nruns_less, int32_t nruns_greater) { + int32_t nruns_common = run->n_runs - nruns_less - nruns_greater; + if (nruns_common == 0) { + makeRoomAtIndex(run, (uint16_t)nruns_less); + run->runs[nruns_less].value = (uint16_t)min; + run->runs[nruns_less].length = (uint16_t)(max - min); + } else { + uint32_t common_min = run->runs[nruns_less].value; + uint32_t common_max = run->runs[nruns_less + nruns_common - 1].value + + run->runs[nruns_less + nruns_common - 1].length; + uint32_t result_min = (common_min < min) ? common_min : min; + uint32_t result_max = (common_max > max) ? common_max : max; + + run->runs[nruns_less].value = (uint16_t)result_min; + run->runs[nruns_less].length = (uint16_t)(result_max - result_min); + + memmove(&(run->runs[nruns_less + 1]), &(run->runs[run->n_runs - nruns_greater]), + nruns_greater * sizeof(rle16_t)); + run->n_runs = nruns_less + 1 + nruns_greater; + } +} + +/** + * Add all values in range [min, max]. This function is currently unused + * and left as documentation. + */ +/*static inline void run_container_add_range(run_container_t* run, + uint32_t min, uint32_t max) { + int32_t nruns_greater = rle16_count_greater(run->runs, run->n_runs, max); + int32_t nruns_less = rle16_count_less(run->runs, run->n_runs - nruns_greater, min); + run_container_add_range_nruns(run, min, max, nruns_less, nruns_greater); +}*/ + +/** + * Shifts last $count elements either left (distance < 0) or right (distance > 0) + */ +static inline void run_container_shift_tail(run_container_t *run, int32_t count, int32_t distance) { + if (distance > 0) { + if (run->capacity < count + distance) { + run_container_grow(run, count + distance, true); + } + } + int32_t srcpos = run->n_runs - count; + int32_t dstpos = srcpos + distance; + memmove(&(run->runs[dstpos]), &(run->runs[srcpos]), sizeof(rle16_t) * count); + run->n_runs += distance; +} + +/** + * Remove all elements in range [min, max] + */ +static inline void run_container_remove_range(run_container_t *run, uint32_t min, uint32_t max) { + int32_t first = rle16_find_run(run->runs, run->n_runs, (uint16_t)min); + int32_t last = rle16_find_run(run->runs, run->n_runs, (uint16_t)max); + + if (first >= 0 && min > run->runs[first].value && + max < ((uint32_t)run->runs[first].value + (uint32_t)run->runs[first].length)) { + // split this run into two adjacent runs + + // right subinterval + makeRoomAtIndex(run, (uint16_t)(first + 1)); + run->runs[first + 1].value = (uint16_t)(max + 1); + run->runs[first + 1].length = + (uint16_t)((run->runs[first].value + run->runs[first].length) - (max + 1)); + + // left subinterval + run->runs[first].length = (uint16_t)((min - 1) - run->runs[first].value); + + return; + } + + // update left-most partial run + if (first >= 0) { + if (min > run->runs[first].value) { + run->runs[first].length = (uint16_t)((min - 1) - run->runs[first].value); + first++; + } + } else { + first = -first - 1; + } + + // update right-most run + if (last >= 0) { + uint16_t run_max = run->runs[last].value + run->runs[last].length; + if (run_max > max) { + run->runs[last].value = (uint16_t)(max + 1); + run->runs[last].length = (uint16_t)(run_max - (max + 1)); + last--; + } + } else { + last = (-last - 1) - 1; + } + + // remove intermediate runs + if (first <= last) { + run_container_shift_tail(run, run->n_runs - (last + 1), -(last - first + 1)); + } +} + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_RUN_H_ */ +/* end file include/roaring/containers/run.h */ +/* begin file include/roaring/containers/convert.h */ +/* + * convert.h + * + */ + +#ifndef INCLUDE_CONTAINERS_CONVERT_H_ +#define INCLUDE_CONTAINERS_CONVERT_H_ + +namespace paimon::roaring { +namespace internal { +/* Convert an array into a bitset. The input container is not freed or modified. + */ +bitset_container_t *bitset_container_from_array(const array_container_t *arr); + +/* Convert a run into a bitset. The input container is not freed or modified. */ +bitset_container_t *bitset_container_from_run(const run_container_t *arr); + +/* Convert a run into an array. The input container is not freed or modified. */ +array_container_t *array_container_from_run(const run_container_t *arr); + +/* Convert a bitset into an array. The input container is not freed or modified. + */ +array_container_t *array_container_from_bitset(const bitset_container_t *bits); + +/* Convert an array into a run. The input container is not freed or modified. + */ +run_container_t *run_container_from_array(const array_container_t *c); + +/* convert a run into either an array or a bitset + * might free the container. This does not free the input run container. */ +container_t *convert_to_bitset_or_array_container(run_container_t *rc, int32_t card, + uint8_t *resulttype); + +/* convert containers to and from runcontainers, as is most space efficient. + * The container might be freed. */ +container_t *convert_run_optimize(container_t *c, uint8_t typecode_original, + uint8_t *typecode_after); + +/* converts a run container to either an array or a bitset, IF it saves space. + */ +/* If a conversion occurs, the caller is responsible to free the original + * container and + * he becomes reponsible to free the new one. */ +container_t *convert_run_to_efficient_container(run_container_t *c, uint8_t *typecode_after); + +// like convert_run_to_efficient_container but frees the old result if needed +container_t *convert_run_to_efficient_container_and_free(run_container_t *c, + uint8_t *typecode_after); + +/** + * Create new container which is a union of run container and + * range [min, max]. Caller is responsible for freeing run container. + */ +container_t *container_from_run_range(const run_container_t *run, uint32_t min, uint32_t max, + uint8_t *typecode_after); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_CONVERT_H_ */ +/* end file include/roaring/containers/convert.h */ +/* begin file include/roaring/containers/mixed_equal.h */ +/* + * mixed_equal.h + * + */ + +#ifndef CONTAINERS_MIXED_EQUAL_H_ +#define CONTAINERS_MIXED_EQUAL_H_ + +namespace paimon::roaring { +namespace internal { +/** + * Return true if the two containers have the same content. + */ +bool array_container_equal_bitset(const array_container_t *container1, + const bitset_container_t *container2); + +/** + * Return true if the two containers have the same content. + */ +bool run_container_equals_array(const run_container_t *container1, + const array_container_t *container2); +/** + * Return true if the two containers have the same content. + */ +bool run_container_equals_bitset(const run_container_t *container1, + const bitset_container_t *container2); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* CONTAINERS_MIXED_EQUAL_H_ */ +/* end file include/roaring/containers/mixed_equal.h */ +/* begin file include/roaring/containers/mixed_subset.h */ +/* + * mixed_subset.h + * + */ + +#ifndef CONTAINERS_MIXED_SUBSET_H_ +#define CONTAINERS_MIXED_SUBSET_H_ + +namespace paimon::roaring { +namespace internal { +/** + * Return true if container1 is a subset of container2. + */ +bool array_container_is_subset_bitset(const array_container_t *container1, + const bitset_container_t *container2); + +/** + * Return true if container1 is a subset of container2. + */ +bool run_container_is_subset_array(const run_container_t *container1, + const array_container_t *container2); + +/** + * Return true if container1 is a subset of container2. + */ +bool array_container_is_subset_run(const array_container_t *container1, + const run_container_t *container2); + +/** + * Return true if container1 is a subset of container2. + */ +bool run_container_is_subset_bitset(const run_container_t *container1, + const bitset_container_t *container2); + +/** + * Return true if container1 is a subset of container2. + */ +bool bitset_container_is_subset_run(const bitset_container_t *container1, + const run_container_t *container2); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* CONTAINERS_MIXED_SUBSET_H_ */ +/* end file include/roaring/containers/mixed_subset.h */ +/* begin file include/roaring/containers/mixed_andnot.h */ +/* + * mixed_andnot.h + */ +#ifndef INCLUDE_CONTAINERS_MIXED_ANDNOT_H_ +#define INCLUDE_CONTAINERS_MIXED_ANDNOT_H_ + +namespace paimon::roaring { +namespace internal { +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, a valid array container that could be the same as dst.*/ +void array_bitset_container_andnot(const array_container_t *src_1, const bitset_container_t *src_2, + array_container_t *dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * src_1 */ + +void array_bitset_container_iandnot(array_container_t *src_1, const bitset_container_t *src_2); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, which does not initially have a valid container. + * Return true for a bitset result; false for array + */ + +bool bitset_array_container_andnot(const bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_iandnot(bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_andnot(const run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_iandnot(run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool bitset_run_container_andnot(const bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_run_container_iandnot(bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any type of container. + */ + +int run_array_container_andnot(const run_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_array_container_iandnot(run_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* dst must be a valid array container, allowed to be src_1 */ + +void array_run_container_andnot(const array_container_t *src_1, const run_container_t *src_2, + array_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +void array_run_container_iandnot(array_container_t *src_1, const run_container_t *src_2); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_andnot(const run_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_run_container_iandnot(run_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* + * dst is a valid array container and may be the same as src_1 + */ + +void array_array_container_andnot(const array_container_t *src_1, const array_container_t *src_2, + array_container_t *dst); + +/* inplace array-array andnot will always be able to reuse the space of + * src_1 */ +void array_array_container_iandnot(array_container_t *src_1, const array_container_t *src_2); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_andnot(const bitset_container_t *src_1, + const bitset_container_t *src_2, container_t **dst); + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_bitset_container_iandnot(bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/containers/mixed_andnot.h */ +/* begin file include/roaring/containers/mixed_intersection.h */ +/* + * mixed_intersection.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ +#define INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, array intersection + */ + +namespace paimon::roaring { +namespace internal { + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_bitset_container_intersection(const array_container_t *src_1, + const bitset_container_t *src_2, array_container_t *dst); + +/* Compute the size of the intersection of src_1 and src_2. */ +int array_bitset_container_intersection_cardinality(const array_container_t *src_1, + const bitset_container_t *src_2); + +/* Checking whether src_1 and src_2 intersect. */ +bool array_bitset_container_intersect(const array_container_t *src_1, + const bitset_container_t *src_2); + +/* + * Compute the intersection between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_bitset_container_intersection(const bitset_container_t *src_1, + const bitset_container_t *src_2, container_t **dst); + +/* Compute the intersection between src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_run_container_intersection(const array_container_t *src_1, const run_container_t *src_2, + array_container_t *dst); + +/* Compute the intersection between src_1 and src_2 and write the result to + * *dst. If the result is true then the result is a bitset_container_t + * otherwise is a array_container_t. + * If *dst == src_2, then an in-place intersection is attempted + **/ +bool run_bitset_container_intersection(const run_container_t *src_1, + const bitset_container_t *src_2, container_t **dst); + +/* Compute the size of the intersection between src_1 and src_2 . */ +int array_run_container_intersection_cardinality(const array_container_t *src_1, + const run_container_t *src_2); + +/* Compute the size of the intersection between src_1 and src_2 + **/ +int run_bitset_container_intersection_cardinality(const run_container_t *src_1, + const bitset_container_t *src_2); + +/* Check that src_1 and src_2 intersect. */ +bool array_run_container_intersect(const array_container_t *src_1, const run_container_t *src_2); + +/* Check that src_1 and src_2 intersect. + **/ +bool run_bitset_container_intersect(const run_container_t *src_1, const bitset_container_t *src_2); + +/* + * Same as bitset_bitset_container_intersection except that if the output is to + * be a + * bitset_container_t, then src_1 is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_bitset_container_intersection_inplace(bitset_container_t *src_1, + const bitset_container_t *src_2, + container_t **dst); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ */ +/* end file include/roaring/containers/mixed_intersection.h */ +/* begin file include/roaring/containers/mixed_negation.h */ +/* + * mixed_negation.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_NEGATION_H_ +#define INCLUDE_CONTAINERS_MIXED_NEGATION_H_ + +namespace paimon::roaring { +namespace internal { + +/* Negation across the entire range of the container. + * Compute the negation of src and write the result + * to *dst. The complement of a + * sufficiently sparse set will always be dense and a hence a bitmap + * We assume that dst is pre-allocated and a valid bitset container + * There can be no in-place version. + */ +void array_container_negation(const array_container_t *src, bitset_container_t *dst); + +/* Negation across the entire range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation(const bitset_container_t *src, container_t **dst); + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_inplace(bitset_container_t *src, container_t **dst); + +/* Negation across the entire range of container + * Compute the negation of src and write the result + * to *dst. + * Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation(const run_container_t *src, container_t **dst); + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_inplace(run_container_t *src, container_t **dst); + +/* Negation across a range of the container. + * Compute the negation of src and write the result + * to *dst. Returns true if the result is a bitset container + * and false for an array container. *dst is not preallocated. + */ +bool array_container_negation_range(const array_container_t *src, const int range_start, + const int range_end, container_t **dst); + +/* Even when the result would fit, it is unclear how to make an + * inplace version without inefficient copying. Thus this routine + * may be a wrapper for the non-in-place version + */ +bool array_container_negation_range_inplace(array_container_t *src, const int range_start, + const int range_end, container_t **dst); + +/* Negation across a range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation_range(const bitset_container_t *src, const int range_start, + const int range_end, container_t **dst); + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_range_inplace(bitset_container_t *src, const int range_start, + const int range_end, container_t **dst); + +/* Negation across a range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation_range(const run_container_t *src, const int range_start, + const int range_end, container_t **dst); + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_range_inplace(run_container_t *src, const int range_start, + const int range_end, container_t **dst); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_MIXED_NEGATION_H_ */ +/* end file include/roaring/containers/mixed_negation.h */ +/* begin file include/roaring/containers/mixed_union.h */ +/* + * mixed_intersection.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_UNION_H_ +#define INCLUDE_CONTAINERS_MIXED_UNION_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, bitset unions + */ + +namespace paimon::roaring { +namespace internal { + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. */ +void array_bitset_container_union(const array_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). */ +void array_bitset_container_lazy_union(const array_container_t *src_1, + const bitset_container_t *src_2, bitset_container_t *dst); + +/* + * Compute the union between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool array_array_container_union(const array_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* + * Compute the union between src_1 and src_2 and write the result + * to *dst if it cannot be written to src_1. If the return function is true, + * the result is a bitset_container_t + * otherwise is a array_container_t. When the result is an array_container_t, it + * it either written to src_1 (if *dst is null) or to *dst. + * If the result is a bitset_container_t and *dst is null, then there was a failure. + */ +bool array_array_container_inplace_union(array_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* + * Same as array_array_container_union except that it will more eagerly produce + * a bitset. + */ +bool array_array_container_lazy_union(const array_container_t *src_1, + const array_container_t *src_2, container_t **dst); + +/* + * Same as array_array_container_inplace_union except that it will more eagerly produce + * a bitset. + */ +bool array_array_container_lazy_inplace_union(array_container_t *src_1, + const array_container_t *src_2, container_t **dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. We assume that dst is a + * valid container. The result might need to be further converted to array or + * bitset container, + * the caller is responsible for the eventual conversion. */ +void array_run_container_union(const array_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * src2. The result might need to be further converted to array or + * bitset container, + * the caller is responsible for the eventual conversion. */ +void array_run_container_inplace_union(const array_container_t *src_1, run_container_t *src_2); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be src_2. + * If run_container_is_full(src_1) is true, you must not be calling this + *function. + **/ +void run_bitset_container_union(const run_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be src_2. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + * If run_container_is_full(src_1) is true, you must not be calling this + * function. + * */ +void run_bitset_container_lazy_union(const run_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* INCLUDE_CONTAINERS_MIXED_UNION_H_ */ +/* end file include/roaring/containers/mixed_union.h */ +/* begin file include/roaring/containers/mixed_xor.h */ +/* + * mixed_xor.h + * + */ + +#ifndef INCLUDE_CONTAINERS_MIXED_XOR_H_ +#define INCLUDE_CONTAINERS_MIXED_XOR_H_ + +/* These functions appear to exclude cases where the + * inputs have the same type and the output is guaranteed + * to have the same type as the inputs. Eg, bitset unions + */ + +/* + * Java implementation (as of May 2016) for array_run, run_run + * and bitset_run don't do anything different for inplace. + * (They are not truly in place.) + */ + +namespace paimon::roaring { +namespace internal { + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). + * Result is true iff dst is a bitset */ +bool array_bitset_container_xor(const array_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + */ + +void array_bitset_container_lazy_xor(const array_container_t *src_1, + const bitset_container_t *src_2, bitset_container_t *dst); +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_xor(const bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_xor(const run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* lazy xor. Dst is initialized and may be equal to src_2. + * Result is left as a bitset container, even if actual + * cardinality would dictate an array container. + */ + +void run_bitset_container_lazy_xor(const run_container_t *src_1, const bitset_container_t *src_2, + bitset_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_xor(const array_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* dst does not initially have a valid container. Creates either + * an array or a bitset container, indicated by return code + */ + +bool array_array_container_xor(const array_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* dst does not initially have a valid container. Creates either + * an array or a bitset container, indicated by return code. + * A bitset container will not have a valid cardinality and the + * container type might not be correct for the actual cardinality + */ + +bool array_array_container_lazy_xor(const array_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +/* Dst is a valid run container. (Can it be src_2? Let's say not.) + * Leaves result as run container, even if other options are + * smaller. + */ + +void array_run_container_lazy_xor(const array_container_t *src_1, const run_container_t *src_2, + run_container_t *dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_xor(const run_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* INPLACE versions (initial implementation may not exploit all inplace + * opportunities (if any...) + */ + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_ixor(bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +bool bitset_bitset_container_ixor(bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +bool array_bitset_container_ixor(array_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_ixor(run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst); + +bool bitset_run_container_ixor(bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_ixor(array_container_t *src_1, const run_container_t *src_2, + container_t **dst); + +int run_array_container_ixor(run_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +bool array_array_container_ixor(array_container_t *src_1, const array_container_t *src_2, + container_t **dst); + +int run_run_container_ixor(run_container_t *src_1, const run_container_t *src_2, container_t **dst); + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/containers/mixed_xor.h */ +/* begin file include/roaring/containers/containers.h */ +#ifndef CONTAINERS_CONTAINERS_H +#define CONTAINERS_CONTAINERS_H + +#include +#include +#include + +namespace paimon::roaring { +namespace internal { + +// would enum be possible or better? + +/** + * The switch case statements follow + * BITSET_CONTAINER_TYPE -- ARRAY_CONTAINER_TYPE -- RUN_CONTAINER_TYPE + * so it makes more sense to number them 1, 2, 3 (in the vague hope that the + * compiler might exploit this ordering). + */ + +#define BITSET_CONTAINER_TYPE 1 +#define ARRAY_CONTAINER_TYPE 2 +#define RUN_CONTAINER_TYPE 3 +#define SHARED_CONTAINER_TYPE 4 + +/** + * Macros for pairing container type codes, suitable for switch statements. + * Use PAIR_CONTAINER_TYPES() for the switch, CONTAINER_PAIR() for the cases: + * + * switch (PAIR_CONTAINER_TYPES(type1, type2)) { + * case CONTAINER_PAIR(BITSET,ARRAY): + * ... + * } + */ +#define PAIR_CONTAINER_TYPES(type1, type2) (4 * (type1) + (type2)) + +#define CONTAINER_PAIR(name1, name2) (4 * (name1##_CONTAINER_TYPE) + (name2##_CONTAINER_TYPE)) + +/** + * A shared container is a wrapper around a container + * with reference counting. + */ +STRUCT_CONTAINER(shared_container_s) { + container_t *container; + uint8_t typecode; + croaring_refcount_t counter; // to be managed atomically +}; + +typedef struct shared_container_s shared_container_t; + +#define CAST_shared(c) CAST(shared_container_t *, c) // safer downcast +#define const_CAST_shared(c) CAST(const shared_container_t *, c) +#define movable_CAST_shared(c) movable_CAST(shared_container_t **, c) + +/* + * With copy_on_write = true + * Create a new shared container if the typecode is not SHARED_CONTAINER_TYPE, + * otherwise, increase the count + * If copy_on_write = false, then clone. + * Return NULL in case of failure. + **/ +container_t *get_copy_of_container(container_t *container, uint8_t *typecode, bool copy_on_write); + +/* Frees a shared container (actually decrement its counter and only frees when + * the counter falls to zero). */ +void shared_container_free(shared_container_t *container); + +/* extract a copy from the shared container, freeing the shared container if +there is just one instance left, +clone instances when the counter is higher than one +*/ +container_t *shared_container_extract_copy(shared_container_t *container, uint8_t *typecode); + +/* access to container underneath */ +static inline const container_t *container_unwrap_shared( + const container_t *candidate_shared_container, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE) { + *type = const_CAST_shared(candidate_shared_container)->typecode; + assert(*type != SHARED_CONTAINER_TYPE); + return const_CAST_shared(candidate_shared_container)->container; + } else { + return candidate_shared_container; + } +} + +/* access to container underneath */ +static inline container_t *container_mutable_unwrap_shared(container_t *c, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE) { // the passed in container is shared + *type = CAST_shared(c)->typecode; + assert(*type != SHARED_CONTAINER_TYPE); + return CAST_shared(c)->container; // return the enclosed container + } else { + return c; // wasn't shared, so return as-is + } +} + +/* access to container underneath and queries its type */ +static inline uint8_t get_container_type(const container_t *c, uint8_t type) { + if (type == SHARED_CONTAINER_TYPE) { + return const_CAST_shared(c)->typecode; + } else { + return type; + } +} + +/** + * Copies a container, requires a typecode. This allocates new memory, caller + * is responsible for deallocation. If the container is not shared, then it is + * physically cloned. Sharable containers are not cloneable. + */ +container_t *container_clone(const container_t *container, uint8_t typecode); + +/* access to container underneath, cloning it if needed */ +static inline container_t *get_writable_copy_if_shared(container_t *c, uint8_t *type) { + if (*type == SHARED_CONTAINER_TYPE) { // shared, return enclosed container + return shared_container_extract_copy(CAST_shared(c), type); + } else { + return c; // not shared, so return as-is + } +} + +/** + * End of shared container code + */ + +static const char *container_names[] = {"bitset", "array", "run", "shared"}; +static const char *shared_container_names[] = {"bitset (shared)", "array (shared)", "run (shared)"}; + +// no matter what the initial container was, convert it to a bitset +// if a new container is produced, caller responsible for freeing the previous +// one +// container should not be a shared container +static inline bitset_container_t *container_to_bitset(container_t *c, uint8_t typecode) { + bitset_container_t *result = NULL; + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return CAST_bitset(c); // nothing to do + case ARRAY_CONTAINER_TYPE: + result = bitset_container_from_array(CAST_array(c)); + return result; + case RUN_CONTAINER_TYPE: + result = bitset_container_from_run(CAST_run(c)); + return result; + case SHARED_CONTAINER_TYPE: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * Get the container name from the typecode + * (unused at time of writing) + */ +/*static inline const char *get_container_name(uint8_t typecode) { + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return container_names[0]; + case ARRAY_CONTAINER_TYPE: + return container_names[1]; + case RUN_CONTAINER_TYPE: + return container_names[2]; + case SHARED_CONTAINER_TYPE: + return container_names[3]; + default: + assert(false); + roaring_unreachable; + return "unknown"; + } +}*/ + +static inline const char *get_full_container_name(const container_t *c, uint8_t typecode) { + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return container_names[0]; + case ARRAY_CONTAINER_TYPE: + return container_names[1]; + case RUN_CONTAINER_TYPE: + return container_names[2]; + case SHARED_CONTAINER_TYPE: + switch (const_CAST_shared(c)->typecode) { + case BITSET_CONTAINER_TYPE: + return shared_container_names[0]; + case ARRAY_CONTAINER_TYPE: + return shared_container_names[1]; + case RUN_CONTAINER_TYPE: + return shared_container_names[2]; + default: + assert(false); + roaring_unreachable; + return "unknown"; + } + break; + default: + assert(false); + roaring_unreachable; + return "unknown"; + } + roaring_unreachable; + return NULL; +} + +/** + * Get the container cardinality (number of elements), requires a typecode + */ +static inline int container_get_cardinality(const container_t *c, uint8_t typecode) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_cardinality(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_cardinality(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_cardinality(const_CAST_run(c)); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +// returns true if a container is known to be full. Note that a lazy bitset +// container +// might be full without us knowing +static inline bool container_is_full(const container_t *c, uint8_t typecode) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_cardinality(const_CAST_bitset(c)) == (1 << 16); + case ARRAY_CONTAINER_TYPE: + return array_container_cardinality(const_CAST_array(c)) == (1 << 16); + case RUN_CONTAINER_TYPE: + return run_container_is_full(const_CAST_run(c)); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +static inline int container_shrink_to_fit(container_t *c, uint8_t type) { + c = container_mutable_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return 0; // no shrinking possible + case ARRAY_CONTAINER_TYPE: + return array_container_shrink_to_fit(CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_shrink_to_fit(CAST_run(c)); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * make a container with a run of ones + */ +/* initially always use a run container, even if an array might be + * marginally + * smaller */ +static inline container_t *container_range_of_ones(uint32_t range_start, uint32_t range_end, + uint8_t *result_type) { + assert(range_end >= range_start); + uint64_t cardinality = range_end - range_start + 1; + if (cardinality <= 2) { + *result_type = ARRAY_CONTAINER_TYPE; + return array_container_create_range(range_start, range_end); + } else { + *result_type = RUN_CONTAINER_TYPE; + return run_container_create_range(range_start, range_end); + } +} + +/* Create a container with all the values between in [min,max) at a + distance k*step from min. */ +static inline container_t *container_from_range(uint8_t *type, uint32_t min, uint32_t max, + uint16_t step) { + if (step == 0) return NULL; // being paranoid + if (step == 1) { + return container_range_of_ones(min, max, type); + // Note: the result is not always a run (need to check the cardinality) + //*type = RUN_CONTAINER_TYPE; + // return run_container_create_range(min, max); + } + int size = (max - min + step - 1) / step; + if (size <= DEFAULT_MAX_SIZE) { // array container + *type = ARRAY_CONTAINER_TYPE; + array_container_t *array = array_container_create_given_capacity(size); + array_container_add_from_range(array, min, max, step); + assert(array->cardinality == size); + return array; + } else { // bitset container + *type = BITSET_CONTAINER_TYPE; + bitset_container_t *bitset = bitset_container_create(); + bitset_container_add_from_range(bitset, min, max, step); + assert(bitset->cardinality == size); + return bitset; + } +} + +/** + * "repair" the container after lazy operations. + */ +static inline container_t *container_repair_after_lazy(container_t *c, uint8_t *type) { + c = get_writable_copy_if_shared(c, type); // !!! unnecessary cloning + container_t *result = NULL; + switch (*type) { + case BITSET_CONTAINER_TYPE: { + bitset_container_t *bc = CAST_bitset(c); + bc->cardinality = bitset_container_compute_cardinality(bc); + if (bc->cardinality <= DEFAULT_MAX_SIZE) { + result = array_container_from_bitset(bc); + bitset_container_free(bc); + *type = ARRAY_CONTAINER_TYPE; + return result; + } + return c; + } + case ARRAY_CONTAINER_TYPE: + return c; // nothing to do + case RUN_CONTAINER_TYPE: + return convert_run_to_efficient_container_and_free(CAST_run(c), type); + case SHARED_CONTAINER_TYPE: + assert(false); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * This is meant to be byte-by-byte compatible with the Java and Go versions of + * Roaring. + * The number of bytes written should be + * container_write(container, buf). + * + */ +static inline int32_t container_write(const container_t *c, uint8_t typecode, char *buf) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_write(const_CAST_bitset(c), buf); + case ARRAY_CONTAINER_TYPE: + return array_container_write(const_CAST_array(c), buf); + case RUN_CONTAINER_TYPE: + return run_container_write(const_CAST_run(c), buf); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * Get the container size in bytes under portable serialization (see + * container_write), requires a + * typecode + */ +static inline int32_t container_size_in_bytes(const container_t *c, uint8_t typecode) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_size_in_bytes(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_size_in_bytes(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_size_in_bytes(const_CAST_run(c)); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * print the container (useful for debugging), requires a typecode + */ +void container_printf(const container_t *container, uint8_t typecode); + +/** + * print the content of the container as a comma-separated list of 32-bit values + * starting at base, requires a typecode + */ +void container_printf_as_uint32_array(const container_t *container, uint8_t typecode, + uint32_t base); + +bool container_internal_validate(const container_t *container, uint8_t typecode, + const char **reason); + +/** + * Checks whether a container is not empty, requires a typecode + */ +static inline bool container_nonzero_cardinality(const container_t *c, uint8_t typecode) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_const_nonzero_cardinality(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_nonzero_cardinality(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_nonzero_cardinality(const_CAST_run(c)); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * Recover memory from a container, requires a typecode + */ +void container_free(container_t *container, uint8_t typecode); + +/** + * Convert a container to an array of values, requires a typecode as well as a + * "base" (most significant values) + * Returns number of ints added. + */ +static inline int container_to_uint32_array(uint32_t *output, const container_t *c, + uint8_t typecode, uint32_t base) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_to_uint32_array(output, const_CAST_bitset(c), base); + case ARRAY_CONTAINER_TYPE: + return array_container_to_uint32_array(output, const_CAST_array(c), base); + case RUN_CONTAINER_TYPE: + return run_container_to_uint32_array(output, const_CAST_run(c), base); + } + assert(false); + roaring_unreachable; + return 0; // unreached +} + +/** + * Add a value to a container, requires a typecode, fills in new_typecode and + * return (possibly different) container. + * This function may allocate a new container, and caller is responsible for + * memory deallocation + */ +static inline container_t *container_add(container_t *c, uint16_t val, + uint8_t typecode, // !!! should be second argument? + uint8_t *new_typecode) { + c = get_writable_copy_if_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + bitset_container_set(CAST_bitset(c), val); + *new_typecode = BITSET_CONTAINER_TYPE; + return c; + case ARRAY_CONTAINER_TYPE: { + array_container_t *ac = CAST_array(c); + if (array_container_try_add(ac, val, DEFAULT_MAX_SIZE) != -1) { + *new_typecode = ARRAY_CONTAINER_TYPE; + return ac; + } else { + bitset_container_t *bitset = bitset_container_from_array(ac); + bitset_container_add(bitset, val); + *new_typecode = BITSET_CONTAINER_TYPE; + return bitset; + } + } break; + case RUN_CONTAINER_TYPE: + // per Java, no container type adjustments are done (revisit?) + run_container_add(CAST_run(c), val); + *new_typecode = RUN_CONTAINER_TYPE; + return c; + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Remove a value from a container, requires a typecode, fills in new_typecode + * and + * return (possibly different) container. + * This function may allocate a new container, and caller is responsible for + * memory deallocation + */ +static inline container_t *container_remove(container_t *c, uint16_t val, + uint8_t typecode, // !!! should be second argument? + uint8_t *new_typecode) { + c = get_writable_copy_if_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + if (bitset_container_remove(CAST_bitset(c), val)) { + int card = bitset_container_cardinality(CAST_bitset(c)); + if (card <= DEFAULT_MAX_SIZE) { + *new_typecode = ARRAY_CONTAINER_TYPE; + return array_container_from_bitset(CAST_bitset(c)); + } + } + *new_typecode = typecode; + return c; + case ARRAY_CONTAINER_TYPE: + *new_typecode = typecode; + array_container_remove(CAST_array(c), val); + return c; + case RUN_CONTAINER_TYPE: + // per Java, no container type adjustments are done (revisit?) + run_container_remove(CAST_run(c), val); + *new_typecode = RUN_CONTAINER_TYPE; + return c; + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Check whether a value is in a container, requires a typecode + */ +static inline bool container_contains(const container_t *c, uint16_t val, + uint8_t typecode // !!! should be second argument? +) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_get(const_CAST_bitset(c), val); + case ARRAY_CONTAINER_TYPE: + return array_container_contains(const_CAST_array(c), val); + case RUN_CONTAINER_TYPE: + return run_container_contains(const_CAST_run(c), val); + default: + assert(false); + roaring_unreachable; + return false; + } +} + +/** + * Check whether a range of values from range_start (included) to range_end (excluded) + * is in a container, requires a typecode + */ +static inline bool container_contains_range(const container_t *c, uint32_t range_start, + uint32_t range_end, + uint8_t typecode // !!! should be second argument? +) { + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_get_range(const_CAST_bitset(c), range_start, range_end); + case ARRAY_CONTAINER_TYPE: + return array_container_contains_range(const_CAST_array(c), range_start, range_end); + case RUN_CONTAINER_TYPE: + return run_container_contains_range(const_CAST_run(c), range_start, range_end); + default: + assert(false); + roaring_unreachable; + return false; + } +} + +/** + * Returns true if the two containers have the same content. Note that + * two containers having different types can be "equal" in this sense. + */ +static inline bool container_equals(const container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + return bitset_container_equals(const_CAST_bitset(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, RUN): + return run_container_equals_bitset(const_CAST_run(c2), const_CAST_bitset(c1)); + + case CONTAINER_PAIR(RUN, BITSET): + return run_container_equals_bitset(const_CAST_run(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, ARRAY): + // java would always return false? + return array_container_equal_bitset(const_CAST_array(c2), const_CAST_bitset(c1)); + + case CONTAINER_PAIR(ARRAY, BITSET): + // java would always return false? + return array_container_equal_bitset(const_CAST_array(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, RUN): + return run_container_equals_array(const_CAST_run(c2), const_CAST_array(c1)); + + case CONTAINER_PAIR(RUN, ARRAY): + return run_container_equals_array(const_CAST_run(c1), const_CAST_array(c2)); + + case CONTAINER_PAIR(ARRAY, ARRAY): + return array_container_equals(const_CAST_array(c1), const_CAST_array(c2)); + + case CONTAINER_PAIR(RUN, RUN): + return run_container_equals(const_CAST_run(c1), const_CAST_run(c2)); + + default: + assert(false); + roaring_unreachable; + return false; + } +} + +/** + * Returns true if the container c1 is a subset of the container c2. Note that + * c1 can be a subset of c2 even if they have a different type. + */ +static inline bool container_is_subset(const container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + return bitset_container_is_subset(const_CAST_bitset(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, RUN): + return bitset_container_is_subset_run(const_CAST_bitset(c1), const_CAST_run(c2)); + + case CONTAINER_PAIR(RUN, BITSET): + return run_container_is_subset_bitset(const_CAST_run(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, ARRAY): + return false; // by construction, size(c1) > size(c2) + + case CONTAINER_PAIR(ARRAY, BITSET): + return array_container_is_subset_bitset(const_CAST_array(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, RUN): + return array_container_is_subset_run(const_CAST_array(c1), const_CAST_run(c2)); + + case CONTAINER_PAIR(RUN, ARRAY): + return run_container_is_subset_array(const_CAST_run(c1), const_CAST_array(c2)); + + case CONTAINER_PAIR(ARRAY, ARRAY): + return array_container_is_subset(const_CAST_array(c1), const_CAST_array(c2)); + + case CONTAINER_PAIR(RUN, RUN): + return run_container_is_subset(const_CAST_run(c1), const_CAST_run(c2)); + + default: + assert(false); + roaring_unreachable; + return false; + } +} + +// macro-izations possibilities for generic non-inplace binary-op dispatch + +/** + * Compute intersection between two containers, generate a new container (having + * type result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +static inline container_t *container_and(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = bitset_bitset_container_intersection(const_CAST_bitset(c1), + const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + result = array_container_create(); + array_container_intersection(const_CAST_array(c1), const_CAST_array(c2), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + return result; + + case CONTAINER_PAIR(RUN, RUN): + result = run_container_create(); + run_container_intersection(const_CAST_run(c1), const_CAST_run(c2), CAST_run(result)); + return convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + + case CONTAINER_PAIR(BITSET, ARRAY): + result = array_container_create(); + array_bitset_container_intersection(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_bitset_container_intersection(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_array(result)); + return result; + + case CONTAINER_PAIR(BITSET, RUN): + *result_type = run_bitset_container_intersection(const_CAST_run(c2), + const_CAST_bitset(c1), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = run_bitset_container_intersection(const_CAST_run(c1), + const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_run_container_intersection(const_CAST_array(c1), const_CAST_run(c2), + CAST_array(result)); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_run_container_intersection(const_CAST_array(c2), const_CAST_run(c1), + CAST_array(result)); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Compute the size of the intersection between two containers. + */ +static inline int container_and_cardinality(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + return bitset_container_and_justcard(const_CAST_bitset(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, ARRAY): + return array_container_intersection_cardinality(const_CAST_array(c1), + const_CAST_array(c2)); + + case CONTAINER_PAIR(RUN, RUN): + return run_container_intersection_cardinality(const_CAST_run(c1), const_CAST_run(c2)); + + case CONTAINER_PAIR(BITSET, ARRAY): + return array_bitset_container_intersection_cardinality(const_CAST_array(c2), + const_CAST_bitset(c1)); + + case CONTAINER_PAIR(ARRAY, BITSET): + return array_bitset_container_intersection_cardinality(const_CAST_array(c1), + const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, RUN): + return run_bitset_container_intersection_cardinality(const_CAST_run(c2), + const_CAST_bitset(c1)); + + case CONTAINER_PAIR(RUN, BITSET): + return run_bitset_container_intersection_cardinality(const_CAST_run(c1), + const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, RUN): + return array_run_container_intersection_cardinality(const_CAST_array(c1), + const_CAST_run(c2)); + + case CONTAINER_PAIR(RUN, ARRAY): + return array_run_container_intersection_cardinality(const_CAST_array(c2), + const_CAST_run(c1)); + + default: + assert(false); + roaring_unreachable; + return 0; + } +} + +/** + * Check whether two containers intersect. + */ +static inline bool container_intersect(const container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + return bitset_container_intersect(const_CAST_bitset(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, ARRAY): + return array_container_intersect(const_CAST_array(c1), const_CAST_array(c2)); + + case CONTAINER_PAIR(RUN, RUN): + return run_container_intersect(const_CAST_run(c1), const_CAST_run(c2)); + + case CONTAINER_PAIR(BITSET, ARRAY): + return array_bitset_container_intersect(const_CAST_array(c2), const_CAST_bitset(c1)); + + case CONTAINER_PAIR(ARRAY, BITSET): + return array_bitset_container_intersect(const_CAST_array(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(BITSET, RUN): + return run_bitset_container_intersect(const_CAST_run(c2), const_CAST_bitset(c1)); + + case CONTAINER_PAIR(RUN, BITSET): + return run_bitset_container_intersect(const_CAST_run(c1), const_CAST_bitset(c2)); + + case CONTAINER_PAIR(ARRAY, RUN): + return array_run_container_intersect(const_CAST_array(c1), const_CAST_run(c2)); + + case CONTAINER_PAIR(RUN, ARRAY): + return array_run_container_intersect(const_CAST_array(c2), const_CAST_run(c1)); + + default: + assert(false); + roaring_unreachable; + return 0; + } +} + +/** + * Compute intersection between two containers, with result in the first + container if possible. If the returned pointer is identical to c1, + then the container has been modified. If the returned pointer is different + from c1, then a new container has been created and the caller is responsible + for freeing it. + The type of the first container may change. Returns the modified + (and possibly new) container. +*/ +static inline container_t *container_iand(container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = bitset_bitset_container_intersection_inplace( + CAST_bitset(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + array_container_intersection_inplace(CAST_array(c1), const_CAST_array(c2)); + *result_type = ARRAY_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(RUN, RUN): + result = run_container_create(); + run_container_intersection(const_CAST_run(c1), const_CAST_run(c2), CAST_run(result)); + // as of January 2016, Java code used non-in-place intersection for + // two runcontainers + return convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + + case CONTAINER_PAIR(BITSET, ARRAY): + // c1 is a bitmap so no inplace possible + result = array_container_create(); + array_bitset_container_intersection(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_bitset_container_intersection( + const_CAST_array(c1), const_CAST_bitset(c2), + CAST_array(c1)); // result is allowed to be same as c1 + return c1; + + case CONTAINER_PAIR(BITSET, RUN): + // will attempt in-place computation + *result_type = + run_bitset_container_intersection(const_CAST_run(c2), const_CAST_bitset(c1), &c1) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = run_bitset_container_intersection(const_CAST_run(c1), + const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_run_container_intersection(const_CAST_array(c1), const_CAST_run(c2), + CAST_array(result)); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; // never bitset + array_run_container_intersection(const_CAST_array(c2), const_CAST_run(c1), + CAST_array(result)); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Compute union between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +static inline container_t *container_or(const container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + result = bitset_container_create(); + bitset_container_or(const_CAST_bitset(c1), const_CAST_bitset(c2), CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = + array_array_container_union(const_CAST_array(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + result = run_container_create(); + run_container_union(const_CAST_run(c1), const_CAST_run(c2), CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // todo: could be optimized since will never convert to array + result = convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + result = bitset_container_create(); + array_bitset_container_union(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + result = bitset_container_create(); + array_bitset_container_union(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(BITSET, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c2), CAST_run(result)); + return result; + } + result = bitset_container_create(); + run_bitset_container_union(const_CAST_run(c2), const_CAST_bitset(c1), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + if (run_container_is_full(const_CAST_run(c1))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c1), CAST_run(result)); + return result; + } + result = bitset_container_create(); + run_bitset_container_union(const_CAST_run(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = run_container_create(); + array_run_container_union(const_CAST_array(c1), const_CAST_run(c2), CAST_run(result)); + result = convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + result = run_container_create(); + array_run_container_union(const_CAST_array(c2), const_CAST_run(c1), CAST_run(result)); + result = convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; // unreached + } +} + +/** + * Compute union between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline container_t *container_lazy_or(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + result = bitset_container_create(); + bitset_container_or_nocard(const_CAST_bitset(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = array_array_container_lazy_union(const_CAST_array(c1), + const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + result = run_container_create(); + run_container_union(const_CAST_run(c1), const_CAST_run(c2), CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // we are being lazy + result = convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + result = bitset_container_create(); + array_bitset_container_lazy_union(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + result = bitset_container_create(); + array_bitset_container_lazy_union(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(BITSET, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c2), CAST_run(result)); + return result; + } + result = bitset_container_create(); + run_bitset_container_lazy_union(const_CAST_run(c2), const_CAST_bitset(c1), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + if (run_container_is_full(const_CAST_run(c1))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c1), CAST_run(result)); + return result; + } + result = bitset_container_create(); + run_bitset_container_lazy_union(const_CAST_run(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = run_container_create(); + array_run_container_union(const_CAST_array(c1), const_CAST_run(c2), CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + result = run_container_create(); + array_run_container_union(const_CAST_array(c2), const_CAST_run(c1), + CAST_run(result)); // TODO make lazy + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; // unreached + } +} + +/** + * Compute the union between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + */ +static inline container_t *container_ior(container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + bitset_container_or(const_CAST_bitset(c1), const_CAST_bitset(c2), CAST_bitset(c1)); +#ifdef OR_BITSET_CONVERSION_TO_FULL + if (CAST_bitset(c1)->cardinality == (1 << 16)) { // we convert + result = run_container_create_range(0, (1 << 16)); + *result_type = RUN_CONTAINER_TYPE; + return result; + } +#endif + *result_type = BITSET_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = + array_array_container_inplace_union(CAST_array(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + if ((result == NULL) && (*result_type == ARRAY_CONTAINER_TYPE)) { + return c1; // the computation was done in-place! + } + return result; + + case CONTAINER_PAIR(RUN, RUN): + run_container_union_inplace(CAST_run(c1), const_CAST_run(c2)); + return convert_run_to_efficient_container(CAST_run(c1), result_type); + + case CONTAINER_PAIR(BITSET, ARRAY): + array_bitset_container_union(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_bitset(c1)); + *result_type = BITSET_CONTAINER_TYPE; // never array + return c1; + + case CONTAINER_PAIR(ARRAY, BITSET): + // c1 is an array, so no in-place possible + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_bitset_container_union(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + return result; + + case CONTAINER_PAIR(BITSET, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c2), CAST_run(result)); + return result; + } + run_bitset_container_union(const_CAST_run(c2), const_CAST_bitset(c1), + CAST_bitset(c1)); // allowed + *result_type = BITSET_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(RUN, BITSET): + if (run_container_is_full(const_CAST_run(c1))) { + *result_type = RUN_CONTAINER_TYPE; + return c1; + } + result = bitset_container_create(); + run_bitset_container_union(const_CAST_run(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = run_container_create(); + array_run_container_union(const_CAST_array(c1), const_CAST_run(c2), CAST_run(result)); + result = convert_run_to_efficient_container_and_free(CAST_run(result), result_type); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + array_run_container_inplace_union(const_CAST_array(c2), CAST_run(c1)); + c1 = convert_run_to_efficient_container(CAST_run(c1), result_type); + return c1; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Compute the union between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline container_t *container_lazy_ior(container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + assert(type1 != SHARED_CONTAINER_TYPE); + // c1 = get_writable_copy_if_shared(c1,&type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): +#ifdef LAZY_OR_BITSET_CONVERSION_TO_FULL + // if we have two bitsets, we might as well compute the cardinality + bitset_container_or(const_CAST_bitset(c1), const_CAST_bitset(c2), CAST_bitset(c1)); + // it is possible that two bitsets can lead to a full container + if (CAST_bitset(c1)->cardinality == (1 << 16)) { // we convert + result = run_container_create_range(0, (1 << 16)); + *result_type = RUN_CONTAINER_TYPE; + return result; + } +#else + bitset_container_or_nocard(const_CAST_bitset(c1), const_CAST_bitset(c2), + CAST_bitset(c1)); + +#endif + *result_type = BITSET_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = array_array_container_lazy_inplace_union(CAST_array(c1), + const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + if ((result == NULL) && (*result_type == ARRAY_CONTAINER_TYPE)) { + return c1; // the computation was done in-place! + } + return result; + + case CONTAINER_PAIR(RUN, RUN): + run_container_union_inplace(CAST_run(c1), const_CAST_run(c2)); + *result_type = RUN_CONTAINER_TYPE; + return convert_run_to_efficient_container(CAST_run(c1), result_type); + + case CONTAINER_PAIR(BITSET, ARRAY): + array_bitset_container_lazy_union(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_bitset(c1)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; // never array + return c1; + + case CONTAINER_PAIR(ARRAY, BITSET): + // c1 is an array, so no in-place possible + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_bitset_container_lazy_union(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // is lazy + return result; + + case CONTAINER_PAIR(BITSET, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = run_container_create(); + *result_type = RUN_CONTAINER_TYPE; + run_container_copy(const_CAST_run(c2), CAST_run(result)); + return result; + } + run_bitset_container_lazy_union(const_CAST_run(c2), const_CAST_bitset(c1), + CAST_bitset(c1)); // allowed // lazy + *result_type = BITSET_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(RUN, BITSET): + if (run_container_is_full(const_CAST_run(c1))) { + *result_type = RUN_CONTAINER_TYPE; + return c1; + } + result = bitset_container_create(); + run_bitset_container_lazy_union(const_CAST_run(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = run_container_create(); + array_run_container_union(const_CAST_array(c1), const_CAST_run(c2), CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container_and_free(result, + // result_type); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + array_run_container_inplace_union(const_CAST_array(c2), CAST_run(c1)); + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container_and_free(result, + // result_type); + return c1; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Compute symmetric difference (xor) between two containers, generate a new + * container (having type result_type), requires a typecode. This allocates new + * memory, caller is responsible for deallocation. + */ +static inline container_t *container_xor(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = + bitset_bitset_container_xor(const_CAST_bitset(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = + array_array_container_xor(const_CAST_array(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + *result_type = + (uint8_t)run_run_container_xor(const_CAST_run(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + *result_type = + array_bitset_container_xor(const_CAST_array(c2), const_CAST_bitset(c1), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + *result_type = + array_bitset_container_xor(const_CAST_array(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(BITSET, RUN): + *result_type = + run_bitset_container_xor(const_CAST_run(c2), const_CAST_bitset(c1), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = + run_bitset_container_xor(const_CAST_run(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + *result_type = + (uint8_t)array_run_container_xor(const_CAST_array(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + *result_type = + (uint8_t)array_run_container_xor(const_CAST_array(c2), const_CAST_run(c1), &result); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; // unreached + } +} + +/* Applies an offset to the non-empty container 'c'. + * The results are stored in new containers returned via 'lo' and 'hi', for the + * low and high halves of the result (where the low half matches the original key + * and the high one corresponds to values for the following key). + * Either one of 'lo' and 'hi' are allowed to be 'NULL', but not both. + * Whenever one of them is not 'NULL', it should point to a 'NULL' container. + * Whenever one of them is 'NULL' the shifted elements for that part will not be + * computed. + * If either of the resulting containers turns out to be empty, the pointed + * container will remain 'NULL'. + */ +static inline void container_add_offset(const container_t *c, uint8_t type, container_t **lo, + container_t **hi, uint16_t offset) { + assert(offset != 0); + assert(container_nonzero_cardinality(c, type)); + assert(lo != NULL || hi != NULL); + assert(lo == NULL || *lo == NULL); + assert(hi == NULL || *hi == NULL); + + switch (type) { + case BITSET_CONTAINER_TYPE: + bitset_container_offset(const_CAST_bitset(c), lo, hi, offset); + break; + case ARRAY_CONTAINER_TYPE: + array_container_offset(const_CAST_array(c), lo, hi, offset); + break; + case RUN_CONTAINER_TYPE: + run_container_offset(const_CAST_run(c), lo, hi, offset); + break; + default: + assert(false); + roaring_unreachable; + break; + } +} + +/** + * Compute xor between two containers, generate a new container (having type + * result_type), requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline container_t *container_lazy_xor(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + result = bitset_container_create(); + bitset_container_xor_nocard(const_CAST_bitset(c1), const_CAST_bitset(c2), + CAST_bitset(result)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = + array_array_container_lazy_xor(const_CAST_array(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + // nothing special done yet. + *result_type = + (uint8_t)run_run_container_xor(const_CAST_run(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_bitset_container_lazy_xor(const_CAST_array(c2), const_CAST_bitset(c1), + CAST_bitset(result)); + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_bitset_container_lazy_xor(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + return result; + + case CONTAINER_PAIR(BITSET, RUN): + result = bitset_container_create(); + run_bitset_container_lazy_xor(const_CAST_run(c2), const_CAST_bitset(c1), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + result = bitset_container_create(); + run_bitset_container_lazy_xor(const_CAST_run(c1), const_CAST_bitset(c2), + CAST_bitset(result)); + *result_type = BITSET_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + result = run_container_create(); + array_run_container_lazy_xor(const_CAST_array(c1), const_CAST_run(c2), + CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + result = run_container_create(); + array_run_container_lazy_xor(const_CAST_array(c2), const_CAST_run(c1), + CAST_run(result)); + *result_type = RUN_CONTAINER_TYPE; + // next line skipped since we are lazy + // result = convert_run_to_efficient_container(result, result_type); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; // unreached + } +} + +/** + * Compute the xor between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + */ +static inline container_t *container_ixor(container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = + bitset_bitset_container_ixor(CAST_bitset(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + *result_type = array_array_container_ixor(CAST_array(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + *result_type = + (uint8_t)run_run_container_ixor(CAST_run(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + *result_type = + bitset_array_container_ixor(CAST_bitset(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + *result_type = + array_bitset_container_ixor(CAST_array(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(BITSET, RUN): + *result_type = bitset_run_container_ixor(CAST_bitset(c1), const_CAST_run(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + + return result; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = run_bitset_container_ixor(CAST_run(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + *result_type = + (uint8_t)array_run_container_ixor(CAST_array(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + *result_type = + (uint8_t)run_array_container_ixor(CAST_run(c1), const_CAST_array(c2), &result); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Compute the xor between two containers, with result in the first container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + * + * This lazy version delays some operations such as the maintenance of the + * cardinality. It requires repair later on the generated containers. + */ +static inline container_t *container_lazy_ixor(container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + assert(type1 != SHARED_CONTAINER_TYPE); + // c1 = get_writable_copy_if_shared(c1,&type1); + c2 = container_unwrap_shared(c2, &type2); + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + bitset_container_xor_nocard(CAST_bitset(c1), const_CAST_bitset(c2), + CAST_bitset(c1)); // is lazy + *result_type = BITSET_CONTAINER_TYPE; + return c1; + + // TODO: other cases being lazy, esp. when we know inplace not likely + // could see the corresponding code for union + default: + // we may have a dirty bitset (without a precomputed cardinality) + // and calling container_ixor on it might be unsafe. + if (type1 == BITSET_CONTAINER_TYPE) { + bitset_container_t *bc = CAST_bitset(c1); + if (bc->cardinality == BITSET_UNKNOWN_CARDINALITY) { + bc->cardinality = bitset_container_compute_cardinality(bc); + } + } + return container_ixor(c1, type1, c2, type2, result_type); + } +} + +/** + * Compute difference (andnot) between two containers, generate a new + * container (having type result_type), requires a typecode. This allocates new + * memory, caller is responsible for deallocation. + */ +static inline container_t *container_andnot(const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type) { + c1 = container_unwrap_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = bitset_bitset_container_andnot(const_CAST_bitset(c1), + const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + result = array_container_create(); + array_array_container_andnot(const_CAST_array(c1), const_CAST_array(c2), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + } + *result_type = + (uint8_t)run_run_container_andnot(const_CAST_run(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + *result_type = + bitset_array_container_andnot(const_CAST_bitset(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + result = array_container_create(); + array_bitset_container_andnot(const_CAST_array(c1), const_CAST_bitset(c2), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(BITSET, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + } + *result_type = + bitset_run_container_andnot(const_CAST_bitset(c1), const_CAST_run(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = + run_bitset_container_andnot(const_CAST_run(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + if (run_container_is_full(const_CAST_run(c2))) { + result = array_container_create(); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + } + result = array_container_create(); + array_run_container_andnot(const_CAST_array(c1), const_CAST_run(c2), + CAST_array(result)); + *result_type = ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, ARRAY): + *result_type = (uint8_t)run_array_container_andnot(const_CAST_run(c1), + const_CAST_array(c2), &result); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; // unreached + } +} + +/** + * Compute the andnot between two containers, with result in the first + * container. + * If the returned pointer is identical to c1, then the container has been + * modified. + * If the returned pointer is different from c1, then a new container has been + * created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container + */ +static inline container_t *container_iandnot(container_t *c1, uint8_t type1, const container_t *c2, + uint8_t type2, uint8_t *result_type) { + c1 = get_writable_copy_if_shared(c1, &type1); + c2 = container_unwrap_shared(c2, &type2); + container_t *result = NULL; + switch (PAIR_CONTAINER_TYPES(type1, type2)) { + case CONTAINER_PAIR(BITSET, BITSET): + *result_type = + bitset_bitset_container_iandnot(CAST_bitset(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, ARRAY): + array_array_container_iandnot(CAST_array(c1), const_CAST_array(c2)); + *result_type = ARRAY_CONTAINER_TYPE; + return c1; + + case CONTAINER_PAIR(RUN, RUN): + *result_type = + (uint8_t)run_run_container_iandnot(CAST_run(c1), const_CAST_run(c2), &result); + return result; + + case CONTAINER_PAIR(BITSET, ARRAY): + *result_type = + bitset_array_container_iandnot(CAST_bitset(c1), const_CAST_array(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, BITSET): + *result_type = ARRAY_CONTAINER_TYPE; + array_bitset_container_iandnot(CAST_array(c1), const_CAST_bitset(c2)); + return c1; + + case CONTAINER_PAIR(BITSET, RUN): + *result_type = + bitset_run_container_iandnot(CAST_bitset(c1), const_CAST_run(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(RUN, BITSET): + *result_type = + run_bitset_container_iandnot(CAST_run(c1), const_CAST_bitset(c2), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + + case CONTAINER_PAIR(ARRAY, RUN): + *result_type = ARRAY_CONTAINER_TYPE; + array_run_container_iandnot(CAST_array(c1), const_CAST_run(c2)); + return c1; + + case CONTAINER_PAIR(RUN, ARRAY): + *result_type = + (uint8_t)run_array_container_iandnot(CAST_run(c1), const_CAST_array(c2), &result); + return result; + + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +/** + * Visit all values x of the container once, passing (base+x,ptr) + * to iterator. You need to specify a container and its type. + * Returns true if the iteration should continue. + */ +static inline bool container_iterate(const container_t *c, uint8_t type, uint32_t base, + roaring_iterator iterator, void *ptr) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_iterate(const_CAST_bitset(c), base, iterator, ptr); + case ARRAY_CONTAINER_TYPE: + return array_container_iterate(const_CAST_array(c), base, iterator, ptr); + case RUN_CONTAINER_TYPE: + return run_container_iterate(const_CAST_run(c), base, iterator, ptr); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +static inline bool container_iterate64(const container_t *c, uint8_t type, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, void *ptr) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_iterate64(const_CAST_bitset(c), base, iterator, high_bits, ptr); + case ARRAY_CONTAINER_TYPE: + return array_container_iterate64(const_CAST_array(c), base, iterator, high_bits, ptr); + case RUN_CONTAINER_TYPE: + return run_container_iterate64(const_CAST_run(c), base, iterator, high_bits, ptr); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +static inline container_t *container_not(const container_t *c, uint8_t type, uint8_t *result_type) { + c = container_unwrap_shared(c, &type); + container_t *result = NULL; + switch (type) { + case BITSET_CONTAINER_TYPE: + *result_type = bitset_container_negation(const_CAST_bitset(c), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case ARRAY_CONTAINER_TYPE: + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_container_negation(const_CAST_array(c), CAST_bitset(result)); + return result; + case RUN_CONTAINER_TYPE: + *result_type = (uint8_t)run_container_negation(const_CAST_run(c), &result); + return result; + + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return NULL; +} + +static inline container_t *container_not_range(const container_t *c, uint8_t type, + uint32_t range_start, uint32_t range_end, + uint8_t *result_type) { + c = container_unwrap_shared(c, &type); + container_t *result = NULL; + switch (type) { + case BITSET_CONTAINER_TYPE: + *result_type = bitset_container_negation_range(const_CAST_bitset(c), range_start, + range_end, &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case ARRAY_CONTAINER_TYPE: + *result_type = + array_container_negation_range(const_CAST_array(c), range_start, range_end, &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case RUN_CONTAINER_TYPE: + *result_type = (uint8_t)run_container_negation_range(const_CAST_run(c), range_start, + range_end, &result); + return result; + + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return NULL; +} + +static inline container_t *container_inot(container_t *c, uint8_t type, uint8_t *result_type) { + c = get_writable_copy_if_shared(c, &type); + container_t *result = NULL; + switch (type) { + case BITSET_CONTAINER_TYPE: + *result_type = bitset_container_negation_inplace(CAST_bitset(c), &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case ARRAY_CONTAINER_TYPE: + // will never be inplace + result = bitset_container_create(); + *result_type = BITSET_CONTAINER_TYPE; + array_container_negation(CAST_array(c), CAST_bitset(result)); + array_container_free(CAST_array(c)); + return result; + case RUN_CONTAINER_TYPE: + *result_type = (uint8_t)run_container_negation_inplace(CAST_run(c), &result); + return result; + + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return NULL; +} + +static inline container_t *container_inot_range(container_t *c, uint8_t type, uint32_t range_start, + uint32_t range_end, uint8_t *result_type) { + c = get_writable_copy_if_shared(c, &type); + container_t *result = NULL; + switch (type) { + case BITSET_CONTAINER_TYPE: + *result_type = bitset_container_negation_range_inplace(CAST_bitset(c), range_start, + range_end, &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case ARRAY_CONTAINER_TYPE: + *result_type = array_container_negation_range_inplace(CAST_array(c), range_start, + range_end, &result) + ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + return result; + case RUN_CONTAINER_TYPE: + *result_type = (uint8_t)run_container_negation_range_inplace(CAST_run(c), range_start, + range_end, &result); + return result; + + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return NULL; +} + +/** + * If the element of given rank is in this container, supposing that + * the first + * element has rank start_rank, then the function returns true and + * sets element + * accordingly. + * Otherwise, it returns false and update start_rank. + */ +static inline bool container_select(const container_t *c, uint8_t type, uint32_t *start_rank, + uint32_t rank, uint32_t *element) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_select(const_CAST_bitset(c), start_rank, rank, element); + case ARRAY_CONTAINER_TYPE: + return array_container_select(const_CAST_array(c), start_rank, rank, element); + case RUN_CONTAINER_TYPE: + return run_container_select(const_CAST_run(c), start_rank, rank, element); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +static inline uint16_t container_maximum(const container_t *c, uint8_t type) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_maximum(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_maximum(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_maximum(const_CAST_run(c)); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +static inline uint16_t container_minimum(const container_t *c, uint8_t type) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_minimum(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_minimum(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_minimum(const_CAST_run(c)); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +// number of values smaller or equal to x +static inline int container_rank(const container_t *c, uint8_t type, uint16_t x) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_rank(const_CAST_bitset(c), x); + case ARRAY_CONTAINER_TYPE: + return array_container_rank(const_CAST_array(c), x); + case RUN_CONTAINER_TYPE: + return run_container_rank(const_CAST_run(c), x); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +// bulk version of container_rank(); return number of consumed elements +static inline uint32_t container_rank_many(const container_t *c, uint8_t type, uint64_t start_rank, + const uint32_t *begin, const uint32_t *end, + uint64_t *ans) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_rank_many(const_CAST_bitset(c), start_rank, begin, end, ans); + case ARRAY_CONTAINER_TYPE: + return array_container_rank_many(const_CAST_array(c), start_rank, begin, end, ans); + case RUN_CONTAINER_TYPE: + return run_container_rank_many(const_CAST_run(c), start_rank, begin, end, ans); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return 0; +} + +// return the index of x, if not exsist return -1 +static inline int container_get_index(const container_t *c, uint8_t type, uint16_t x) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + return bitset_container_get_index(const_CAST_bitset(c), x); + case ARRAY_CONTAINER_TYPE: + return array_container_get_index(const_CAST_array(c), x); + case RUN_CONTAINER_TYPE: + return run_container_get_index(const_CAST_run(c), x); + default: + assert(false); + roaring_unreachable; + } + assert(false); + roaring_unreachable; + return false; +} + +/** + * Add all values in range [min, max] to a given container. + * + * If the returned pointer is different from $container, then a new container + * has been created and the caller is responsible for freeing it. + * The type of the first container may change. Returns the modified + * (and possibly new) container. + */ +static inline container_t *container_add_range(container_t *c, uint8_t type, uint32_t min, + uint32_t max, uint8_t *result_type) { + // NB: when selecting new container type, we perform only inexpensive checks + switch (type) { + case BITSET_CONTAINER_TYPE: { + bitset_container_t *bitset = CAST_bitset(c); + + int32_t union_cardinality = 0; + union_cardinality += bitset->cardinality; + union_cardinality += max - min + 1; + union_cardinality -= bitset_lenrange_cardinality(bitset->words, min, max - min); + + if (union_cardinality == INT32_C(0x10000)) { + *result_type = RUN_CONTAINER_TYPE; + return run_container_create_range(0, INT32_C(0x10000)); + } else { + *result_type = BITSET_CONTAINER_TYPE; + bitset_set_lenrange(bitset->words, min, max - min); + bitset->cardinality = union_cardinality; + return bitset; + } + } + case ARRAY_CONTAINER_TYPE: { + array_container_t *array = CAST_array(c); + + int32_t nvals_greater = count_greater(array->array, array->cardinality, (uint16_t)max); + int32_t nvals_less = + count_less(array->array, array->cardinality - nvals_greater, (uint16_t)min); + int32_t union_cardinality = nvals_less + (max - min + 1) + nvals_greater; + + if (union_cardinality == INT32_C(0x10000)) { + *result_type = RUN_CONTAINER_TYPE; + return run_container_create_range(0, INT32_C(0x10000)); + } else if (union_cardinality <= DEFAULT_MAX_SIZE) { + *result_type = ARRAY_CONTAINER_TYPE; + array_container_add_range_nvals(array, min, max, nvals_less, nvals_greater); + return array; + } else { + *result_type = BITSET_CONTAINER_TYPE; + bitset_container_t *bitset = bitset_container_from_array(array); + bitset_set_lenrange(bitset->words, min, max - min); + bitset->cardinality = union_cardinality; + return bitset; + } + } + case RUN_CONTAINER_TYPE: { + run_container_t *run = CAST_run(c); + + int32_t nruns_greater = rle16_count_greater(run->runs, run->n_runs, (uint16_t)max); + int32_t nruns_less = + rle16_count_less(run->runs, run->n_runs - nruns_greater, (uint16_t)min); + + int32_t run_size_bytes = (nruns_less + 1 + nruns_greater) * sizeof(rle16_t); + int32_t bitset_size_bytes = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + + if (run_size_bytes <= bitset_size_bytes) { + run_container_add_range_nruns(run, min, max, nruns_less, nruns_greater); + *result_type = RUN_CONTAINER_TYPE; + return run; + } else { + return container_from_run_range(run, min, max, result_type); + } + } + default: + roaring_unreachable; + } +} + +/* + * Removes all elements in range [min, max]. + * Returns one of: + * - NULL if no elements left + * - pointer to the original container + * - pointer to a newly-allocated container (if it is more efficient) + * + * If the returned pointer is different from $container, then a new container + * has been created and the caller is responsible for freeing the original container. + */ +static inline container_t *container_remove_range(container_t *c, uint8_t type, uint32_t min, + uint32_t max, uint8_t *result_type) { + switch (type) { + case BITSET_CONTAINER_TYPE: { + bitset_container_t *bitset = CAST_bitset(c); + + int32_t result_cardinality = + bitset->cardinality - bitset_lenrange_cardinality(bitset->words, min, max - min); + + if (result_cardinality == 0) { + return NULL; + } else if (result_cardinality <= DEFAULT_MAX_SIZE) { + *result_type = ARRAY_CONTAINER_TYPE; + bitset_reset_range(bitset->words, min, max + 1); + bitset->cardinality = result_cardinality; + return array_container_from_bitset(bitset); + } else { + *result_type = BITSET_CONTAINER_TYPE; + bitset_reset_range(bitset->words, min, max + 1); + bitset->cardinality = result_cardinality; + return bitset; + } + } + case ARRAY_CONTAINER_TYPE: { + array_container_t *array = CAST_array(c); + + int32_t nvals_greater = count_greater(array->array, array->cardinality, (uint16_t)max); + int32_t nvals_less = + count_less(array->array, array->cardinality - nvals_greater, (uint16_t)min); + int32_t result_cardinality = nvals_less + nvals_greater; + + if (result_cardinality == 0) { + return NULL; + } else { + *result_type = ARRAY_CONTAINER_TYPE; + array_container_remove_range(array, nvals_less, + array->cardinality - result_cardinality); + return array; + } + } + case RUN_CONTAINER_TYPE: { + run_container_t *run = CAST_run(c); + + if (run->n_runs == 0) { + return NULL; + } + if (min <= run_container_minimum(run) && max >= run_container_maximum(run)) { + return NULL; + } + + run_container_remove_range(run, min, max); + return convert_run_to_efficient_container(run, result_type); + } + default: + roaring_unreachable; + } +} + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/containers/containers.h */ +/* begin file include/roaring/roaring_array.h */ +#ifndef INCLUDE_ROARING_ARRAY_H +#define INCLUDE_ROARING_ARRAY_H + +#include +#include +#include + +namespace paimon::roaring { + +// Note: in pure C++ code, you should avoid putting `using` in header files +using internal::roaring_array_t; + +namespace internal { +//#endif + +enum { + SERIAL_COOKIE_NO_RUNCONTAINER = 12346, + SERIAL_COOKIE = 12347, + FROZEN_COOKIE = 13766, + NO_OFFSET_THRESHOLD = 4 +}; + +/** + * Create a new roaring array + */ +roaring_array_t *ra_create(void); + +/** + * Initialize an existing roaring array with the specified capacity (in number + * of containers) + */ +bool ra_init_with_capacity(roaring_array_t *new_ra, uint32_t cap); + +/** + * Initialize with zero capacity + */ +void ra_init(roaring_array_t *t); + +/** + * Copies this roaring array, we assume that dest is not initialized + */ +bool ra_copy(const roaring_array_t *source, roaring_array_t *dest, bool copy_on_write); + +/* + * Shrinks the capacity, returns the number of bytes saved. + */ +int ra_shrink_to_fit(roaring_array_t *ra); + +/** + * Copies this roaring array, we assume that dest is initialized + */ +bool ra_overwrite(const roaring_array_t *source, roaring_array_t *dest, bool copy_on_write); + +/** + * Frees the memory used by a roaring array + */ +void ra_clear(roaring_array_t *r); + +/** + * Frees the memory used by a roaring array, but does not free the containers + */ +void ra_clear_without_containers(roaring_array_t *r); + +/** + * Frees just the containers + */ +void ra_clear_containers(roaring_array_t *ra); + +/** + * Get the index corresponding to a 16-bit key + */ +inline int32_t ra_get_index(const roaring_array_t *ra, uint16_t x) { + if ((ra->size == 0) || ra->keys[ra->size - 1] == x) return ra->size - 1; + return binarySearch(ra->keys, (int32_t)ra->size, x); +} + +/** + * Retrieves the container at index i, filling in the typecode + */ +inline container_t *ra_get_container_at_index(const roaring_array_t *ra, uint16_t i, + uint8_t *typecode) { + *typecode = ra->typecodes[i]; + return ra->containers[i]; +} + +/** + * Retrieves the key at index i + */ +inline uint16_t ra_get_key_at_index(const roaring_array_t *ra, uint16_t i) { + return ra->keys[i]; +} + +/** + * Add a new key-value pair at index i + */ +void ra_insert_new_key_value_at(roaring_array_t *ra, int32_t i, uint16_t key, container_t *c, + uint8_t typecode); + +/** + * Append a new key-value pair + */ +void ra_append(roaring_array_t *ra, uint16_t key, container_t *c, uint8_t typecode); + +/** + * Append a new key-value pair to ra, cloning (in COW sense) a value from sa + * at index index + */ +void ra_append_copy(roaring_array_t *ra, const roaring_array_t *sa, uint16_t index, + bool copy_on_write); + +/** + * Append new key-value pairs to ra, cloning (in COW sense) values from sa + * at indexes + * [start_index, end_index) + */ +void ra_append_copy_range(roaring_array_t *ra, const roaring_array_t *sa, int32_t start_index, + int32_t end_index, bool copy_on_write); + +/** appends from sa to ra, ending with the greatest key that is + * is less or equal stopping_key + */ +void ra_append_copies_until(roaring_array_t *ra, const roaring_array_t *sa, uint16_t stopping_key, + bool copy_on_write); + +/** appends from sa to ra, starting with the smallest key that is + * is strictly greater than before_start + */ + +void ra_append_copies_after(roaring_array_t *ra, const roaring_array_t *sa, uint16_t before_start, + bool copy_on_write); + +/** + * Move the key-value pairs to ra from sa at indexes + * [start_index, end_index), old array should not be freed + * (use ra_clear_without_containers) + **/ +void ra_append_move_range(roaring_array_t *ra, roaring_array_t *sa, int32_t start_index, + int32_t end_index); +/** + * Append new key-value pairs to ra, from sa at indexes + * [start_index, end_index) + */ +void ra_append_range(roaring_array_t *ra, roaring_array_t *sa, int32_t start_index, + int32_t end_index, bool copy_on_write); + +/** + * Set the container at the corresponding index using the specified + * typecode. + */ +inline void ra_set_container_at_index(const roaring_array_t *ra, int32_t i, container_t *c, + uint8_t typecode) { + assert(i < ra->size); + ra->containers[i] = c; + ra->typecodes[i] = typecode; +} + +container_t *ra_get_container(roaring_array_t *ra, uint16_t x, uint8_t *typecode); + +/** + * If needed, increase the capacity of the array so that it can fit k values + * (at + * least); + */ +bool extend_array(roaring_array_t *ra, int32_t k); + +inline int32_t ra_get_size(const roaring_array_t *ra) { + return ra->size; +} + +static inline int32_t ra_advance_until(const roaring_array_t *ra, uint16_t x, int32_t pos) { + return advanceUntil(ra->keys, pos, ra->size, x); +} + +int32_t ra_advance_until_freeing(roaring_array_t *ra, uint16_t x, int32_t pos); + +void ra_downsize(roaring_array_t *ra, int32_t new_length); + +inline void ra_replace_key_and_container_at_index(roaring_array_t *ra, int32_t i, uint16_t key, + container_t *c, uint8_t typecode) { + assert(i < ra->size); + + ra->keys[i] = key; + ra->containers[i] = c; + ra->typecodes[i] = typecode; +} + +// write set bits to an array +void ra_to_uint32_array(const roaring_array_t *ra, uint32_t *ans); + +bool ra_range_uint32_array(const roaring_array_t *ra, size_t offset, size_t limit, uint32_t *ans); + +/** + * write a bitmap to a buffer. This is meant to be compatible with + * the + * Java and Go versions. Return the size in bytes of the serialized + * output (which should be ra_portable_size_in_bytes(ra)). + */ +size_t ra_portable_serialize(const roaring_array_t *ra, char *buf); + +/** + * read a bitmap from a serialized version. This is meant to be compatible + * with the Java and Go versions. + * maxbytes indicates how many bytes available from buf. + * When the function returns true, roaring_array_t is populated with the data + * and *readbytes indicates how many bytes were read. In all cases, if the function + * returns true, then maxbytes >= *readbytes. + */ +bool ra_portable_deserialize(roaring_array_t *ra, const char *buf, const size_t maxbytes, + size_t *readbytes); + +/** + * Quickly checks whether there is a serialized bitmap at the pointer, + * not exceeding size "maxbytes" in bytes. This function does not allocate + * memory dynamically. + * + * This function returns 0 if and only if no valid bitmap is found. + * Otherwise, it returns how many bytes are occupied by the bitmap data. + */ +size_t ra_portable_deserialize_size(const char *buf, const size_t maxbytes); + +/** + * How many bytes are required to serialize this bitmap (meant to be + * compatible + * with Java and Go versions) + */ +size_t ra_portable_size_in_bytes(const roaring_array_t *ra); + +/** + * return true if it contains at least one run container. + */ +bool ra_has_run_container(const roaring_array_t *ra); + +/** + * Size of the header when serializing (meant to be compatible + * with Java and Go versions) + */ +uint32_t ra_portable_header_size(const roaring_array_t *ra); + +/** + * If the container at the index i is share, unshare it (creating a local + * copy if needed). + */ +static inline void ra_unshare_container_at_index(roaring_array_t *ra, uint16_t i) { + assert(i < ra->size); + ra->containers[i] = get_writable_copy_if_shared(ra->containers[i], &ra->typecodes[i]); +} + +/** + * remove at index i, sliding over all entries after i + */ +void ra_remove_at_index(roaring_array_t *ra, int32_t i); + +/** + * clears all containers, sets the size at 0 and shrinks the memory usage. + */ +void ra_reset(roaring_array_t *ra); + +/** + * remove at index i, sliding over all entries after i. Free removed container. + */ +void ra_remove_at_index_and_free(roaring_array_t *ra, int32_t i); + +/** + * remove a chunk of indices, sliding over entries after it + */ +// void ra_remove_index_range(roaring_array_t *ra, int32_t begin, int32_t end); + +// used in inplace andNot only, to slide left the containers from +// the mutated RoaringBitmap that are after the largest container of +// the argument RoaringBitmap. It is followed by a call to resize. +// +void ra_copy_range(roaring_array_t *ra, uint32_t begin, uint32_t end, uint32_t new_begin); + +/** + * Shifts rightmost $count containers to the left (distance < 0) or + * to the right (distance > 0). + * Allocates memory if necessary. + * This function doesn't free or create new containers. + * Caller is responsible for that. + */ +void ra_shift_tail(roaring_array_t *ra, int32_t count, int32_t distance); + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/roaring_array.h */ +/* begin file src/array_util.c */ +#include +#include +#include +#include +#include +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { +inline int32_t binarySearch(const uint16_t *array, int32_t lenarray, uint16_t ikey); + +#if CROARING_IS_X64 +// used by intersect_vector16 +ALIGNED(0x1000) +static const uint8_t shuffle_mask16[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0xFF, 0xFF, 0xFF, 0xFF, + 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF, 0xFF, + 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 0xFF, 0xFF, + 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 0xFF, 0xFF, + 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, + 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +/** + * From Schlegel et al., Fast Sorted-Set Intersection using SIMD Instructions + * Optimized by D. Lemire on May 3rd 2013 + */ +CROARING_TARGET_AVX2 +int32_t intersect_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, uint16_t *C) { + size_t count = 0; + size_t i_a = 0, i_b = 0; + const int vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + __m128i v_a, v_b; + if ((i_a < st_a) && (i_b < st_b)) { + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + while ((A[i_a] == 0) || (B[i_b] == 0)) { + const __m128i res_v = + _mm_cmpestrm(v_b, vectorlength, v_a, vectorlength, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = _mm_loadu_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + if ((i_a < st_a) && (i_b < st_b)) + while (true) { + const __m128i res_v = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = _mm_loadu_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + } + // intersect the tail using scalar intersection + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (a < b) { + i_a++; + } else if (b < a) { + i_b++; + } else { + C[count] = a; //==b; + count++; + i_a++; + i_b++; + } + } + return (int32_t)count; +} + +ALLOW_UNALIGNED +int array_container_to_uint32_array_vector16(void *vout, const uint16_t *array, size_t cardinality, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + size_t i = 0; + for (; i + sizeof(__m128i) / sizeof(uint16_t) <= cardinality; + i += sizeof(__m128i) / sizeof(uint16_t)) { + __m128i vinput = _mm_loadu_si128((const __m128i *)(array + i)); + __m256i voutput = _mm256_add_epi32(_mm256_cvtepu16_epi32(vinput), _mm256_set1_epi32(base)); + _mm256_storeu_si256((__m256i *)(out + outpos), voutput); + outpos += sizeof(__m256i) / sizeof(uint32_t); + } + for (; i < cardinality; ++i) { + const uint32_t val = base + array[i]; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + return outpos; +} + +int32_t intersect_vector16_inplace(uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b) { + size_t count = 0; + size_t i_a = 0, i_b = 0; + const int vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + __m128i v_a, v_b; + if ((i_a < st_a) && (i_b < st_b)) { + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + __m128i tmp[2] = {_mm_setzero_si128()}; + size_t tmp_count = 0; + while ((A[i_a] == 0) || (B[i_b] == 0)) { + const __m128i res_v = + _mm_cmpestrm(v_b, vectorlength, v_a, vectorlength, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = _mm_loadu_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&((uint16_t *)tmp)[tmp_count], p); + tmp_count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + _mm_storeu_si128((__m128i *)&A[count], tmp[0]); + _mm_storeu_si128(tmp, _mm_setzero_si128()); + count += tmp_count; + tmp_count = 0; + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + if ((i_a < st_a) && (i_b < st_b)) { + while (true) { + const __m128i res_v = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + __m128i sm16 = _mm_loadu_si128((const __m128i *)shuffle_mask16 + r); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&((uint16_t *)tmp)[tmp_count], p); + tmp_count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + _mm_storeu_si128((__m128i *)&A[count], tmp[0]); + _mm_storeu_si128(tmp, _mm_setzero_si128()); + count += tmp_count; + tmp_count = 0; + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + } + // tmp_count <= 8, so this does not affect efficiency so much + for (size_t i = 0; i < tmp_count; i++) { + A[count] = ((uint16_t *)tmp)[i]; + count++; + } + i_a += tmp_count; // We can at least jump pass $tmp_count elements in A + } + // intersect the tail using scalar intersection + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (a < b) { + i_a++; + } else if (b < a) { + i_b++; + } else { + A[count] = a; //==b; + count++; + i_a++; + i_b++; + } + } + return (int32_t)count; +} +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +int32_t intersect_vector16_cardinality(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b) { + size_t count = 0; + size_t i_a = 0, i_b = 0; + const int vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + __m128i v_a, v_b; + if ((i_a < st_a) && (i_b < st_b)) { + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + while ((A[i_a] == 0) || (B[i_b] == 0)) { + const __m128i res_v = + _mm_cmpestrm(v_b, vectorlength, v_a, vectorlength, + _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + if ((i_a < st_a) && (i_b < st_b)) + while (true) { + const __m128i res_v = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + const int r = _mm_extract_epi32(res_v, 0); + count += _mm_popcnt_u32(r); + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + i_a += vectorlength; + if (i_a == st_a) break; + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + } + // intersect the tail using scalar intersection + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (a < b) { + i_a++; + } else if (b < a) { + i_b++; + } else { + count++; + i_a++; + i_b++; + } + } + return (int32_t)count; +} +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +///////// +// Warning: +// This function may not be safe if A == C or B == C. +///////// +int32_t difference_vector16(const uint16_t *__restrict__ A, size_t s_a, + const uint16_t *__restrict__ B, size_t s_b, uint16_t *C) { + // we handle the degenerate case + if (s_a == 0) return 0; + if (s_b == 0) { + if (A != C) memcpy(C, A, sizeof(uint16_t) * s_a); + return (int32_t)s_a; + } + // handle the leading zeroes, it is messy but it allows us to use the fast + // _mm_cmpistrm instrinsic safely + int32_t count = 0; + if ((A[0] == 0) || (B[0] == 0)) { + if ((A[0] == 0) && (B[0] == 0)) { + A++; + s_a--; + B++; + s_b--; + } else if (A[0] == 0) { + C[count++] = 0; + A++; + s_a--; + } else { + B++; + s_b--; + } + } + // at this point, we have two non-empty arrays, made of non-zero + // increasing values. + size_t i_a = 0, i_b = 0; + const size_t vectorlength = sizeof(__m128i) / sizeof(uint16_t); + const size_t st_a = (s_a / vectorlength) * vectorlength; + const size_t st_b = (s_b / vectorlength) * vectorlength; + if ((i_a < st_a) && (i_b < st_b)) { // this is the vectorized code path + __m128i v_a, v_b; //, v_bmax; + // we load a vector from A and a vector from B + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + // we have a runningmask which indicates which values from A have been + // spotted in B, these don't get written out. + __m128i runningmask_a_found_in_b = _mm_setzero_si128(); + /**** + * start of the main vectorized loop + *****/ + while (true) { + // afoundinb will contain a mask indicate for each entry in A + // whether it is seen + // in B + const __m128i a_found_in_b = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + runningmask_a_found_in_b = _mm_or_si128(runningmask_a_found_in_b, a_found_in_b); + // we always compare the last values of A and B + const uint16_t a_max = A[i_a + vectorlength - 1]; + const uint16_t b_max = B[i_b + vectorlength - 1]; + if (a_max <= b_max) { + // Ok. In this code path, we are ready to write our v_a + // because there is no need to read more from B, they will + // all be large values. + const int bitmask_belongs_to_difference = + _mm_extract_epi32(runningmask_a_found_in_b, 0) ^ 0xFF; + /*** next few lines are probably expensive *****/ + __m128i sm16 = _mm_loadu_si128((const __m128i *)shuffle_mask16 + + bitmask_belongs_to_difference); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(bitmask_belongs_to_difference); + // we advance a + i_a += vectorlength; + if (i_a == st_a) // no more + break; + runningmask_a_found_in_b = _mm_setzero_si128(); + v_a = _mm_lddqu_si128((__m128i *)&A[i_a]); + } + if (b_max <= a_max) { + // in this code path, the current v_b has become useless + i_b += vectorlength; + if (i_b == st_b) break; + v_b = _mm_lddqu_si128((__m128i *)&B[i_b]); + } + } + // at this point, either we have i_a == st_a, which is the end of the + // vectorized processing, + // or we have i_b == st_b, and we are not done processing the vector... + // so we need to finish it off. + if (i_a < st_a) { // we have unfinished business... + uint16_t buffer[8]; // buffer to do a masked load + memset(buffer, 0, 8 * sizeof(uint16_t)); + memcpy(buffer, B + i_b, (s_b - i_b) * sizeof(uint16_t)); + v_b = _mm_lddqu_si128((__m128i *)buffer); + const __m128i a_found_in_b = + _mm_cmpistrm(v_b, v_a, _SIDD_UWORD_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); + runningmask_a_found_in_b = _mm_or_si128(runningmask_a_found_in_b, a_found_in_b); + const int bitmask_belongs_to_difference = + _mm_extract_epi32(runningmask_a_found_in_b, 0) ^ 0xFF; + __m128i sm16 = + _mm_loadu_si128((const __m128i *)shuffle_mask16 + bitmask_belongs_to_difference); + __m128i p = _mm_shuffle_epi8(v_a, sm16); + _mm_storeu_si128((__m128i *)&C[count], p); // can overflow + count += _mm_popcnt_u32(bitmask_belongs_to_difference); + i_a += vectorlength; + } + // at this point we should have i_a == st_a and i_b == st_b + } + // do the tail using scalar code + while (i_a < s_a && i_b < s_b) { + uint16_t a = A[i_a]; + uint16_t b = B[i_b]; + if (b < a) { + i_b++; + } else if (a < b) { + C[count] = a; + count++; + i_a++; + } else { //== + i_a++; + i_b++; + } + } + if (i_a < s_a) { + if (C == A) { + assert((size_t)count <= i_a); + if ((size_t)count < i_a) { + memmove(C + count, A + i_a, sizeof(uint16_t) * (s_a - i_a)); + } + } else { + for (size_t i = 0; i < (s_a - i_a); i++) { + C[count + i] = A[i + i_a]; + } + } + count += (int32_t)(s_a - i_a); + } + return count; +} +CROARING_UNTARGET_AVX2 +#endif // CROARING_IS_X64 + +/** + * Branchless binary search going after 4 values at once. + * Assumes that array is sorted. + * You have that array[*index1] >= target1, array[*index12] >= target2, ... + * except when *index1 = n, in which case you know that all values in array are + * smaller than target1, and so forth. + * It has logarithmic complexity. + */ +static void binarySearch4(const uint16_t *array, int32_t n, uint16_t target1, uint16_t target2, + uint16_t target3, uint16_t target4, int32_t *index1, int32_t *index2, + int32_t *index3, int32_t *index4) { + const uint16_t *base1 = array; + const uint16_t *base2 = array; + const uint16_t *base3 = array; + const uint16_t *base4 = array; + if (n == 0) return; + while (n > 1) { + int32_t half = n >> 1; + base1 = (base1[half] < target1) ? &base1[half] : base1; + base2 = (base2[half] < target2) ? &base2[half] : base2; + base3 = (base3[half] < target3) ? &base3[half] : base3; + base4 = (base4[half] < target4) ? &base4[half] : base4; + n -= half; + } + *index1 = (int32_t)((*base1 < target1) + base1 - array); + *index2 = (int32_t)((*base2 < target2) + base2 - array); + *index3 = (int32_t)((*base3 < target3) + base3 - array); + *index4 = (int32_t)((*base4 < target4) + base4 - array); +} + +/** + * Branchless binary search going after 2 values at once. + * Assumes that array is sorted. + * You have that array[*index1] >= target1, array[*index12] >= target2. + * except when *index1 = n, in which case you know that all values in array are + * smaller than target1, and so forth. + * It has logarithmic complexity. + */ +static void binarySearch2(const uint16_t *array, int32_t n, uint16_t target1, uint16_t target2, + int32_t *index1, int32_t *index2) { + const uint16_t *base1 = array; + const uint16_t *base2 = array; + if (n == 0) return; + while (n > 1) { + int32_t half = n >> 1; + base1 = (base1[half] < target1) ? &base1[half] : base1; + base2 = (base2[half] < target2) ? &base2[half] : base2; + n -= half; + } + *index1 = (int32_t)((*base1 < target1) + base1 - array); + *index2 = (int32_t)((*base2 < target2) + base2 - array); +} + +/* Computes the intersection between one small and one large set of uint16_t. + * Stores the result into buffer and return the number of elements. + * Processes the small set in blocks of 4 values calling binarySearch4 + * and binarySearch2. This approach can be slightly superior to a conventional + * galloping search in some instances. + */ +int32_t intersect_skewed_uint16(const uint16_t *small, size_t size_s, const uint16_t *large, + size_t size_l, uint16_t *buffer) { + size_t pos = 0, idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return 0; + } + int32_t index1 = 0, index2 = 0, index3 = 0, index4 = 0; + while ((idx_s + 4 <= size_s) && (idx_l < size_l)) { + uint16_t target1 = small[idx_s]; + uint16_t target2 = small[idx_s + 1]; + uint16_t target3 = small[idx_s + 2]; + uint16_t target4 = small[idx_s + 3]; + binarySearch4(large + idx_l, (int32_t)(size_l - idx_l), target1, target2, target3, target4, + &index1, &index2, &index3, &index4); + if ((index1 + idx_l < size_l) && (large[idx_l + index1] == target1)) { + buffer[pos++] = target1; + } + if ((index2 + idx_l < size_l) && (large[idx_l + index2] == target2)) { + buffer[pos++] = target2; + } + if ((index3 + idx_l < size_l) && (large[idx_l + index3] == target3)) { + buffer[pos++] = target3; + } + if ((index4 + idx_l < size_l) && (large[idx_l + index4] == target4)) { + buffer[pos++] = target4; + } + idx_s += 4; + idx_l += index4; + } + if ((idx_s + 2 <= size_s) && (idx_l < size_l)) { + uint16_t target1 = small[idx_s]; + uint16_t target2 = small[idx_s + 1]; + binarySearch2(large + idx_l, (int32_t)(size_l - idx_l), target1, target2, &index1, &index2); + if ((index1 + idx_l < size_l) && (large[idx_l + index1] == target1)) { + buffer[pos++] = target1; + } + if ((index2 + idx_l < size_l) && (large[idx_l + index2] == target2)) { + buffer[pos++] = target2; + } + idx_s += 2; + idx_l += index2; + } + if ((idx_s < size_s) && (idx_l < size_l)) { + uint16_t val_s = small[idx_s]; + int32_t index = binarySearch(large + idx_l, (int32_t)(size_l - idx_l), val_s); + if (index >= 0) buffer[pos++] = val_s; + } + return (int32_t)pos; +} + +// TODO: this could be accelerated, possibly, by using binarySearch4 as above. +int32_t intersect_skewed_uint16_cardinality(const uint16_t *small, size_t size_s, + const uint16_t *large, size_t size_l) { + size_t pos = 0, idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return 0; + } + + uint16_t val_l = large[idx_l], val_s = small[idx_s]; + + while (true) { + if (val_l < val_s) { + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } else if (val_s < val_l) { + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + } else { + pos++; + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } + } + + return (int32_t)pos; +} + +bool intersect_skewed_uint16_nonempty(const uint16_t *small, size_t size_s, const uint16_t *large, + size_t size_l) { + size_t idx_l = 0, idx_s = 0; + + if (0 == size_s) { + return false; + } + + uint16_t val_l = large[idx_l], val_s = small[idx_s]; + + while (true) { + if (val_l < val_s) { + idx_l = advanceUntil(large, (int32_t)idx_l, (int32_t)size_l, val_s); + if (idx_l == size_l) break; + val_l = large[idx_l]; + } else if (val_s < val_l) { + idx_s++; + if (idx_s == size_s) break; + val_s = small[idx_s]; + } else { + return true; + } + } + + return false; +} + +/** + * Generic intersection function. + */ +int32_t intersect_uint16(const uint16_t *A, const size_t lenA, const uint16_t *B, const size_t lenB, + uint16_t *out) { + const uint16_t *initout = out; + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return (int32_t)(out - initout); + } + while (*A > *B) { + if (++B == endB) return (int32_t)(out - initout); + } + if (*A == *B) { + *out++ = *A; + if (++A == endA || ++B == endB) return (int32_t)(out - initout); + } else { + goto SKIP_FIRST_COMPARE; + } + } + // return (int32_t)(out - initout); // NOTREACHED +} + +int32_t intersect_uint16_cardinality(const uint16_t *A, const size_t lenA, const uint16_t *B, + const size_t lenB) { + int32_t answer = 0; + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return answer; + } + while (*A > *B) { + if (++B == endB) return answer; + } + if (*A == *B) { + ++answer; + if (++A == endA || ++B == endB) return answer; + } else { + goto SKIP_FIRST_COMPARE; + } + } + // return answer; // NOTREACHED +} + +bool intersect_uint16_nonempty(const uint16_t *A, const size_t lenA, const uint16_t *B, + const size_t lenB) { + if (lenA == 0 || lenB == 0) return 0; + const uint16_t *endA = A + lenA; + const uint16_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return false; + } + while (*A > *B) { + if (++B == endB) return false; + } + if (*A == *B) { + return true; + } else { + goto SKIP_FIRST_COMPARE; + } + } + return false; // NOTREACHED +} + +/** + * Generic intersection function. + */ +size_t intersection_uint32(const uint32_t *A, const size_t lenA, const uint32_t *B, + const size_t lenB, uint32_t *out) { + const uint32_t *initout = out; + if (lenA == 0 || lenB == 0) return 0; + const uint32_t *endA = A + lenA; + const uint32_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return (out - initout); + } + while (*A > *B) { + if (++B == endB) return (out - initout); + } + if (*A == *B) { + *out++ = *A; + if (++A == endA || ++B == endB) return (out - initout); + } else { + goto SKIP_FIRST_COMPARE; + } + } + // return (out - initout); // NOTREACHED +} + +size_t intersection_uint32_card(const uint32_t *A, const size_t lenA, const uint32_t *B, + const size_t lenB) { + if (lenA == 0 || lenB == 0) return 0; + size_t card = 0; + const uint32_t *endA = A + lenA; + const uint32_t *endB = B + lenB; + + while (1) { + while (*A < *B) { + SKIP_FIRST_COMPARE: + if (++A == endA) return card; + } + while (*A > *B) { + if (++B == endB) return card; + } + if (*A == *B) { + card++; + if (++A == endA || ++B == endB) return card; + } else { + goto SKIP_FIRST_COMPARE; + } + } + // return card; // NOTREACHED +} + +// can one vectorize the computation of the union? (Update: Yes! See +// union_vector16). + +size_t union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, size_t size_2, + uint16_t *buffer) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + memmove(buffer, set_1, size_1 * sizeof(uint16_t)); + return size_1; + } + if (0 == size_1) { + memmove(buffer, set_2, size_2 * sizeof(uint16_t)); + return size_2; + } + + uint16_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + buffer[pos++] = val_1; + ++idx_1; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + buffer[pos++] = val_2; + ++idx_2; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + buffer[pos++] = val_1; + ++idx_1; + ++idx_2; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + memmove(buffer + pos, set_1 + idx_1, n_elems * sizeof(uint16_t)); + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + memmove(buffer + pos, set_2 + idx_2, n_elems * sizeof(uint16_t)); + pos += n_elems; + } + + return pos; +} + +int difference_uint16(const uint16_t *a1, int length1, const uint16_t *a2, int length2, + uint16_t *a_out) { + int out_card = 0; + int k1 = 0, k2 = 0; + if (length1 == 0) return 0; + if (length2 == 0) { + if (a1 != a_out) memcpy(a_out, a1, sizeof(uint16_t) * length1); + return length1; + } + uint16_t s1 = a1[k1]; + uint16_t s2 = a2[k2]; + while (true) { + if (s1 < s2) { + a_out[out_card++] = s1; + ++k1; + if (k1 >= length1) { + break; + } + s1 = a1[k1]; + } else if (s1 == s2) { + ++k1; + ++k2; + if (k1 >= length1) { + break; + } + if (k2 >= length2) { + memmove(a_out + out_card, a1 + k1, sizeof(uint16_t) * (length1 - k1)); + return out_card + length1 - k1; + } + s1 = a1[k1]; + s2 = a2[k2]; + } else { // if (val1>val2) + ++k2; + if (k2 >= length2) { + memmove(a_out + out_card, a1 + k1, sizeof(uint16_t) * (length1 - k1)); + return out_card + length1 - k1; + } + s2 = a2[k2]; + } + } + return out_card; +} + +int32_t xor_uint16(const uint16_t *array_1, int32_t card_1, const uint16_t *array_2, int32_t card_2, + uint16_t *out) { + int32_t pos1 = 0, pos2 = 0, pos_out = 0; + while (pos1 < card_1 && pos2 < card_2) { + const uint16_t v1 = array_1[pos1]; + const uint16_t v2 = array_2[pos2]; + if (v1 == v2) { + ++pos1; + ++pos2; + continue; + } + if (v1 < v2) { + out[pos_out++] = v1; + ++pos1; + } else { + out[pos_out++] = v2; + ++pos2; + } + } + if (pos1 < card_1) { + const size_t n_elems = card_1 - pos1; + memcpy(out + pos_out, array_1 + pos1, n_elems * sizeof(uint16_t)); + pos_out += (int32_t)n_elems; + } else if (pos2 < card_2) { + const size_t n_elems = card_2 - pos2; + memcpy(out + pos_out, array_2 + pos2, n_elems * sizeof(uint16_t)); + pos_out += (int32_t)n_elems; + } + return pos_out; +} + +#if CROARING_IS_X64 + +/*** + * start of the SIMD 16-bit union code + * + */ +CROARING_TARGET_AVX2 + +// Assuming that vInput1 and vInput2 are sorted, produces a sorted output going +// from vecMin all the way to vecMax +// developed originally for merge sort using SIMD instructions. +// Standard merge. See, e.g., Inoue and Taura, SIMD- and Cache-Friendly +// Algorithm for Sorting an Array of Structures +static inline void sse_merge(const __m128i *vInput1, + const __m128i *vInput2, // input 1 & 2 + __m128i *vecMin, __m128i *vecMax) { // output + __m128i vecTmp; + vecTmp = _mm_min_epu16(*vInput1, *vInput2); + *vecMax = _mm_max_epu16(*vInput1, *vInput2); + vecTmp = _mm_alignr_epi8(vecTmp, vecTmp, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + vecTmp = _mm_alignr_epi8(*vecMin, *vecMin, 2); + *vecMin = _mm_min_epu16(vecTmp, *vecMax); + *vecMax = _mm_max_epu16(vecTmp, *vecMax); + *vecMin = _mm_alignr_epi8(*vecMin, *vecMin, 2); +} +CROARING_UNTARGET_AVX2 +// used by store_unique, generated by simdunion.py +static uint8_t uniqshuf[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xc, 0xd, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xa, 0xb, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xe, 0xf, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xa, 0xb, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xc, 0xd, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xa, 0xb, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x8, 0x9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x6, 0x7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x4, 0x5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0x2, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x2, 0x3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +CROARING_TARGET_AVX2 +// write vector new, while omitting repeated values assuming that previously +// written vector was "old" +static inline int store_unique(__m128i old, __m128i newval, uint16_t *output) { + __m128i vecTmp = _mm_alignr_epi8(newval, old, 16 - 2); + // lots of high latency instructions follow (optimize?) + int M = + _mm_movemask_epi8(_mm_packs_epi16(_mm_cmpeq_epi16(vecTmp, newval), _mm_setzero_si128())); + int numberofnewvalues = 8 - _mm_popcnt_u32(M); + __m128i key = _mm_lddqu_si128((const __m128i *)uniqshuf + M); + __m128i val = _mm_shuffle_epi8(newval, key); + _mm_storeu_si128((__m128i *)output, val); + return numberofnewvalues; +} +CROARING_UNTARGET_AVX2 + +// working in-place, this function overwrites the repeated values +// could be avoided? +static inline uint32_t unique(uint16_t *out, uint32_t len) { + uint32_t pos = 1; + for (uint32_t i = 1; i < len; ++i) { + if (out[i] != out[i - 1]) { + out[pos++] = out[i]; + } + } + return pos; +} + +// use with qsort, could be avoided +static int uint16_compare(const void *a, const void *b) { + return (*(uint16_t *)a - *(uint16_t *)b); +} + +CROARING_TARGET_AVX2 +// a one-pass SSE union algorithm +// This function may not be safe if array1 == output or array2 == output. +uint32_t union_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output) { + if ((length1 < 8) || (length2 < 8)) { + return (uint32_t)union_uint16(array1, length1, array2, length2, output); + } + __m128i vA, vB, V, vecMin, vecMax; + __m128i laststore; + uint16_t *initoutput = output; + uint32_t len1 = length1 / 8; + uint32_t len2 = length2 / 8; + uint32_t pos1 = 0; + uint32_t pos2 = 0; + // we start the machine + vA = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + vB = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + sse_merge(&vA, &vB, &vecMin, &vecMax); + laststore = _mm_set1_epi16(-1); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + if ((pos1 < len1) && (pos2 < len2)) { + uint16_t curA, curB; + curA = array1[8 * pos1]; + curB = array2[8 * pos2]; + while (true) { + if (curA <= curB) { + V = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + if (pos1 < len1) { + curA = array1[8 * pos1]; + } else { + break; + } + } else { + V = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + if (pos2 < len2) { + curB = array2[8 * pos2]; + } else { + break; + } + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + output += store_unique(laststore, vecMin, output); + laststore = vecMin; + } + // we finish the rest off using a scalar algorithm + // could be improved? + // + // copy the small end on a tmp buffer + uint32_t len = (uint32_t)(output - initoutput); + uint16_t buffer[16]; + uint32_t leftoversize = store_unique(laststore, vecMax, buffer); + if (pos1 == len1) { + memcpy(buffer + leftoversize, array1 + 8 * pos1, (length1 - 8 * len1) * sizeof(uint16_t)); + leftoversize += length1 - 8 * len1; + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + + leftoversize = unique(buffer, leftoversize); + len += (uint32_t)union_uint16(buffer, leftoversize, array2 + 8 * pos2, length2 - 8 * pos2, + output); + } else { + memcpy(buffer + leftoversize, array2 + 8 * pos2, (length2 - 8 * len2) * sizeof(uint16_t)); + leftoversize += length2 - 8 * len2; + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique(buffer, leftoversize); + len += (uint32_t)union_uint16(buffer, leftoversize, array1 + 8 * pos1, length1 - 8 * pos1, + output); + } + return len; +} +CROARING_UNTARGET_AVX2 + +/** + * End of the SIMD 16-bit union code + * + */ + +/** + * Start of SIMD 16-bit XOR code + */ + +CROARING_TARGET_AVX2 +// write vector new, while omitting repeated values assuming that previously +// written vector was "old" +static inline int store_unique_xor(__m128i old, __m128i newval, uint16_t *output) { + __m128i vecTmp1 = _mm_alignr_epi8(newval, old, 16 - 4); + __m128i vecTmp2 = _mm_alignr_epi8(newval, old, 16 - 2); + __m128i equalleft = _mm_cmpeq_epi16(vecTmp2, vecTmp1); + __m128i equalright = _mm_cmpeq_epi16(vecTmp2, newval); + __m128i equalleftoright = _mm_or_si128(equalleft, equalright); + int M = _mm_movemask_epi8(_mm_packs_epi16(equalleftoright, _mm_setzero_si128())); + int numberofnewvalues = 8 - _mm_popcnt_u32(M); + __m128i key = _mm_lddqu_si128((const __m128i *)uniqshuf + M); + __m128i val = _mm_shuffle_epi8(vecTmp2, key); + _mm_storeu_si128((__m128i *)output, val); + return numberofnewvalues; +} +CROARING_UNTARGET_AVX2 + +// working in-place, this function overwrites the repeated values +// could be avoided? Warning: assumes len > 0 +static inline uint32_t unique_xor(uint16_t *out, uint32_t len) { + uint32_t pos = 1; + for (uint32_t i = 1; i < len; ++i) { + if (out[i] != out[i - 1]) { + out[pos++] = out[i]; + } else + pos--; // if it is identical to previous, delete it + } + return pos; +} +CROARING_TARGET_AVX2 +// a one-pass SSE xor algorithm +uint32_t xor_vector16(const uint16_t *__restrict__ array1, uint32_t length1, + const uint16_t *__restrict__ array2, uint32_t length2, + uint16_t *__restrict__ output) { + if ((length1 < 8) || (length2 < 8)) { + return xor_uint16(array1, length1, array2, length2, output); + } + __m128i vA, vB, V, vecMin, vecMax; + __m128i laststore; + uint16_t *initoutput = output; + uint32_t len1 = length1 / 8; + uint32_t len2 = length2 / 8; + uint32_t pos1 = 0; + uint32_t pos2 = 0; + // we start the machine + vA = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + vB = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + sse_merge(&vA, &vB, &vecMin, &vecMax); + laststore = _mm_set1_epi16(-1); + uint16_t buffer[17]; + output += store_unique_xor(laststore, vecMin, output); + + laststore = vecMin; + if ((pos1 < len1) && (pos2 < len2)) { + uint16_t curA, curB; + curA = array1[8 * pos1]; + curB = array2[8 * pos2]; + while (true) { + if (curA <= curB) { + V = _mm_lddqu_si128((const __m128i *)array1 + pos1); + pos1++; + if (pos1 < len1) { + curA = array1[8 * pos1]; + } else { + break; + } + } else { + V = _mm_lddqu_si128((const __m128i *)array2 + pos2); + pos2++; + if (pos2 < len2) { + curB = array2[8 * pos2]; + } else { + break; + } + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + // conditionally stores the last value of laststore as well as all + // but the + // last value of vecMin + output += store_unique_xor(laststore, vecMin, output); + laststore = vecMin; + } + sse_merge(&V, &vecMax, &vecMin, &vecMax); + // conditionally stores the last value of laststore as well as all but + // the + // last value of vecMin + output += store_unique_xor(laststore, vecMin, output); + laststore = vecMin; + } + uint32_t len = (uint32_t)(output - initoutput); + + // we finish the rest off using a scalar algorithm + // could be improved? + // conditionally stores the last value of laststore as well as all but the + // last value of vecMax, + // we store to "buffer" + int leftoversize = store_unique_xor(laststore, vecMax, buffer); + uint16_t vec7 = (uint16_t)_mm_extract_epi16(vecMax, 7); + uint16_t vec6 = (uint16_t)_mm_extract_epi16(vecMax, 6); + if (vec7 != vec6) buffer[leftoversize++] = vec7; + if (pos1 == len1) { + memcpy(buffer + leftoversize, array1 + 8 * pos1, (length1 - 8 * len1) * sizeof(uint16_t)); + leftoversize += length1 - 8 * len1; + if (leftoversize == 0) { // trivial case + memcpy(output, array2 + 8 * pos2, (length2 - 8 * pos2) * sizeof(uint16_t)); + len += (length2 - 8 * pos2); + } else { + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique_xor(buffer, leftoversize); + len += xor_uint16(buffer, leftoversize, array2 + 8 * pos2, length2 - 8 * pos2, output); + } + } else { + memcpy(buffer + leftoversize, array2 + 8 * pos2, (length2 - 8 * len2) * sizeof(uint16_t)); + leftoversize += length2 - 8 * len2; + if (leftoversize == 0) { // trivial case + memcpy(output, array1 + 8 * pos1, (length1 - 8 * pos1) * sizeof(uint16_t)); + len += (length1 - 8 * pos1); + } else { + qsort(buffer, leftoversize, sizeof(uint16_t), uint16_compare); + leftoversize = unique_xor(buffer, leftoversize); + len += xor_uint16(buffer, leftoversize, array1 + 8 * pos1, length1 - 8 * pos1, output); + } + } + return len; +} +CROARING_UNTARGET_AVX2 +/** + * End of SIMD 16-bit XOR code + */ + +#endif // CROARING_IS_X64 + +size_t union_uint32(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, size_t size_2, + uint32_t *buffer) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + memmove(buffer, set_1, size_1 * sizeof(uint32_t)); + return size_1; + } + if (0 == size_1) { + memmove(buffer, set_2, size_2 * sizeof(uint32_t)); + return size_2; + } + + uint32_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + buffer[pos++] = val_1; + ++idx_1; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + buffer[pos++] = val_2; + ++idx_2; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + buffer[pos++] = val_1; + ++idx_1; + ++idx_2; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + memmove(buffer + pos, set_1 + idx_1, n_elems * sizeof(uint32_t)); + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + memmove(buffer + pos, set_2 + idx_2, n_elems * sizeof(uint32_t)); + pos += n_elems; + } + + return pos; +} + +size_t union_uint32_card(const uint32_t *set_1, size_t size_1, const uint32_t *set_2, + size_t size_2) { + size_t pos = 0, idx_1 = 0, idx_2 = 0; + + if (0 == size_2) { + return size_1; + } + if (0 == size_1) { + return size_2; + } + + uint32_t val_1 = set_1[idx_1], val_2 = set_2[idx_2]; + + while (true) { + if (val_1 < val_2) { + ++idx_1; + ++pos; + if (idx_1 >= size_1) break; + val_1 = set_1[idx_1]; + } else if (val_2 < val_1) { + ++idx_2; + ++pos; + if (idx_2 >= size_2) break; + val_2 = set_2[idx_2]; + } else { + ++idx_1; + ++idx_2; + ++pos; + if (idx_1 >= size_1 || idx_2 >= size_2) break; + val_1 = set_1[idx_1]; + val_2 = set_2[idx_2]; + } + } + + if (idx_1 < size_1) { + const size_t n_elems = size_1 - idx_1; + pos += n_elems; + } else if (idx_2 < size_2) { + const size_t n_elems = size_2 - idx_2; + pos += n_elems; + } + return pos; +} + +size_t fast_union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2, size_t size_2, + uint16_t *buffer) { +#if CROARING_IS_X64 + if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + // compute union with smallest array first + if (size_1 < size_2) { + return union_vector16(set_1, (uint32_t)size_1, set_2, (uint32_t)size_2, buffer); + } else { + return union_vector16(set_2, (uint32_t)size_2, set_1, (uint32_t)size_1, buffer); + } + } else { + // compute union with smallest array first + if (size_1 < size_2) { + return union_uint16(set_1, size_1, set_2, size_2, buffer); + } else { + return union_uint16(set_2, size_2, set_1, size_1, buffer); + } + } +#else + // compute union with smallest array first + if (size_1 < size_2) { + return union_uint16(set_1, size_1, set_2, size_2, buffer); + } else { + return union_uint16(set_2, size_2, set_1, size_1, buffer); + } +#endif +} +#if CROARING_IS_X64 +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +static inline bool _avx512_memequals(const void *s1, const void *s2, size_t n) { + const uint8_t *ptr1 = (const uint8_t *)s1; + const uint8_t *ptr2 = (const uint8_t *)s2; + const uint8_t *end1 = ptr1 + n; + const uint8_t *end8 = ptr1 + ((n >> 3) << 3); + const uint8_t *end32 = ptr1 + ((n >> 5) << 5); + const uint8_t *end64 = ptr1 + ((n >> 6) << 6); + + while (ptr1 < end64) { + __m512i r1 = _mm512_loadu_si512((const __m512i *)ptr1); + __m512i r2 = _mm512_loadu_si512((const __m512i *)ptr2); + + uint64_t mask = _mm512_cmpeq_epi8_mask(r1, r2); + + if (mask != UINT64_MAX) { + return false; + } + + ptr1 += 64; + ptr2 += 64; + } + + while (ptr1 < end32) { + __m256i r1 = _mm256_loadu_si256((const __m256i *)ptr1); + __m256i r2 = _mm256_loadu_si256((const __m256i *)ptr2); + int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(r1, r2)); + if ((uint32_t)mask != UINT32_MAX) { + return false; + } + ptr1 += 32; + ptr2 += 32; + } + + while (ptr1 < end8) { + uint64_t v1, v2; + memcpy(&v1, ptr1, sizeof(uint64_t)); + memcpy(&v2, ptr2, sizeof(uint64_t)); + if (v1 != v2) { + return false; + } + ptr1 += 8; + ptr2 += 8; + } + + while (ptr1 < end1) { + if (*ptr1 != *ptr2) { + return false; + } + ptr1++; + ptr2++; + } + + return true; +} +CROARING_UNTARGET_AVX512 +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + +CROARING_TARGET_AVX2 +static inline bool _avx2_memequals(const void *s1, const void *s2, size_t n) { + const uint8_t *ptr1 = (const uint8_t *)s1; + const uint8_t *ptr2 = (const uint8_t *)s2; + const uint8_t *end1 = ptr1 + n; + const uint8_t *end8 = ptr1 + n / 8 * 8; + const uint8_t *end32 = ptr1 + n / 32 * 32; + + while (ptr1 < end32) { + __m256i r1 = _mm256_loadu_si256((const __m256i *)ptr1); + __m256i r2 = _mm256_loadu_si256((const __m256i *)ptr2); + int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(r1, r2)); + if ((uint32_t)mask != UINT32_MAX) { + return false; + } + ptr1 += 32; + ptr2 += 32; + } + + while (ptr1 < end8) { + uint64_t v1, v2; + memcpy(&v1, ptr1, sizeof(uint64_t)); + memcpy(&v2, ptr2, sizeof(uint64_t)); + if (v1 != v2) { + return false; + } + ptr1 += 8; + ptr2 += 8; + } + + while (ptr1 < end1) { + if (*ptr1 != *ptr2) { + return false; + } + ptr1++; + ptr2++; + } + + return true; +} +CROARING_UNTARGET_AVX2 +#endif + +bool memequals(const void *s1, const void *s2, size_t n) { + if (n == 0) { + return true; + } +#if CROARING_IS_X64 + int support = croaring_hardware_support(); +#if CROARING_COMPILER_SUPPORTS_AVX512 + if (support & ROARING_SUPPORTS_AVX512) { + return _avx512_memequals(s1, s2, n); + } else +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + if (support & ROARING_SUPPORTS_AVX2) { + return _avx2_memequals(s1, s2, n); + } else { + return memcmp(s1, s2, n) == 0; + } +#else + return memcmp(s1, s2, n) == 0; +#endif +} + +#if CROARING_IS_X64 +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +ALLOW_UNALIGNED +int avx512_array_container_to_uint32_array(void *vout, const uint16_t *array, size_t cardinality, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + size_t i = 0; + for (; i + sizeof(__m256i) / sizeof(uint16_t) <= cardinality; + i += sizeof(__m256i) / sizeof(uint16_t)) { + __m256i vinput = _mm256_loadu_si256((const __m256i *)(array + i)); + __m512i voutput = _mm512_add_epi32(_mm512_cvtepu16_epi32(vinput), _mm512_set1_epi32(base)); + _mm512_storeu_si512((__m512i *)(out + outpos), voutput); + outpos += sizeof(__m512i) / sizeof(uint32_t); + } + for (; i < cardinality; ++i) { + const uint32_t val = base + array[i]; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + return outpos; +} +CROARING_UNTARGET_AVX512 +#endif // #if CROARING_COMPILER_SUPPORTS_AVX512 +#endif // #if CROARING_IS_X64 + +} // namespace internal +} // namespace paimon::roaring +/* end file src/array_util.c */ +/* begin file src/bitset.c */ +#include +#include +#include +#include +#include + +namespace paimon::roaring { +namespace internal { + +inline void bitset_print(const bitset_t *b); +inline bool bitset_for_each(const bitset_t *b, bitset_iterator iterator, void *ptr); +inline size_t bitset_next_set_bits(const bitset_t *bitset, size_t *buffer, size_t capacity, + size_t *startfrom); +inline void bitset_set_to_value(bitset_t *bitset, size_t i, bool flag); +inline bool bitset_next_set_bit(const bitset_t *bitset, size_t *i); +inline void bitset_set(bitset_t *bitset, size_t i); +inline bool bitset_get(const bitset_t *bitset, size_t i); +inline size_t bitset_size_in_words(const bitset_t *bitset); +inline size_t bitset_size_in_bits(const bitset_t *bitset); +inline size_t bitset_size_in_bytes(const bitset_t *bitset); + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_t *bitset_create(void) { + bitset_t *bitset = NULL; + /* Allocate the bitset itself. */ + if ((bitset = (bitset_t *)roaring_malloc(sizeof(bitset_t))) == NULL) { + return NULL; + } + bitset->array = NULL; + bitset->arraysize = 0; + bitset->capacity = 0; + return bitset; +} + +/* Create a new bitset able to contain size bits. Return NULL in case of + * failure. */ +bitset_t *bitset_create_with_capacity(size_t size) { + bitset_t *bitset = NULL; + /* Allocate the bitset itself. */ + if ((bitset = (bitset_t *)roaring_malloc(sizeof(bitset_t))) == NULL) { + return NULL; + } + bitset->arraysize = (size + sizeof(uint64_t) * 8 - 1) / (sizeof(uint64_t) * 8); + bitset->capacity = bitset->arraysize; + if ((bitset->array = (uint64_t *)roaring_calloc(bitset->arraysize, sizeof(uint64_t))) == NULL) { + roaring_free(bitset); + return NULL; + } + return bitset; +} + +/* Create a copy */ +bitset_t *bitset_copy(const bitset_t *bitset) { + bitset_t *copy = NULL; + /* Allocate the bitset itself. */ + if ((copy = (bitset_t *)roaring_malloc(sizeof(bitset_t))) == NULL) { + return NULL; + } + memcpy(copy, bitset, sizeof(bitset_t)); + copy->capacity = copy->arraysize; + if ((copy->array = (uint64_t *)roaring_malloc(sizeof(uint64_t) * bitset->arraysize)) == NULL) { + roaring_free(copy); + return NULL; + } + memcpy(copy->array, bitset->array, sizeof(uint64_t) * bitset->arraysize); + return copy; +} + +void bitset_clear(bitset_t *bitset) { + memset(bitset->array, 0, sizeof(uint64_t) * bitset->arraysize); +} + +void bitset_fill(bitset_t *bitset) { + memset(bitset->array, 0xff, sizeof(uint64_t) * bitset->arraysize); +} + +void bitset_shift_left(bitset_t *bitset, size_t s) { + size_t extra_words = s / 64; + int inword_shift = s % 64; + size_t as = bitset->arraysize; + if (inword_shift == 0) { + bitset_resize(bitset, as + extra_words, false); + // could be done with a memmove + for (size_t i = as + extra_words; i > extra_words; i--) { + bitset->array[i - 1] = bitset->array[i - 1 - extra_words]; + } + } else { + bitset_resize(bitset, as + extra_words + 1, true); + bitset->array[as + extra_words] = bitset->array[as - 1] >> (64 - inword_shift); + for (size_t i = as + extra_words; i >= extra_words + 2; i--) { + bitset->array[i - 1] = (bitset->array[i - 1 - extra_words] << inword_shift) | + (bitset->array[i - 2 - extra_words] >> (64 - inword_shift)); + } + bitset->array[extra_words] = bitset->array[0] << inword_shift; + } + for (size_t i = 0; i < extra_words; i++) { + bitset->array[i] = 0; + } +} + +void bitset_shift_right(bitset_t *bitset, size_t s) { + size_t extra_words = s / 64; + int inword_shift = s % 64; + size_t as = bitset->arraysize; + if (inword_shift == 0) { + // could be done with a memmove + for (size_t i = 0; i < as - extra_words; i++) { + bitset->array[i] = bitset->array[i + extra_words]; + } + bitset_resize(bitset, as - extra_words, false); + + } else { + for (size_t i = 0; i + extra_words + 1 < as; i++) { + bitset->array[i] = (bitset->array[i + extra_words] >> inword_shift) | + (bitset->array[i + extra_words + 1] << (64 - inword_shift)); + } + bitset->array[as - extra_words - 1] = (bitset->array[as - 1] >> inword_shift); + bitset_resize(bitset, as - extra_words, false); + } +} + +/* Free memory. */ +void bitset_free(bitset_t *bitset) { + if (bitset == NULL) { + return; + } + roaring_free(bitset->array); + roaring_free(bitset); +} + +/* Resize the bitset so that it can support newarraysize * 64 bits. Return true + * in case of success, false for failure. */ +bool bitset_resize(bitset_t *bitset, size_t newarraysize, bool padwithzeroes) { + if (newarraysize > SIZE_MAX / 64) { + return false; + } + size_t smallest = newarraysize < bitset->arraysize ? newarraysize : bitset->arraysize; + if (bitset->capacity < newarraysize) { + uint64_t *newarray; + size_t newcapacity = bitset->capacity; + if (newcapacity == 0) { + newcapacity = 1; + } + while (newcapacity < newarraysize) { + newcapacity *= 2; + } + if ((newarray = (uint64_t *)roaring_realloc(bitset->array, + sizeof(uint64_t) * newcapacity)) == NULL) { + return false; + } + bitset->capacity = newcapacity; + bitset->array = newarray; + } + if (padwithzeroes && (newarraysize > smallest)) + memset(bitset->array + smallest, 0, sizeof(uint64_t) * (newarraysize - smallest)); + bitset->arraysize = newarraysize; + return true; // success! +} + +size_t bitset_count(const bitset_t *bitset) { + size_t card = 0; + size_t k = 0; + for (; k + 7 < bitset->arraysize; k += 8) { + card += roaring_hamming(bitset->array[k]); + card += roaring_hamming(bitset->array[k + 1]); + card += roaring_hamming(bitset->array[k + 2]); + card += roaring_hamming(bitset->array[k + 3]); + card += roaring_hamming(bitset->array[k + 4]); + card += roaring_hamming(bitset->array[k + 5]); + card += roaring_hamming(bitset->array[k + 6]); + card += roaring_hamming(bitset->array[k + 7]); + } + for (; k + 3 < bitset->arraysize; k += 4) { + card += roaring_hamming(bitset->array[k]); + card += roaring_hamming(bitset->array[k + 1]); + card += roaring_hamming(bitset->array[k + 2]); + card += roaring_hamming(bitset->array[k + 3]); + } + for (; k < bitset->arraysize; k++) { + card += roaring_hamming(bitset->array[k]); + } + return card; +} + +bool bitset_inplace_union(bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + for (size_t k = 0; k < minlength; ++k) { + b1->array[k] |= b2->array[k]; + } + if (b2->arraysize > b1->arraysize) { + size_t oldsize = b1->arraysize; + if (!bitset_resize(b1, b2->arraysize, false)) return false; + memcpy(b1->array + oldsize, b2->array + oldsize, + (b2->arraysize - oldsize) * sizeof(uint64_t)); + } + return true; +} + +size_t bitset_minimum(const bitset_t *bitset) { + for (size_t k = 0; k < bitset->arraysize; k++) { + uint64_t w = bitset->array[k]; + if (w != 0) { + return roaring_trailing_zeroes(w) + k * 64; + } + } + return 0; +} + +bool bitset_grow(bitset_t *bitset, size_t newarraysize) { + if (newarraysize < bitset->arraysize) { + return false; + } + if (newarraysize > SIZE_MAX / 64) { + return false; + } + if (bitset->capacity < newarraysize) { + uint64_t *newarray; + size_t newcapacity = + (UINT64_C(0xFFFFFFFFFFFFFFFF) >> roaring_leading_zeroes(newarraysize)) + 1; + while (newcapacity < newarraysize) { + newcapacity *= 2; + } + if ((newarray = (uint64_t *)roaring_realloc(bitset->array, + sizeof(uint64_t) * newcapacity)) == NULL) { + return false; + } + bitset->capacity = newcapacity; + bitset->array = newarray; + } + memset(bitset->array + bitset->arraysize, 0, + sizeof(uint64_t) * (newarraysize - bitset->arraysize)); + bitset->arraysize = newarraysize; + return true; // success! +} + +size_t bitset_maximum(const bitset_t *bitset) { + for (size_t k = bitset->arraysize; k > 0; k--) { + uint64_t w = bitset->array[k - 1]; + if (w != 0) { + return 63 - roaring_leading_zeroes(w) + (k - 1) * 64; + } + } + return 0; +} + +/* Returns true if bitsets share no common elements, false otherwise. + * + * Performs early-out if common element found. */ +bool bitsets_disjoint(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + + for (size_t k = 0; k < minlength; k++) { + if ((b1->array[k] & b2->array[k]) != 0) return false; + } + return true; +} + +/* Returns true if bitsets contain at least 1 common element, false if they are + * disjoint. + * + * Performs early-out if common element found. */ +bool bitsets_intersect(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + + for (size_t k = 0; k < minlength; k++) { + if ((b1->array[k] & b2->array[k]) != 0) return true; + } + return false; +} + +/* Returns true if b has any bits set in or after b->array[starting_loc]. */ +static bool any_bits_set(const bitset_t *b, size_t starting_loc) { + if (starting_loc >= b->arraysize) { + return false; + } + for (size_t k = starting_loc; k < b->arraysize; k++) { + if (b->array[k] != 0) return true; + } + return false; +} + +/* Returns true if b1 has all of b2's bits set. + * + * Performs early out if a bit is found in b2 that is not found in b1. */ +bool bitset_contains_all(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2) { + size_t min_size = b1->arraysize; + if (b1->arraysize > b2->arraysize) { + min_size = b2->arraysize; + } + for (size_t k = 0; k < min_size; k++) { + if ((b1->array[k] & b2->array[k]) != b2->array[k]) { + return false; + } + } + if (b2->arraysize > b1->arraysize) { + /* Need to check if b2 has any bits set beyond b1's array */ + return !any_bits_set(b2, b1->arraysize); + } + return true; +} + +size_t bitset_union_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t answer = 0; + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + for (; k + 3 < minlength; k += 4) { + answer += roaring_hamming(b1->array[k] | b2->array[k]); + answer += roaring_hamming(b1->array[k + 1] | b2->array[k + 1]); + answer += roaring_hamming(b1->array[k + 2] | b2->array[k + 2]); + answer += roaring_hamming(b1->array[k + 3] | b2->array[k + 3]); + } + for (; k < minlength; ++k) { + answer += roaring_hamming(b1->array[k] | b2->array[k]); + } + if (b2->arraysize > b1->arraysize) { + // k is equal to b1->arraysize + for (; k + 3 < b2->arraysize; k += 4) { + answer += roaring_hamming(b2->array[k]); + answer += roaring_hamming(b2->array[k + 1]); + answer += roaring_hamming(b2->array[k + 2]); + answer += roaring_hamming(b2->array[k + 3]); + } + for (; k < b2->arraysize; ++k) { + answer += roaring_hamming(b2->array[k]); + } + } else { + // k is equal to b2->arraysize + for (; k + 3 < b1->arraysize; k += 4) { + answer += roaring_hamming(b1->array[k]); + answer += roaring_hamming(b1->array[k + 1]); + answer += roaring_hamming(b1->array[k + 2]); + answer += roaring_hamming(b1->array[k + 3]); + } + for (; k < b1->arraysize; ++k) { + answer += roaring_hamming(b1->array[k]); + } + } + return answer; +} + +void bitset_inplace_intersection(bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + for (; k < minlength; ++k) { + b1->array[k] &= b2->array[k]; + } + for (; k < b1->arraysize; ++k) { + b1->array[k] = 0; // memset could, maybe, be a tiny bit faster + } +} + +size_t bitset_intersection_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t answer = 0; + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + for (size_t k = 0; k < minlength; ++k) { + answer += roaring_hamming(b1->array[k] & b2->array[k]); + } + return answer; +} + +void bitset_inplace_difference(bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + for (; k < minlength; ++k) { + b1->array[k] &= ~(b2->array[k]); + } +} + +size_t bitset_difference_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + size_t answer = 0; + for (; k < minlength; ++k) { + answer += roaring_hamming(b1->array[k] & ~(b2->array[k])); + } + for (; k < b1->arraysize; ++k) { + answer += roaring_hamming(b1->array[k]); + } + return answer; +} + +bool bitset_inplace_symmetric_difference(bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + for (; k < minlength; ++k) { + b1->array[k] ^= b2->array[k]; + } + if (b2->arraysize > b1->arraysize) { + size_t oldsize = b1->arraysize; + if (!bitset_resize(b1, b2->arraysize, false)) return false; + memcpy(b1->array + oldsize, b2->array + oldsize, + (b2->arraysize - oldsize) * sizeof(uint64_t)); + } + return true; +} + +size_t bitset_symmetric_difference_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2) { + size_t minlength = b1->arraysize < b2->arraysize ? b1->arraysize : b2->arraysize; + size_t k = 0; + size_t answer = 0; + for (; k < minlength; ++k) { + answer += roaring_hamming(b1->array[k] ^ b2->array[k]); + } + if (b2->arraysize > b1->arraysize) { + for (; k < b2->arraysize; ++k) { + answer += roaring_hamming(b2->array[k]); + } + } else { + for (; k < b1->arraysize; ++k) { + answer += roaring_hamming(b1->array[k]); + } + } + return answer; +} + +bool bitset_trim(bitset_t *bitset) { + size_t newsize = bitset->arraysize; + while (newsize > 0) { + if (bitset->array[newsize - 1] == 0) + newsize -= 1; + else + break; + } + if (bitset->capacity == newsize) return true; // nothing to do + uint64_t *newarray; + if ((newarray = (uint64_t *)roaring_realloc(bitset->array, sizeof(uint64_t) * newsize)) == + NULL) { + return false; + } + bitset->array = newarray; + bitset->capacity = newsize; + bitset->arraysize = newsize; + return true; +} + +} // namespace internal +} // namespace paimon::roaring +/* end file src/bitset.c */ +/* begin file src/bitset_util.c */ +#include +#include +#include +#include +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { +#if CROARING_IS_X64 +static uint8_t lengthTable[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; +#endif + +#if CROARING_IS_X64 +ALIGNED(32) +static uint32_t vecDecodeTable[256][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, /* 0x00 (00000000) */ + {1, 0, 0, 0, 0, 0, 0, 0}, /* 0x01 (00000001) */ + {2, 0, 0, 0, 0, 0, 0, 0}, /* 0x02 (00000010) */ + {1, 2, 0, 0, 0, 0, 0, 0}, /* 0x03 (00000011) */ + {3, 0, 0, 0, 0, 0, 0, 0}, /* 0x04 (00000100) */ + {1, 3, 0, 0, 0, 0, 0, 0}, /* 0x05 (00000101) */ + {2, 3, 0, 0, 0, 0, 0, 0}, /* 0x06 (00000110) */ + {1, 2, 3, 0, 0, 0, 0, 0}, /* 0x07 (00000111) */ + {4, 0, 0, 0, 0, 0, 0, 0}, /* 0x08 (00001000) */ + {1, 4, 0, 0, 0, 0, 0, 0}, /* 0x09 (00001001) */ + {2, 4, 0, 0, 0, 0, 0, 0}, /* 0x0A (00001010) */ + {1, 2, 4, 0, 0, 0, 0, 0}, /* 0x0B (00001011) */ + {3, 4, 0, 0, 0, 0, 0, 0}, /* 0x0C (00001100) */ + {1, 3, 4, 0, 0, 0, 0, 0}, /* 0x0D (00001101) */ + {2, 3, 4, 0, 0, 0, 0, 0}, /* 0x0E (00001110) */ + {1, 2, 3, 4, 0, 0, 0, 0}, /* 0x0F (00001111) */ + {5, 0, 0, 0, 0, 0, 0, 0}, /* 0x10 (00010000) */ + {1, 5, 0, 0, 0, 0, 0, 0}, /* 0x11 (00010001) */ + {2, 5, 0, 0, 0, 0, 0, 0}, /* 0x12 (00010010) */ + {1, 2, 5, 0, 0, 0, 0, 0}, /* 0x13 (00010011) */ + {3, 5, 0, 0, 0, 0, 0, 0}, /* 0x14 (00010100) */ + {1, 3, 5, 0, 0, 0, 0, 0}, /* 0x15 (00010101) */ + {2, 3, 5, 0, 0, 0, 0, 0}, /* 0x16 (00010110) */ + {1, 2, 3, 5, 0, 0, 0, 0}, /* 0x17 (00010111) */ + {4, 5, 0, 0, 0, 0, 0, 0}, /* 0x18 (00011000) */ + {1, 4, 5, 0, 0, 0, 0, 0}, /* 0x19 (00011001) */ + {2, 4, 5, 0, 0, 0, 0, 0}, /* 0x1A (00011010) */ + {1, 2, 4, 5, 0, 0, 0, 0}, /* 0x1B (00011011) */ + {3, 4, 5, 0, 0, 0, 0, 0}, /* 0x1C (00011100) */ + {1, 3, 4, 5, 0, 0, 0, 0}, /* 0x1D (00011101) */ + {2, 3, 4, 5, 0, 0, 0, 0}, /* 0x1E (00011110) */ + {1, 2, 3, 4, 5, 0, 0, 0}, /* 0x1F (00011111) */ + {6, 0, 0, 0, 0, 0, 0, 0}, /* 0x20 (00100000) */ + {1, 6, 0, 0, 0, 0, 0, 0}, /* 0x21 (00100001) */ + {2, 6, 0, 0, 0, 0, 0, 0}, /* 0x22 (00100010) */ + {1, 2, 6, 0, 0, 0, 0, 0}, /* 0x23 (00100011) */ + {3, 6, 0, 0, 0, 0, 0, 0}, /* 0x24 (00100100) */ + {1, 3, 6, 0, 0, 0, 0, 0}, /* 0x25 (00100101) */ + {2, 3, 6, 0, 0, 0, 0, 0}, /* 0x26 (00100110) */ + {1, 2, 3, 6, 0, 0, 0, 0}, /* 0x27 (00100111) */ + {4, 6, 0, 0, 0, 0, 0, 0}, /* 0x28 (00101000) */ + {1, 4, 6, 0, 0, 0, 0, 0}, /* 0x29 (00101001) */ + {2, 4, 6, 0, 0, 0, 0, 0}, /* 0x2A (00101010) */ + {1, 2, 4, 6, 0, 0, 0, 0}, /* 0x2B (00101011) */ + {3, 4, 6, 0, 0, 0, 0, 0}, /* 0x2C (00101100) */ + {1, 3, 4, 6, 0, 0, 0, 0}, /* 0x2D (00101101) */ + {2, 3, 4, 6, 0, 0, 0, 0}, /* 0x2E (00101110) */ + {1, 2, 3, 4, 6, 0, 0, 0}, /* 0x2F (00101111) */ + {5, 6, 0, 0, 0, 0, 0, 0}, /* 0x30 (00110000) */ + {1, 5, 6, 0, 0, 0, 0, 0}, /* 0x31 (00110001) */ + {2, 5, 6, 0, 0, 0, 0, 0}, /* 0x32 (00110010) */ + {1, 2, 5, 6, 0, 0, 0, 0}, /* 0x33 (00110011) */ + {3, 5, 6, 0, 0, 0, 0, 0}, /* 0x34 (00110100) */ + {1, 3, 5, 6, 0, 0, 0, 0}, /* 0x35 (00110101) */ + {2, 3, 5, 6, 0, 0, 0, 0}, /* 0x36 (00110110) */ + {1, 2, 3, 5, 6, 0, 0, 0}, /* 0x37 (00110111) */ + {4, 5, 6, 0, 0, 0, 0, 0}, /* 0x38 (00111000) */ + {1, 4, 5, 6, 0, 0, 0, 0}, /* 0x39 (00111001) */ + {2, 4, 5, 6, 0, 0, 0, 0}, /* 0x3A (00111010) */ + {1, 2, 4, 5, 6, 0, 0, 0}, /* 0x3B (00111011) */ + {3, 4, 5, 6, 0, 0, 0, 0}, /* 0x3C (00111100) */ + {1, 3, 4, 5, 6, 0, 0, 0}, /* 0x3D (00111101) */ + {2, 3, 4, 5, 6, 0, 0, 0}, /* 0x3E (00111110) */ + {1, 2, 3, 4, 5, 6, 0, 0}, /* 0x3F (00111111) */ + {7, 0, 0, 0, 0, 0, 0, 0}, /* 0x40 (01000000) */ + {1, 7, 0, 0, 0, 0, 0, 0}, /* 0x41 (01000001) */ + {2, 7, 0, 0, 0, 0, 0, 0}, /* 0x42 (01000010) */ + {1, 2, 7, 0, 0, 0, 0, 0}, /* 0x43 (01000011) */ + {3, 7, 0, 0, 0, 0, 0, 0}, /* 0x44 (01000100) */ + {1, 3, 7, 0, 0, 0, 0, 0}, /* 0x45 (01000101) */ + {2, 3, 7, 0, 0, 0, 0, 0}, /* 0x46 (01000110) */ + {1, 2, 3, 7, 0, 0, 0, 0}, /* 0x47 (01000111) */ + {4, 7, 0, 0, 0, 0, 0, 0}, /* 0x48 (01001000) */ + {1, 4, 7, 0, 0, 0, 0, 0}, /* 0x49 (01001001) */ + {2, 4, 7, 0, 0, 0, 0, 0}, /* 0x4A (01001010) */ + {1, 2, 4, 7, 0, 0, 0, 0}, /* 0x4B (01001011) */ + {3, 4, 7, 0, 0, 0, 0, 0}, /* 0x4C (01001100) */ + {1, 3, 4, 7, 0, 0, 0, 0}, /* 0x4D (01001101) */ + {2, 3, 4, 7, 0, 0, 0, 0}, /* 0x4E (01001110) */ + {1, 2, 3, 4, 7, 0, 0, 0}, /* 0x4F (01001111) */ + {5, 7, 0, 0, 0, 0, 0, 0}, /* 0x50 (01010000) */ + {1, 5, 7, 0, 0, 0, 0, 0}, /* 0x51 (01010001) */ + {2, 5, 7, 0, 0, 0, 0, 0}, /* 0x52 (01010010) */ + {1, 2, 5, 7, 0, 0, 0, 0}, /* 0x53 (01010011) */ + {3, 5, 7, 0, 0, 0, 0, 0}, /* 0x54 (01010100) */ + {1, 3, 5, 7, 0, 0, 0, 0}, /* 0x55 (01010101) */ + {2, 3, 5, 7, 0, 0, 0, 0}, /* 0x56 (01010110) */ + {1, 2, 3, 5, 7, 0, 0, 0}, /* 0x57 (01010111) */ + {4, 5, 7, 0, 0, 0, 0, 0}, /* 0x58 (01011000) */ + {1, 4, 5, 7, 0, 0, 0, 0}, /* 0x59 (01011001) */ + {2, 4, 5, 7, 0, 0, 0, 0}, /* 0x5A (01011010) */ + {1, 2, 4, 5, 7, 0, 0, 0}, /* 0x5B (01011011) */ + {3, 4, 5, 7, 0, 0, 0, 0}, /* 0x5C (01011100) */ + {1, 3, 4, 5, 7, 0, 0, 0}, /* 0x5D (01011101) */ + {2, 3, 4, 5, 7, 0, 0, 0}, /* 0x5E (01011110) */ + {1, 2, 3, 4, 5, 7, 0, 0}, /* 0x5F (01011111) */ + {6, 7, 0, 0, 0, 0, 0, 0}, /* 0x60 (01100000) */ + {1, 6, 7, 0, 0, 0, 0, 0}, /* 0x61 (01100001) */ + {2, 6, 7, 0, 0, 0, 0, 0}, /* 0x62 (01100010) */ + {1, 2, 6, 7, 0, 0, 0, 0}, /* 0x63 (01100011) */ + {3, 6, 7, 0, 0, 0, 0, 0}, /* 0x64 (01100100) */ + {1, 3, 6, 7, 0, 0, 0, 0}, /* 0x65 (01100101) */ + {2, 3, 6, 7, 0, 0, 0, 0}, /* 0x66 (01100110) */ + {1, 2, 3, 6, 7, 0, 0, 0}, /* 0x67 (01100111) */ + {4, 6, 7, 0, 0, 0, 0, 0}, /* 0x68 (01101000) */ + {1, 4, 6, 7, 0, 0, 0, 0}, /* 0x69 (01101001) */ + {2, 4, 6, 7, 0, 0, 0, 0}, /* 0x6A (01101010) */ + {1, 2, 4, 6, 7, 0, 0, 0}, /* 0x6B (01101011) */ + {3, 4, 6, 7, 0, 0, 0, 0}, /* 0x6C (01101100) */ + {1, 3, 4, 6, 7, 0, 0, 0}, /* 0x6D (01101101) */ + {2, 3, 4, 6, 7, 0, 0, 0}, /* 0x6E (01101110) */ + {1, 2, 3, 4, 6, 7, 0, 0}, /* 0x6F (01101111) */ + {5, 6, 7, 0, 0, 0, 0, 0}, /* 0x70 (01110000) */ + {1, 5, 6, 7, 0, 0, 0, 0}, /* 0x71 (01110001) */ + {2, 5, 6, 7, 0, 0, 0, 0}, /* 0x72 (01110010) */ + {1, 2, 5, 6, 7, 0, 0, 0}, /* 0x73 (01110011) */ + {3, 5, 6, 7, 0, 0, 0, 0}, /* 0x74 (01110100) */ + {1, 3, 5, 6, 7, 0, 0, 0}, /* 0x75 (01110101) */ + {2, 3, 5, 6, 7, 0, 0, 0}, /* 0x76 (01110110) */ + {1, 2, 3, 5, 6, 7, 0, 0}, /* 0x77 (01110111) */ + {4, 5, 6, 7, 0, 0, 0, 0}, /* 0x78 (01111000) */ + {1, 4, 5, 6, 7, 0, 0, 0}, /* 0x79 (01111001) */ + {2, 4, 5, 6, 7, 0, 0, 0}, /* 0x7A (01111010) */ + {1, 2, 4, 5, 6, 7, 0, 0}, /* 0x7B (01111011) */ + {3, 4, 5, 6, 7, 0, 0, 0}, /* 0x7C (01111100) */ + {1, 3, 4, 5, 6, 7, 0, 0}, /* 0x7D (01111101) */ + {2, 3, 4, 5, 6, 7, 0, 0}, /* 0x7E (01111110) */ + {1, 2, 3, 4, 5, 6, 7, 0}, /* 0x7F (01111111) */ + {8, 0, 0, 0, 0, 0, 0, 0}, /* 0x80 (10000000) */ + {1, 8, 0, 0, 0, 0, 0, 0}, /* 0x81 (10000001) */ + {2, 8, 0, 0, 0, 0, 0, 0}, /* 0x82 (10000010) */ + {1, 2, 8, 0, 0, 0, 0, 0}, /* 0x83 (10000011) */ + {3, 8, 0, 0, 0, 0, 0, 0}, /* 0x84 (10000100) */ + {1, 3, 8, 0, 0, 0, 0, 0}, /* 0x85 (10000101) */ + {2, 3, 8, 0, 0, 0, 0, 0}, /* 0x86 (10000110) */ + {1, 2, 3, 8, 0, 0, 0, 0}, /* 0x87 (10000111) */ + {4, 8, 0, 0, 0, 0, 0, 0}, /* 0x88 (10001000) */ + {1, 4, 8, 0, 0, 0, 0, 0}, /* 0x89 (10001001) */ + {2, 4, 8, 0, 0, 0, 0, 0}, /* 0x8A (10001010) */ + {1, 2, 4, 8, 0, 0, 0, 0}, /* 0x8B (10001011) */ + {3, 4, 8, 0, 0, 0, 0, 0}, /* 0x8C (10001100) */ + {1, 3, 4, 8, 0, 0, 0, 0}, /* 0x8D (10001101) */ + {2, 3, 4, 8, 0, 0, 0, 0}, /* 0x8E (10001110) */ + {1, 2, 3, 4, 8, 0, 0, 0}, /* 0x8F (10001111) */ + {5, 8, 0, 0, 0, 0, 0, 0}, /* 0x90 (10010000) */ + {1, 5, 8, 0, 0, 0, 0, 0}, /* 0x91 (10010001) */ + {2, 5, 8, 0, 0, 0, 0, 0}, /* 0x92 (10010010) */ + {1, 2, 5, 8, 0, 0, 0, 0}, /* 0x93 (10010011) */ + {3, 5, 8, 0, 0, 0, 0, 0}, /* 0x94 (10010100) */ + {1, 3, 5, 8, 0, 0, 0, 0}, /* 0x95 (10010101) */ + {2, 3, 5, 8, 0, 0, 0, 0}, /* 0x96 (10010110) */ + {1, 2, 3, 5, 8, 0, 0, 0}, /* 0x97 (10010111) */ + {4, 5, 8, 0, 0, 0, 0, 0}, /* 0x98 (10011000) */ + {1, 4, 5, 8, 0, 0, 0, 0}, /* 0x99 (10011001) */ + {2, 4, 5, 8, 0, 0, 0, 0}, /* 0x9A (10011010) */ + {1, 2, 4, 5, 8, 0, 0, 0}, /* 0x9B (10011011) */ + {3, 4, 5, 8, 0, 0, 0, 0}, /* 0x9C (10011100) */ + {1, 3, 4, 5, 8, 0, 0, 0}, /* 0x9D (10011101) */ + {2, 3, 4, 5, 8, 0, 0, 0}, /* 0x9E (10011110) */ + {1, 2, 3, 4, 5, 8, 0, 0}, /* 0x9F (10011111) */ + {6, 8, 0, 0, 0, 0, 0, 0}, /* 0xA0 (10100000) */ + {1, 6, 8, 0, 0, 0, 0, 0}, /* 0xA1 (10100001) */ + {2, 6, 8, 0, 0, 0, 0, 0}, /* 0xA2 (10100010) */ + {1, 2, 6, 8, 0, 0, 0, 0}, /* 0xA3 (10100011) */ + {3, 6, 8, 0, 0, 0, 0, 0}, /* 0xA4 (10100100) */ + {1, 3, 6, 8, 0, 0, 0, 0}, /* 0xA5 (10100101) */ + {2, 3, 6, 8, 0, 0, 0, 0}, /* 0xA6 (10100110) */ + {1, 2, 3, 6, 8, 0, 0, 0}, /* 0xA7 (10100111) */ + {4, 6, 8, 0, 0, 0, 0, 0}, /* 0xA8 (10101000) */ + {1, 4, 6, 8, 0, 0, 0, 0}, /* 0xA9 (10101001) */ + {2, 4, 6, 8, 0, 0, 0, 0}, /* 0xAA (10101010) */ + {1, 2, 4, 6, 8, 0, 0, 0}, /* 0xAB (10101011) */ + {3, 4, 6, 8, 0, 0, 0, 0}, /* 0xAC (10101100) */ + {1, 3, 4, 6, 8, 0, 0, 0}, /* 0xAD (10101101) */ + {2, 3, 4, 6, 8, 0, 0, 0}, /* 0xAE (10101110) */ + {1, 2, 3, 4, 6, 8, 0, 0}, /* 0xAF (10101111) */ + {5, 6, 8, 0, 0, 0, 0, 0}, /* 0xB0 (10110000) */ + {1, 5, 6, 8, 0, 0, 0, 0}, /* 0xB1 (10110001) */ + {2, 5, 6, 8, 0, 0, 0, 0}, /* 0xB2 (10110010) */ + {1, 2, 5, 6, 8, 0, 0, 0}, /* 0xB3 (10110011) */ + {3, 5, 6, 8, 0, 0, 0, 0}, /* 0xB4 (10110100) */ + {1, 3, 5, 6, 8, 0, 0, 0}, /* 0xB5 (10110101) */ + {2, 3, 5, 6, 8, 0, 0, 0}, /* 0xB6 (10110110) */ + {1, 2, 3, 5, 6, 8, 0, 0}, /* 0xB7 (10110111) */ + {4, 5, 6, 8, 0, 0, 0, 0}, /* 0xB8 (10111000) */ + {1, 4, 5, 6, 8, 0, 0, 0}, /* 0xB9 (10111001) */ + {2, 4, 5, 6, 8, 0, 0, 0}, /* 0xBA (10111010) */ + {1, 2, 4, 5, 6, 8, 0, 0}, /* 0xBB (10111011) */ + {3, 4, 5, 6, 8, 0, 0, 0}, /* 0xBC (10111100) */ + {1, 3, 4, 5, 6, 8, 0, 0}, /* 0xBD (10111101) */ + {2, 3, 4, 5, 6, 8, 0, 0}, /* 0xBE (10111110) */ + {1, 2, 3, 4, 5, 6, 8, 0}, /* 0xBF (10111111) */ + {7, 8, 0, 0, 0, 0, 0, 0}, /* 0xC0 (11000000) */ + {1, 7, 8, 0, 0, 0, 0, 0}, /* 0xC1 (11000001) */ + {2, 7, 8, 0, 0, 0, 0, 0}, /* 0xC2 (11000010) */ + {1, 2, 7, 8, 0, 0, 0, 0}, /* 0xC3 (11000011) */ + {3, 7, 8, 0, 0, 0, 0, 0}, /* 0xC4 (11000100) */ + {1, 3, 7, 8, 0, 0, 0, 0}, /* 0xC5 (11000101) */ + {2, 3, 7, 8, 0, 0, 0, 0}, /* 0xC6 (11000110) */ + {1, 2, 3, 7, 8, 0, 0, 0}, /* 0xC7 (11000111) */ + {4, 7, 8, 0, 0, 0, 0, 0}, /* 0xC8 (11001000) */ + {1, 4, 7, 8, 0, 0, 0, 0}, /* 0xC9 (11001001) */ + {2, 4, 7, 8, 0, 0, 0, 0}, /* 0xCA (11001010) */ + {1, 2, 4, 7, 8, 0, 0, 0}, /* 0xCB (11001011) */ + {3, 4, 7, 8, 0, 0, 0, 0}, /* 0xCC (11001100) */ + {1, 3, 4, 7, 8, 0, 0, 0}, /* 0xCD (11001101) */ + {2, 3, 4, 7, 8, 0, 0, 0}, /* 0xCE (11001110) */ + {1, 2, 3, 4, 7, 8, 0, 0}, /* 0xCF (11001111) */ + {5, 7, 8, 0, 0, 0, 0, 0}, /* 0xD0 (11010000) */ + {1, 5, 7, 8, 0, 0, 0, 0}, /* 0xD1 (11010001) */ + {2, 5, 7, 8, 0, 0, 0, 0}, /* 0xD2 (11010010) */ + {1, 2, 5, 7, 8, 0, 0, 0}, /* 0xD3 (11010011) */ + {3, 5, 7, 8, 0, 0, 0, 0}, /* 0xD4 (11010100) */ + {1, 3, 5, 7, 8, 0, 0, 0}, /* 0xD5 (11010101) */ + {2, 3, 5, 7, 8, 0, 0, 0}, /* 0xD6 (11010110) */ + {1, 2, 3, 5, 7, 8, 0, 0}, /* 0xD7 (11010111) */ + {4, 5, 7, 8, 0, 0, 0, 0}, /* 0xD8 (11011000) */ + {1, 4, 5, 7, 8, 0, 0, 0}, /* 0xD9 (11011001) */ + {2, 4, 5, 7, 8, 0, 0, 0}, /* 0xDA (11011010) */ + {1, 2, 4, 5, 7, 8, 0, 0}, /* 0xDB (11011011) */ + {3, 4, 5, 7, 8, 0, 0, 0}, /* 0xDC (11011100) */ + {1, 3, 4, 5, 7, 8, 0, 0}, /* 0xDD (11011101) */ + {2, 3, 4, 5, 7, 8, 0, 0}, /* 0xDE (11011110) */ + {1, 2, 3, 4, 5, 7, 8, 0}, /* 0xDF (11011111) */ + {6, 7, 8, 0, 0, 0, 0, 0}, /* 0xE0 (11100000) */ + {1, 6, 7, 8, 0, 0, 0, 0}, /* 0xE1 (11100001) */ + {2, 6, 7, 8, 0, 0, 0, 0}, /* 0xE2 (11100010) */ + {1, 2, 6, 7, 8, 0, 0, 0}, /* 0xE3 (11100011) */ + {3, 6, 7, 8, 0, 0, 0, 0}, /* 0xE4 (11100100) */ + {1, 3, 6, 7, 8, 0, 0, 0}, /* 0xE5 (11100101) */ + {2, 3, 6, 7, 8, 0, 0, 0}, /* 0xE6 (11100110) */ + {1, 2, 3, 6, 7, 8, 0, 0}, /* 0xE7 (11100111) */ + {4, 6, 7, 8, 0, 0, 0, 0}, /* 0xE8 (11101000) */ + {1, 4, 6, 7, 8, 0, 0, 0}, /* 0xE9 (11101001) */ + {2, 4, 6, 7, 8, 0, 0, 0}, /* 0xEA (11101010) */ + {1, 2, 4, 6, 7, 8, 0, 0}, /* 0xEB (11101011) */ + {3, 4, 6, 7, 8, 0, 0, 0}, /* 0xEC (11101100) */ + {1, 3, 4, 6, 7, 8, 0, 0}, /* 0xED (11101101) */ + {2, 3, 4, 6, 7, 8, 0, 0}, /* 0xEE (11101110) */ + {1, 2, 3, 4, 6, 7, 8, 0}, /* 0xEF (11101111) */ + {5, 6, 7, 8, 0, 0, 0, 0}, /* 0xF0 (11110000) */ + {1, 5, 6, 7, 8, 0, 0, 0}, /* 0xF1 (11110001) */ + {2, 5, 6, 7, 8, 0, 0, 0}, /* 0xF2 (11110010) */ + {1, 2, 5, 6, 7, 8, 0, 0}, /* 0xF3 (11110011) */ + {3, 5, 6, 7, 8, 0, 0, 0}, /* 0xF4 (11110100) */ + {1, 3, 5, 6, 7, 8, 0, 0}, /* 0xF5 (11110101) */ + {2, 3, 5, 6, 7, 8, 0, 0}, /* 0xF6 (11110110) */ + {1, 2, 3, 5, 6, 7, 8, 0}, /* 0xF7 (11110111) */ + {4, 5, 6, 7, 8, 0, 0, 0}, /* 0xF8 (11111000) */ + {1, 4, 5, 6, 7, 8, 0, 0}, /* 0xF9 (11111001) */ + {2, 4, 5, 6, 7, 8, 0, 0}, /* 0xFA (11111010) */ + {1, 2, 4, 5, 6, 7, 8, 0}, /* 0xFB (11111011) */ + {3, 4, 5, 6, 7, 8, 0, 0}, /* 0xFC (11111100) */ + {1, 3, 4, 5, 6, 7, 8, 0}, /* 0xFD (11111101) */ + {2, 3, 4, 5, 6, 7, 8, 0}, /* 0xFE (11111110) */ + {1, 2, 3, 4, 5, 6, 7, 8} /* 0xFF (11111111) */ +}; + +#endif // #if CROARING_IS_X64 + +#if CROARING_IS_X64 +// same as vecDecodeTable but in 16 bits +ALIGNED(32) +static uint16_t vecDecodeTable_uint16[256][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, /* 0x00 (00000000) */ + {1, 0, 0, 0, 0, 0, 0, 0}, /* 0x01 (00000001) */ + {2, 0, 0, 0, 0, 0, 0, 0}, /* 0x02 (00000010) */ + {1, 2, 0, 0, 0, 0, 0, 0}, /* 0x03 (00000011) */ + {3, 0, 0, 0, 0, 0, 0, 0}, /* 0x04 (00000100) */ + {1, 3, 0, 0, 0, 0, 0, 0}, /* 0x05 (00000101) */ + {2, 3, 0, 0, 0, 0, 0, 0}, /* 0x06 (00000110) */ + {1, 2, 3, 0, 0, 0, 0, 0}, /* 0x07 (00000111) */ + {4, 0, 0, 0, 0, 0, 0, 0}, /* 0x08 (00001000) */ + {1, 4, 0, 0, 0, 0, 0, 0}, /* 0x09 (00001001) */ + {2, 4, 0, 0, 0, 0, 0, 0}, /* 0x0A (00001010) */ + {1, 2, 4, 0, 0, 0, 0, 0}, /* 0x0B (00001011) */ + {3, 4, 0, 0, 0, 0, 0, 0}, /* 0x0C (00001100) */ + {1, 3, 4, 0, 0, 0, 0, 0}, /* 0x0D (00001101) */ + {2, 3, 4, 0, 0, 0, 0, 0}, /* 0x0E (00001110) */ + {1, 2, 3, 4, 0, 0, 0, 0}, /* 0x0F (00001111) */ + {5, 0, 0, 0, 0, 0, 0, 0}, /* 0x10 (00010000) */ + {1, 5, 0, 0, 0, 0, 0, 0}, /* 0x11 (00010001) */ + {2, 5, 0, 0, 0, 0, 0, 0}, /* 0x12 (00010010) */ + {1, 2, 5, 0, 0, 0, 0, 0}, /* 0x13 (00010011) */ + {3, 5, 0, 0, 0, 0, 0, 0}, /* 0x14 (00010100) */ + {1, 3, 5, 0, 0, 0, 0, 0}, /* 0x15 (00010101) */ + {2, 3, 5, 0, 0, 0, 0, 0}, /* 0x16 (00010110) */ + {1, 2, 3, 5, 0, 0, 0, 0}, /* 0x17 (00010111) */ + {4, 5, 0, 0, 0, 0, 0, 0}, /* 0x18 (00011000) */ + {1, 4, 5, 0, 0, 0, 0, 0}, /* 0x19 (00011001) */ + {2, 4, 5, 0, 0, 0, 0, 0}, /* 0x1A (00011010) */ + {1, 2, 4, 5, 0, 0, 0, 0}, /* 0x1B (00011011) */ + {3, 4, 5, 0, 0, 0, 0, 0}, /* 0x1C (00011100) */ + {1, 3, 4, 5, 0, 0, 0, 0}, /* 0x1D (00011101) */ + {2, 3, 4, 5, 0, 0, 0, 0}, /* 0x1E (00011110) */ + {1, 2, 3, 4, 5, 0, 0, 0}, /* 0x1F (00011111) */ + {6, 0, 0, 0, 0, 0, 0, 0}, /* 0x20 (00100000) */ + {1, 6, 0, 0, 0, 0, 0, 0}, /* 0x21 (00100001) */ + {2, 6, 0, 0, 0, 0, 0, 0}, /* 0x22 (00100010) */ + {1, 2, 6, 0, 0, 0, 0, 0}, /* 0x23 (00100011) */ + {3, 6, 0, 0, 0, 0, 0, 0}, /* 0x24 (00100100) */ + {1, 3, 6, 0, 0, 0, 0, 0}, /* 0x25 (00100101) */ + {2, 3, 6, 0, 0, 0, 0, 0}, /* 0x26 (00100110) */ + {1, 2, 3, 6, 0, 0, 0, 0}, /* 0x27 (00100111) */ + {4, 6, 0, 0, 0, 0, 0, 0}, /* 0x28 (00101000) */ + {1, 4, 6, 0, 0, 0, 0, 0}, /* 0x29 (00101001) */ + {2, 4, 6, 0, 0, 0, 0, 0}, /* 0x2A (00101010) */ + {1, 2, 4, 6, 0, 0, 0, 0}, /* 0x2B (00101011) */ + {3, 4, 6, 0, 0, 0, 0, 0}, /* 0x2C (00101100) */ + {1, 3, 4, 6, 0, 0, 0, 0}, /* 0x2D (00101101) */ + {2, 3, 4, 6, 0, 0, 0, 0}, /* 0x2E (00101110) */ + {1, 2, 3, 4, 6, 0, 0, 0}, /* 0x2F (00101111) */ + {5, 6, 0, 0, 0, 0, 0, 0}, /* 0x30 (00110000) */ + {1, 5, 6, 0, 0, 0, 0, 0}, /* 0x31 (00110001) */ + {2, 5, 6, 0, 0, 0, 0, 0}, /* 0x32 (00110010) */ + {1, 2, 5, 6, 0, 0, 0, 0}, /* 0x33 (00110011) */ + {3, 5, 6, 0, 0, 0, 0, 0}, /* 0x34 (00110100) */ + {1, 3, 5, 6, 0, 0, 0, 0}, /* 0x35 (00110101) */ + {2, 3, 5, 6, 0, 0, 0, 0}, /* 0x36 (00110110) */ + {1, 2, 3, 5, 6, 0, 0, 0}, /* 0x37 (00110111) */ + {4, 5, 6, 0, 0, 0, 0, 0}, /* 0x38 (00111000) */ + {1, 4, 5, 6, 0, 0, 0, 0}, /* 0x39 (00111001) */ + {2, 4, 5, 6, 0, 0, 0, 0}, /* 0x3A (00111010) */ + {1, 2, 4, 5, 6, 0, 0, 0}, /* 0x3B (00111011) */ + {3, 4, 5, 6, 0, 0, 0, 0}, /* 0x3C (00111100) */ + {1, 3, 4, 5, 6, 0, 0, 0}, /* 0x3D (00111101) */ + {2, 3, 4, 5, 6, 0, 0, 0}, /* 0x3E (00111110) */ + {1, 2, 3, 4, 5, 6, 0, 0}, /* 0x3F (00111111) */ + {7, 0, 0, 0, 0, 0, 0, 0}, /* 0x40 (01000000) */ + {1, 7, 0, 0, 0, 0, 0, 0}, /* 0x41 (01000001) */ + {2, 7, 0, 0, 0, 0, 0, 0}, /* 0x42 (01000010) */ + {1, 2, 7, 0, 0, 0, 0, 0}, /* 0x43 (01000011) */ + {3, 7, 0, 0, 0, 0, 0, 0}, /* 0x44 (01000100) */ + {1, 3, 7, 0, 0, 0, 0, 0}, /* 0x45 (01000101) */ + {2, 3, 7, 0, 0, 0, 0, 0}, /* 0x46 (01000110) */ + {1, 2, 3, 7, 0, 0, 0, 0}, /* 0x47 (01000111) */ + {4, 7, 0, 0, 0, 0, 0, 0}, /* 0x48 (01001000) */ + {1, 4, 7, 0, 0, 0, 0, 0}, /* 0x49 (01001001) */ + {2, 4, 7, 0, 0, 0, 0, 0}, /* 0x4A (01001010) */ + {1, 2, 4, 7, 0, 0, 0, 0}, /* 0x4B (01001011) */ + {3, 4, 7, 0, 0, 0, 0, 0}, /* 0x4C (01001100) */ + {1, 3, 4, 7, 0, 0, 0, 0}, /* 0x4D (01001101) */ + {2, 3, 4, 7, 0, 0, 0, 0}, /* 0x4E (01001110) */ + {1, 2, 3, 4, 7, 0, 0, 0}, /* 0x4F (01001111) */ + {5, 7, 0, 0, 0, 0, 0, 0}, /* 0x50 (01010000) */ + {1, 5, 7, 0, 0, 0, 0, 0}, /* 0x51 (01010001) */ + {2, 5, 7, 0, 0, 0, 0, 0}, /* 0x52 (01010010) */ + {1, 2, 5, 7, 0, 0, 0, 0}, /* 0x53 (01010011) */ + {3, 5, 7, 0, 0, 0, 0, 0}, /* 0x54 (01010100) */ + {1, 3, 5, 7, 0, 0, 0, 0}, /* 0x55 (01010101) */ + {2, 3, 5, 7, 0, 0, 0, 0}, /* 0x56 (01010110) */ + {1, 2, 3, 5, 7, 0, 0, 0}, /* 0x57 (01010111) */ + {4, 5, 7, 0, 0, 0, 0, 0}, /* 0x58 (01011000) */ + {1, 4, 5, 7, 0, 0, 0, 0}, /* 0x59 (01011001) */ + {2, 4, 5, 7, 0, 0, 0, 0}, /* 0x5A (01011010) */ + {1, 2, 4, 5, 7, 0, 0, 0}, /* 0x5B (01011011) */ + {3, 4, 5, 7, 0, 0, 0, 0}, /* 0x5C (01011100) */ + {1, 3, 4, 5, 7, 0, 0, 0}, /* 0x5D (01011101) */ + {2, 3, 4, 5, 7, 0, 0, 0}, /* 0x5E (01011110) */ + {1, 2, 3, 4, 5, 7, 0, 0}, /* 0x5F (01011111) */ + {6, 7, 0, 0, 0, 0, 0, 0}, /* 0x60 (01100000) */ + {1, 6, 7, 0, 0, 0, 0, 0}, /* 0x61 (01100001) */ + {2, 6, 7, 0, 0, 0, 0, 0}, /* 0x62 (01100010) */ + {1, 2, 6, 7, 0, 0, 0, 0}, /* 0x63 (01100011) */ + {3, 6, 7, 0, 0, 0, 0, 0}, /* 0x64 (01100100) */ + {1, 3, 6, 7, 0, 0, 0, 0}, /* 0x65 (01100101) */ + {2, 3, 6, 7, 0, 0, 0, 0}, /* 0x66 (01100110) */ + {1, 2, 3, 6, 7, 0, 0, 0}, /* 0x67 (01100111) */ + {4, 6, 7, 0, 0, 0, 0, 0}, /* 0x68 (01101000) */ + {1, 4, 6, 7, 0, 0, 0, 0}, /* 0x69 (01101001) */ + {2, 4, 6, 7, 0, 0, 0, 0}, /* 0x6A (01101010) */ + {1, 2, 4, 6, 7, 0, 0, 0}, /* 0x6B (01101011) */ + {3, 4, 6, 7, 0, 0, 0, 0}, /* 0x6C (01101100) */ + {1, 3, 4, 6, 7, 0, 0, 0}, /* 0x6D (01101101) */ + {2, 3, 4, 6, 7, 0, 0, 0}, /* 0x6E (01101110) */ + {1, 2, 3, 4, 6, 7, 0, 0}, /* 0x6F (01101111) */ + {5, 6, 7, 0, 0, 0, 0, 0}, /* 0x70 (01110000) */ + {1, 5, 6, 7, 0, 0, 0, 0}, /* 0x71 (01110001) */ + {2, 5, 6, 7, 0, 0, 0, 0}, /* 0x72 (01110010) */ + {1, 2, 5, 6, 7, 0, 0, 0}, /* 0x73 (01110011) */ + {3, 5, 6, 7, 0, 0, 0, 0}, /* 0x74 (01110100) */ + {1, 3, 5, 6, 7, 0, 0, 0}, /* 0x75 (01110101) */ + {2, 3, 5, 6, 7, 0, 0, 0}, /* 0x76 (01110110) */ + {1, 2, 3, 5, 6, 7, 0, 0}, /* 0x77 (01110111) */ + {4, 5, 6, 7, 0, 0, 0, 0}, /* 0x78 (01111000) */ + {1, 4, 5, 6, 7, 0, 0, 0}, /* 0x79 (01111001) */ + {2, 4, 5, 6, 7, 0, 0, 0}, /* 0x7A (01111010) */ + {1, 2, 4, 5, 6, 7, 0, 0}, /* 0x7B (01111011) */ + {3, 4, 5, 6, 7, 0, 0, 0}, /* 0x7C (01111100) */ + {1, 3, 4, 5, 6, 7, 0, 0}, /* 0x7D (01111101) */ + {2, 3, 4, 5, 6, 7, 0, 0}, /* 0x7E (01111110) */ + {1, 2, 3, 4, 5, 6, 7, 0}, /* 0x7F (01111111) */ + {8, 0, 0, 0, 0, 0, 0, 0}, /* 0x80 (10000000) */ + {1, 8, 0, 0, 0, 0, 0, 0}, /* 0x81 (10000001) */ + {2, 8, 0, 0, 0, 0, 0, 0}, /* 0x82 (10000010) */ + {1, 2, 8, 0, 0, 0, 0, 0}, /* 0x83 (10000011) */ + {3, 8, 0, 0, 0, 0, 0, 0}, /* 0x84 (10000100) */ + {1, 3, 8, 0, 0, 0, 0, 0}, /* 0x85 (10000101) */ + {2, 3, 8, 0, 0, 0, 0, 0}, /* 0x86 (10000110) */ + {1, 2, 3, 8, 0, 0, 0, 0}, /* 0x87 (10000111) */ + {4, 8, 0, 0, 0, 0, 0, 0}, /* 0x88 (10001000) */ + {1, 4, 8, 0, 0, 0, 0, 0}, /* 0x89 (10001001) */ + {2, 4, 8, 0, 0, 0, 0, 0}, /* 0x8A (10001010) */ + {1, 2, 4, 8, 0, 0, 0, 0}, /* 0x8B (10001011) */ + {3, 4, 8, 0, 0, 0, 0, 0}, /* 0x8C (10001100) */ + {1, 3, 4, 8, 0, 0, 0, 0}, /* 0x8D (10001101) */ + {2, 3, 4, 8, 0, 0, 0, 0}, /* 0x8E (10001110) */ + {1, 2, 3, 4, 8, 0, 0, 0}, /* 0x8F (10001111) */ + {5, 8, 0, 0, 0, 0, 0, 0}, /* 0x90 (10010000) */ + {1, 5, 8, 0, 0, 0, 0, 0}, /* 0x91 (10010001) */ + {2, 5, 8, 0, 0, 0, 0, 0}, /* 0x92 (10010010) */ + {1, 2, 5, 8, 0, 0, 0, 0}, /* 0x93 (10010011) */ + {3, 5, 8, 0, 0, 0, 0, 0}, /* 0x94 (10010100) */ + {1, 3, 5, 8, 0, 0, 0, 0}, /* 0x95 (10010101) */ + {2, 3, 5, 8, 0, 0, 0, 0}, /* 0x96 (10010110) */ + {1, 2, 3, 5, 8, 0, 0, 0}, /* 0x97 (10010111) */ + {4, 5, 8, 0, 0, 0, 0, 0}, /* 0x98 (10011000) */ + {1, 4, 5, 8, 0, 0, 0, 0}, /* 0x99 (10011001) */ + {2, 4, 5, 8, 0, 0, 0, 0}, /* 0x9A (10011010) */ + {1, 2, 4, 5, 8, 0, 0, 0}, /* 0x9B (10011011) */ + {3, 4, 5, 8, 0, 0, 0, 0}, /* 0x9C (10011100) */ + {1, 3, 4, 5, 8, 0, 0, 0}, /* 0x9D (10011101) */ + {2, 3, 4, 5, 8, 0, 0, 0}, /* 0x9E (10011110) */ + {1, 2, 3, 4, 5, 8, 0, 0}, /* 0x9F (10011111) */ + {6, 8, 0, 0, 0, 0, 0, 0}, /* 0xA0 (10100000) */ + {1, 6, 8, 0, 0, 0, 0, 0}, /* 0xA1 (10100001) */ + {2, 6, 8, 0, 0, 0, 0, 0}, /* 0xA2 (10100010) */ + {1, 2, 6, 8, 0, 0, 0, 0}, /* 0xA3 (10100011) */ + {3, 6, 8, 0, 0, 0, 0, 0}, /* 0xA4 (10100100) */ + {1, 3, 6, 8, 0, 0, 0, 0}, /* 0xA5 (10100101) */ + {2, 3, 6, 8, 0, 0, 0, 0}, /* 0xA6 (10100110) */ + {1, 2, 3, 6, 8, 0, 0, 0}, /* 0xA7 (10100111) */ + {4, 6, 8, 0, 0, 0, 0, 0}, /* 0xA8 (10101000) */ + {1, 4, 6, 8, 0, 0, 0, 0}, /* 0xA9 (10101001) */ + {2, 4, 6, 8, 0, 0, 0, 0}, /* 0xAA (10101010) */ + {1, 2, 4, 6, 8, 0, 0, 0}, /* 0xAB (10101011) */ + {3, 4, 6, 8, 0, 0, 0, 0}, /* 0xAC (10101100) */ + {1, 3, 4, 6, 8, 0, 0, 0}, /* 0xAD (10101101) */ + {2, 3, 4, 6, 8, 0, 0, 0}, /* 0xAE (10101110) */ + {1, 2, 3, 4, 6, 8, 0, 0}, /* 0xAF (10101111) */ + {5, 6, 8, 0, 0, 0, 0, 0}, /* 0xB0 (10110000) */ + {1, 5, 6, 8, 0, 0, 0, 0}, /* 0xB1 (10110001) */ + {2, 5, 6, 8, 0, 0, 0, 0}, /* 0xB2 (10110010) */ + {1, 2, 5, 6, 8, 0, 0, 0}, /* 0xB3 (10110011) */ + {3, 5, 6, 8, 0, 0, 0, 0}, /* 0xB4 (10110100) */ + {1, 3, 5, 6, 8, 0, 0, 0}, /* 0xB5 (10110101) */ + {2, 3, 5, 6, 8, 0, 0, 0}, /* 0xB6 (10110110) */ + {1, 2, 3, 5, 6, 8, 0, 0}, /* 0xB7 (10110111) */ + {4, 5, 6, 8, 0, 0, 0, 0}, /* 0xB8 (10111000) */ + {1, 4, 5, 6, 8, 0, 0, 0}, /* 0xB9 (10111001) */ + {2, 4, 5, 6, 8, 0, 0, 0}, /* 0xBA (10111010) */ + {1, 2, 4, 5, 6, 8, 0, 0}, /* 0xBB (10111011) */ + {3, 4, 5, 6, 8, 0, 0, 0}, /* 0xBC (10111100) */ + {1, 3, 4, 5, 6, 8, 0, 0}, /* 0xBD (10111101) */ + {2, 3, 4, 5, 6, 8, 0, 0}, /* 0xBE (10111110) */ + {1, 2, 3, 4, 5, 6, 8, 0}, /* 0xBF (10111111) */ + {7, 8, 0, 0, 0, 0, 0, 0}, /* 0xC0 (11000000) */ + {1, 7, 8, 0, 0, 0, 0, 0}, /* 0xC1 (11000001) */ + {2, 7, 8, 0, 0, 0, 0, 0}, /* 0xC2 (11000010) */ + {1, 2, 7, 8, 0, 0, 0, 0}, /* 0xC3 (11000011) */ + {3, 7, 8, 0, 0, 0, 0, 0}, /* 0xC4 (11000100) */ + {1, 3, 7, 8, 0, 0, 0, 0}, /* 0xC5 (11000101) */ + {2, 3, 7, 8, 0, 0, 0, 0}, /* 0xC6 (11000110) */ + {1, 2, 3, 7, 8, 0, 0, 0}, /* 0xC7 (11000111) */ + {4, 7, 8, 0, 0, 0, 0, 0}, /* 0xC8 (11001000) */ + {1, 4, 7, 8, 0, 0, 0, 0}, /* 0xC9 (11001001) */ + {2, 4, 7, 8, 0, 0, 0, 0}, /* 0xCA (11001010) */ + {1, 2, 4, 7, 8, 0, 0, 0}, /* 0xCB (11001011) */ + {3, 4, 7, 8, 0, 0, 0, 0}, /* 0xCC (11001100) */ + {1, 3, 4, 7, 8, 0, 0, 0}, /* 0xCD (11001101) */ + {2, 3, 4, 7, 8, 0, 0, 0}, /* 0xCE (11001110) */ + {1, 2, 3, 4, 7, 8, 0, 0}, /* 0xCF (11001111) */ + {5, 7, 8, 0, 0, 0, 0, 0}, /* 0xD0 (11010000) */ + {1, 5, 7, 8, 0, 0, 0, 0}, /* 0xD1 (11010001) */ + {2, 5, 7, 8, 0, 0, 0, 0}, /* 0xD2 (11010010) */ + {1, 2, 5, 7, 8, 0, 0, 0}, /* 0xD3 (11010011) */ + {3, 5, 7, 8, 0, 0, 0, 0}, /* 0xD4 (11010100) */ + {1, 3, 5, 7, 8, 0, 0, 0}, /* 0xD5 (11010101) */ + {2, 3, 5, 7, 8, 0, 0, 0}, /* 0xD6 (11010110) */ + {1, 2, 3, 5, 7, 8, 0, 0}, /* 0xD7 (11010111) */ + {4, 5, 7, 8, 0, 0, 0, 0}, /* 0xD8 (11011000) */ + {1, 4, 5, 7, 8, 0, 0, 0}, /* 0xD9 (11011001) */ + {2, 4, 5, 7, 8, 0, 0, 0}, /* 0xDA (11011010) */ + {1, 2, 4, 5, 7, 8, 0, 0}, /* 0xDB (11011011) */ + {3, 4, 5, 7, 8, 0, 0, 0}, /* 0xDC (11011100) */ + {1, 3, 4, 5, 7, 8, 0, 0}, /* 0xDD (11011101) */ + {2, 3, 4, 5, 7, 8, 0, 0}, /* 0xDE (11011110) */ + {1, 2, 3, 4, 5, 7, 8, 0}, /* 0xDF (11011111) */ + {6, 7, 8, 0, 0, 0, 0, 0}, /* 0xE0 (11100000) */ + {1, 6, 7, 8, 0, 0, 0, 0}, /* 0xE1 (11100001) */ + {2, 6, 7, 8, 0, 0, 0, 0}, /* 0xE2 (11100010) */ + {1, 2, 6, 7, 8, 0, 0, 0}, /* 0xE3 (11100011) */ + {3, 6, 7, 8, 0, 0, 0, 0}, /* 0xE4 (11100100) */ + {1, 3, 6, 7, 8, 0, 0, 0}, /* 0xE5 (11100101) */ + {2, 3, 6, 7, 8, 0, 0, 0}, /* 0xE6 (11100110) */ + {1, 2, 3, 6, 7, 8, 0, 0}, /* 0xE7 (11100111) */ + {4, 6, 7, 8, 0, 0, 0, 0}, /* 0xE8 (11101000) */ + {1, 4, 6, 7, 8, 0, 0, 0}, /* 0xE9 (11101001) */ + {2, 4, 6, 7, 8, 0, 0, 0}, /* 0xEA (11101010) */ + {1, 2, 4, 6, 7, 8, 0, 0}, /* 0xEB (11101011) */ + {3, 4, 6, 7, 8, 0, 0, 0}, /* 0xEC (11101100) */ + {1, 3, 4, 6, 7, 8, 0, 0}, /* 0xED (11101101) */ + {2, 3, 4, 6, 7, 8, 0, 0}, /* 0xEE (11101110) */ + {1, 2, 3, 4, 6, 7, 8, 0}, /* 0xEF (11101111) */ + {5, 6, 7, 8, 0, 0, 0, 0}, /* 0xF0 (11110000) */ + {1, 5, 6, 7, 8, 0, 0, 0}, /* 0xF1 (11110001) */ + {2, 5, 6, 7, 8, 0, 0, 0}, /* 0xF2 (11110010) */ + {1, 2, 5, 6, 7, 8, 0, 0}, /* 0xF3 (11110011) */ + {3, 5, 6, 7, 8, 0, 0, 0}, /* 0xF4 (11110100) */ + {1, 3, 5, 6, 7, 8, 0, 0}, /* 0xF5 (11110101) */ + {2, 3, 5, 6, 7, 8, 0, 0}, /* 0xF6 (11110110) */ + {1, 2, 3, 5, 6, 7, 8, 0}, /* 0xF7 (11110111) */ + {4, 5, 6, 7, 8, 0, 0, 0}, /* 0xF8 (11111000) */ + {1, 4, 5, 6, 7, 8, 0, 0}, /* 0xF9 (11111001) */ + {2, 4, 5, 6, 7, 8, 0, 0}, /* 0xFA (11111010) */ + {1, 2, 4, 5, 6, 7, 8, 0}, /* 0xFB (11111011) */ + {3, 4, 5, 6, 7, 8, 0, 0}, /* 0xFC (11111100) */ + {1, 3, 4, 5, 6, 7, 8, 0}, /* 0xFD (11111101) */ + {2, 3, 4, 5, 6, 7, 8, 0}, /* 0xFE (11111110) */ + {1, 2, 3, 4, 5, 6, 7, 8} /* 0xFF (11111111) */ +}; + +#endif + +#if CROARING_IS_X64 +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +const uint8_t vbmi2_table[64] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; +size_t bitset_extract_setbits_avx512(const uint64_t *words, size_t length, uint32_t *vout, + size_t outcapacity, uint32_t base) { + uint32_t *out = (uint32_t *)vout; + uint32_t *initout = out; + uint32_t *safeout = out + outcapacity; + __m512i base_v = _mm512_set1_epi32(base); + __m512i index_table = _mm512_loadu_si512(vbmi2_table); + size_t i = 0; + + for (; (i < length) && ((out + 64) < safeout); i += 1) { + uint64_t v = words[i]; + __m512i vec = _mm512_maskz_compress_epi8(v, index_table); + + uint8_t advance = (uint8_t)roaring_hamming(v); + + __m512i vbase = _mm512_add_epi32(base_v, _mm512_set1_epi32((int)(i * 64))); + __m512i r1 = _mm512_cvtepi8_epi32(_mm512_extracti32x4_epi32(vec, 0)); + __m512i r2 = _mm512_cvtepi8_epi32(_mm512_extracti32x4_epi32(vec, 1)); + __m512i r3 = _mm512_cvtepi8_epi32(_mm512_extracti32x4_epi32(vec, 2)); + __m512i r4 = _mm512_cvtepi8_epi32(_mm512_extracti32x4_epi32(vec, 3)); + + r1 = _mm512_add_epi32(r1, vbase); + r2 = _mm512_add_epi32(r2, vbase); + r3 = _mm512_add_epi32(r3, vbase); + r4 = _mm512_add_epi32(r4, vbase); + _mm512_storeu_si512((__m512i *)out, r1); + _mm512_storeu_si512((__m512i *)(out + 16), r2); + _mm512_storeu_si512((__m512i *)(out + 32), r3); + _mm512_storeu_si512((__m512i *)(out + 48), r4); + + out += advance; + } + + base += i * 64; + + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = words[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = + w & + (~w + + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = roaring_trailing_zeroes(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + out++; + w ^= t; + } + base += 64; + } + + return out - initout; +} + +// Reference: https://lemire.me/blog/2022/05/10/faster-bitset-decoding-using-intel-avx-512/ +size_t bitset_extract_setbits_avx512_uint16(const uint64_t *array, size_t length, uint16_t *vout, + size_t capacity, uint16_t base) { + uint16_t *out = (uint16_t *)vout; + uint16_t *initout = out; + uint16_t *safeout = vout + capacity; + + __m512i base_v = _mm512_set1_epi16(base); + __m512i index_table = _mm512_loadu_si512(vbmi2_table); + size_t i = 0; + + for (; (i < length) && ((out + 64) < safeout); i++) { + uint64_t v = array[i]; + __m512i vec = _mm512_maskz_compress_epi8(v, index_table); + + uint8_t advance = (uint8_t)roaring_hamming(v); + + __m512i vbase = _mm512_add_epi16(base_v, _mm512_set1_epi16((short)(i * 64))); + __m512i r1 = _mm512_cvtepi8_epi16(_mm512_extracti32x8_epi32(vec, 0)); + __m512i r2 = _mm512_cvtepi8_epi16(_mm512_extracti32x8_epi32(vec, 1)); + + r1 = _mm512_add_epi16(r1, vbase); + r2 = _mm512_add_epi16(r2, vbase); + + _mm512_storeu_si512((__m512i *)out, r1); + _mm512_storeu_si512((__m512i *)(out + 32), r2); + out += advance; + } + + base += i * 64; + + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = array[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = + w & + (~w + + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = roaring_trailing_zeroes(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out, &val, sizeof(uint16_t)); + out++; + w ^= t; + } + base += 64; + } + + return out - initout; +} +CROARING_UNTARGET_AVX512 +#endif + +CROARING_TARGET_AVX2 +size_t bitset_extract_setbits_avx2(const uint64_t *words, size_t length, uint32_t *out, + size_t outcapacity, uint32_t base) { + uint32_t *initout = out; + __m256i baseVec = _mm256_set1_epi32(base - 1); + __m256i incVec = _mm256_set1_epi32(64); + __m256i add8 = _mm256_set1_epi32(8); + uint32_t *safeout = out + outcapacity; + size_t i = 0; + for (; (i < length) && (out + 64 <= safeout); ++i) { + uint64_t w = words[i]; + if (w == 0) { + baseVec = _mm256_add_epi32(baseVec, incVec); + } else { + for (int k = 0; k < 4; ++k) { + uint8_t byteA = (uint8_t)w; + uint8_t byteB = (uint8_t)(w >> 8); + w >>= 16; + __m256i vecA = _mm256_loadu_si256((const __m256i *)vecDecodeTable[byteA]); + __m256i vecB = _mm256_loadu_si256((const __m256i *)vecDecodeTable[byteB]); + uint8_t advanceA = lengthTable[byteA]; + uint8_t advanceB = lengthTable[byteB]; + vecA = _mm256_add_epi32(baseVec, vecA); + baseVec = _mm256_add_epi32(baseVec, add8); + vecB = _mm256_add_epi32(baseVec, vecB); + baseVec = _mm256_add_epi32(baseVec, add8); + _mm256_storeu_si256((__m256i *)out, vecA); + out += advanceA; + _mm256_storeu_si256((__m256i *)out, vecB); + out += advanceB; + } + } + } + base += i * 64; + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = words[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = + w & + (~w + + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = roaring_trailing_zeroes(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + out++; + w ^= t; + } + base += 64; + } + return out - initout; +} +CROARING_UNTARGET_AVX2 +#endif // CROARING_IS_X64 + +size_t bitset_extract_setbits(const uint64_t *words, size_t length, uint32_t *out, uint32_t base) { + int outpos = 0; + for (size_t i = 0; i < length; ++i) { + uint64_t w = words[i]; + while (w != 0) { + uint64_t t = + w & + (~w + + 1); // on x64, should compile to BLSI (careful: the Intel compiler seems to fail) + int r = roaring_trailing_zeroes(w); // on x64, should compile to TZCNT + uint32_t val = r + base; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + w ^= t; + } + base += 64; + } + return outpos; +} + +size_t bitset_extract_intersection_setbits_uint16(const uint64_t *__restrict__ words1, + const uint64_t *__restrict__ words2, + size_t length, uint16_t *out, uint16_t base) { + int outpos = 0; + for (size_t i = 0; i < length; ++i) { + uint64_t w = words1[i] & words2[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + out[outpos++] = (uint16_t)(r + base); + w ^= t; + } + base += 64; + } + return outpos; +} + +#if CROARING_IS_X64 +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out" as 16-bit integers, values start at "base" (can + *be set to zero). + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + * + * This function uses SSE decoding. + */ +CROARING_TARGET_AVX2 +size_t bitset_extract_setbits_sse_uint16(const uint64_t *words, size_t length, uint16_t *out, + size_t outcapacity, uint16_t base) { + uint16_t *initout = out; + __m128i baseVec = _mm_set1_epi16(base - 1); + __m128i incVec = _mm_set1_epi16(64); + __m128i add8 = _mm_set1_epi16(8); + uint16_t *safeout = out + outcapacity; + const int numberofbytes = 2; // process two bytes at a time + size_t i = 0; + for (; (i < length) && (out + numberofbytes * 8 <= safeout); ++i) { + uint64_t w = words[i]; + if (w == 0) { + baseVec = _mm_add_epi16(baseVec, incVec); + } else { + for (int k = 0; k < 4; ++k) { + uint8_t byteA = (uint8_t)w; + uint8_t byteB = (uint8_t)(w >> 8); + w >>= 16; + __m128i vecA = _mm_loadu_si128((const __m128i *)vecDecodeTable_uint16[byteA]); + __m128i vecB = _mm_loadu_si128((const __m128i *)vecDecodeTable_uint16[byteB]); + uint8_t advanceA = lengthTable[byteA]; + uint8_t advanceB = lengthTable[byteB]; + vecA = _mm_add_epi16(baseVec, vecA); + baseVec = _mm_add_epi16(baseVec, add8); + vecB = _mm_add_epi16(baseVec, vecB); + baseVec = _mm_add_epi16(baseVec, add8); + _mm_storeu_si128((__m128i *)out, vecA); + out += advanceA; + _mm_storeu_si128((__m128i *)out, vecB); + out += advanceB; + } + } + } + base += (uint16_t)(i * 64); + for (; (i < length) && (out < safeout); ++i) { + uint64_t w = words[i]; + while ((w != 0) && (out < safeout)) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + *out = (uint16_t)(r + base); + out++; + w ^= t; + } + base += 64; + } + return out - initout; +} +CROARING_UNTARGET_AVX2 +#endif + +/* + * Given a bitset containing "length" 64-bit words, write out the position + * of all the set bits to "out", values start at "base" (can be set to zero). + * + * The "out" pointer should be sufficient to store the actual number of bits + *set. + * + * Returns how many values were actually decoded. + */ +size_t bitset_extract_setbits_uint16(const uint64_t *words, size_t length, uint16_t *out, + uint16_t base) { + int outpos = 0; + for (size_t i = 0; i < length; ++i) { + uint64_t w = words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + out[outpos++] = (uint16_t)(r + base); + w ^= t; + } + base += 64; + } + return outpos; +} + +#if defined(CROARING_ASMBITMANIPOPTIMIZATION) && defined(CROARING_IS_X64) + +static inline uint64_t _asm_bitset_set_list_withcard(uint64_t *words, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, pos; + uint64_t shift = 6; + const uint16_t *end = list + length; + if (!length) return card; + // TODO: could unroll for performance, see bitset_set_list + // bts is not available as an intrinsic in GCC + __asm volatile( + "1:\n" + "movzwq (%[list]), %[pos]\n" + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)\n" + "sbb $-1, %[card]\n" + "add $2, %[list]\n" + "cmp %[list], %[end]\n" + "jnz 1b" + : [card] "+&r"(card), [list] "+&r"(list), [load] "=&r"(load), [pos] "=&r"(pos), + [offset] "=&r"(offset) + : [end] "r"(end), [words] "r"(words), [shift] "r"(shift)); + return card; +} + +static inline void _asm_bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length) { + uint64_t pos; + const uint16_t *end = list + length; + + uint64_t shift = 6; + uint64_t offset; + uint64_t load; + for (; list + 3 < end; list += 4) { + pos = list[0]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [words] "r"(words), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[1]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [words] "r"(words), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[2]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [words] "r"(words), [shift] "r"(shift), [pos] "r"(pos)); + pos = list[3]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [words] "r"(words), [shift] "r"(shift), [pos] "r"(pos)); + } + + while (list != end) { + pos = list[0]; + __asm volatile( + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "bts %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)" + : [load] "=&r"(load), [offset] "=&r"(offset) + : [words] "r"(words), [shift] "r"(shift), [pos] "r"(pos)); + list++; + } +} + +static inline uint64_t _asm_bitset_clear_list(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length) { + uint64_t offset, load, pos; + uint64_t shift = 6; + const uint16_t *end = list + length; + if (!length) return card; + // btr is not available as an intrinsic in GCC + __asm volatile( + "1:\n" + "movzwq (%[list]), %[pos]\n" + "shrx %[shift], %[pos], %[offset]\n" + "mov (%[words],%[offset],8), %[load]\n" + "btr %[pos], %[load]\n" + "mov %[load], (%[words],%[offset],8)\n" + "sbb $0, %[card]\n" + "add $2, %[list]\n" + "cmp %[list], %[end]\n" + "jnz 1b" + : [card] "+&r"(card), [list] "+&r"(list), [load] "=&r"(load), [pos] "=&r"(pos), + [offset] "=&r"(offset) + : [end] "r"(end), [words] "r"(words), [shift] "r"(shift) + : + /* clobbers */ "memory"); + return card; +} + +static inline uint64_t _scalar_bitset_clear_list(uint64_t *words, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load & ~(UINT64_C(1) << index); + card -= (load ^ newload) >> index; + words[offset] = newload; + list++; + } + return card; +} + +static inline uint64_t _scalar_bitset_set_list_withcard(uint64_t *words, uint64_t card, + const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load | (UINT64_C(1) << index); + card += (load ^ newload) >> index; + words[offset] = newload; + list++; + } + return card; +} + +static inline void _scalar_bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load | (UINT64_C(1) << index); + words[offset] = newload; + list++; + } +} + +uint64_t bitset_clear_list(uint64_t *words, uint64_t card, const uint16_t *list, uint64_t length) { + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + return _asm_bitset_clear_list(words, card, list, length); + } else { + return _scalar_bitset_clear_list(words, card, list, length); + } +} + +uint64_t bitset_set_list_withcard(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length) { + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + return _asm_bitset_set_list_withcard(words, card, list, length); + } else { + return _scalar_bitset_set_list_withcard(words, card, list, length); + } +} + +void bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length) { + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + _asm_bitset_set_list(words, list, length); + } else { + _scalar_bitset_set_list(words, list, length); + } +} +#else +uint64_t bitset_clear_list(uint64_t *words, uint64_t card, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *(const uint16_t *)list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load & ~(UINT64_C(1) << index); + card -= (load ^ newload) >> index; + words[offset] = newload; + list++; + } + return card; +} + +uint64_t bitset_set_list_withcard(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load | (UINT64_C(1) << index); + card += (load ^ newload) >> index; + words[offset] = newload; + list++; + } + return card; +} + +void bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load | (UINT64_C(1) << index); + words[offset] = newload; + list++; + } +} + +#endif + +/* flip specified bits */ +/* TODO: consider whether worthwhile to make an asm version */ + +uint64_t bitset_flip_list_withcard(uint64_t *words, uint64_t card, const uint16_t *list, + uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load ^ (UINT64_C(1) << index); + // todo: is a branch here all that bad? + card += (1 - 2 * (((UINT64_C(1) << index) & load) >> index)); // +1 or -1 + words[offset] = newload; + list++; + } + return card; +} + +void bitset_flip_list(uint64_t *words, const uint16_t *list, uint64_t length) { + uint64_t offset, load, newload, pos, index; + const uint16_t *end = list + length; + while (list != end) { + pos = *list; + offset = pos >> 6; + index = pos % 64; + load = words[offset]; + newload = load ^ (UINT64_C(1) << index); + words[offset] = newload; + list++; + } +} + +} // namespace internal +} // namespace paimon::roaring +/* end file src/bitset_util.c */ +/* begin file src/containers/array.c */ +/* + * array.c + * + */ + +#include +#include +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { + +inline uint16_t array_container_minimum(const array_container_t *arr); +inline uint16_t array_container_maximum(const array_container_t *arr); +inline int array_container_index_equalorlarger(const array_container_t *arr, uint16_t x); + +inline int array_container_rank(const array_container_t *arr, uint16_t x); +inline uint32_t array_container_rank_many(const array_container_t *arr, uint64_t start_rank, + const uint32_t *begin, const uint32_t *end, + uint64_t *ans); +inline int array_container_get_index(const array_container_t *arr, uint16_t x); +inline bool array_container_contains(const array_container_t *arr, uint16_t pos); +inline int array_container_cardinality(const array_container_t *array); +inline bool array_container_nonzero_cardinality(const array_container_t *array); +inline int32_t array_container_serialized_size_in_bytes(int32_t card); +inline bool array_container_empty(const array_container_t *array); +inline bool array_container_full(const array_container_t *array); + +/* Create a new array with capacity size. Return NULL in case of failure. */ +array_container_t *array_container_create_given_capacity(int32_t size) { + array_container_t *container; + + if ((container = (array_container_t *)roaring_malloc(sizeof(array_container_t))) == NULL) { + return NULL; + } + + if (size <= 0) { // we don't want to rely on malloc(0) + container->array = NULL; + } else if ((container->array = (uint16_t *)roaring_malloc(sizeof(uint16_t) * size)) == NULL) { + roaring_free(container); + return NULL; + } + + container->capacity = size; + container->cardinality = 0; + + return container; +} + +/* Create a new array. Return NULL in case of failure. */ +array_container_t *array_container_create(void) { + return array_container_create_given_capacity(ARRAY_DEFAULT_INIT_SIZE); +} + +/* Create a new array containing all values in [min,max). */ +array_container_t *array_container_create_range(uint32_t min, uint32_t max) { + array_container_t *answer = array_container_create_given_capacity(max - min + 1); + if (answer == NULL) return answer; + answer->cardinality = 0; + for (uint32_t k = min; k < max; k++) { + answer->array[answer->cardinality++] = k; + } + return answer; +} + +/* Duplicate container */ +array_container_t *array_container_clone(const array_container_t *src) { + array_container_t *newcontainer = array_container_create_given_capacity(src->capacity); + if (newcontainer == NULL) return NULL; + + newcontainer->cardinality = src->cardinality; + + memcpy(newcontainer->array, src->array, src->cardinality * sizeof(uint16_t)); + + return newcontainer; +} + +void array_container_offset(const array_container_t *c, container_t **loc, container_t **hic, + uint16_t offset) { + array_container_t *lo = NULL, *hi = NULL; + int top, lo_cap, hi_cap; + + top = (1 << 16) - offset; + + lo_cap = count_less(c->array, c->cardinality, top); + if (loc && lo_cap) { + lo = array_container_create_given_capacity(lo_cap); + for (int i = 0; i < lo_cap; ++i) { + array_container_add(lo, c->array[i] + offset); + } + *loc = (container_t *)lo; + } + + hi_cap = c->cardinality - lo_cap; + if (hic && hi_cap) { + hi = array_container_create_given_capacity(hi_cap); + for (int i = lo_cap; i < c->cardinality; ++i) { + array_container_add(hi, c->array[i] + offset); + } + *hic = (container_t *)hi; + } +} + +int array_container_shrink_to_fit(array_container_t *src) { + if (src->cardinality == src->capacity) return 0; // nothing to do + int savings = src->capacity - src->cardinality; + src->capacity = src->cardinality; + if (src->capacity == 0) { // we do not want to rely on realloc for zero allocs + roaring_free(src->array); + src->array = NULL; + } else { + uint16_t *oldarray = src->array; + src->array = (uint16_t *)roaring_realloc(oldarray, src->capacity * sizeof(uint16_t)); + if (src->array == NULL) roaring_free(oldarray); // should never happen? + } + return savings; +} + +/* Free memory. */ +void array_container_free(array_container_t *arr) { + if (arr->array != NULL) { // Jon Strabala reports that some tools complain otherwise + roaring_free(arr->array); + arr->array = NULL; // pedantic + } + roaring_free(arr); +} + +static inline int32_t grow_capacity(int32_t capacity) { + return (capacity <= 0) ? ARRAY_DEFAULT_INIT_SIZE + : capacity < 64 ? capacity * 2 + : capacity < 1024 ? capacity * 3 / 2 + : capacity * 5 / 4; +} + +static inline int32_t clamp(int32_t val, int32_t min, int32_t max) { + return ((val < min) ? min : (val > max) ? max : val); +} + +void array_container_grow(array_container_t *container, int32_t min, bool preserve) { + int32_t max = (min <= DEFAULT_MAX_SIZE ? DEFAULT_MAX_SIZE : 65536); + int32_t new_capacity = clamp(grow_capacity(container->capacity), min, max); + + container->capacity = new_capacity; + uint16_t *array = container->array; + + if (preserve) { + container->array = (uint16_t *)roaring_realloc(array, new_capacity * sizeof(uint16_t)); + if (container->array == NULL) roaring_free(array); + } else { + // Jon Strabala reports that some tools complain otherwise + if (array != NULL) { + roaring_free(array); + } + container->array = (uint16_t *)roaring_malloc(new_capacity * sizeof(uint16_t)); + } + + // if realloc fails, we have container->array == NULL. +} + +/* Copy one container into another. We assume that they are distinct. */ +void array_container_copy(const array_container_t *src, array_container_t *dst) { + const int32_t cardinality = src->cardinality; + if (cardinality > dst->capacity) { + array_container_grow(dst, cardinality, false); + } + + dst->cardinality = cardinality; + memcpy(dst->array, src->array, cardinality * sizeof(uint16_t)); +} + +void array_container_add_from_range(array_container_t *arr, uint32_t min, uint32_t max, + uint16_t step) { + for (uint32_t value = min; value < max; value += step) { + array_container_append(arr, value); + } +} + +/* Computes the union of array1 and array2 and write the result to arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + */ +void array_container_union(const array_container_t *array_1, const array_container_t *array_2, + array_container_t *out) { + const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; + const int32_t max_cardinality = card_1 + card_2; + + if (out->capacity < max_cardinality) { + array_container_grow(out, max_cardinality, false); + } + out->cardinality = + (int32_t)fast_union_uint16(array_1->array, card_1, array_2->array, card_2, out->array); +} + +/* Computes the difference of array1 and array2 and write the result + * to array out. + * Array out does not need to be distinct from array_1 + */ +void array_container_andnot(const array_container_t *array_1, const array_container_t *array_2, + array_container_t *out) { + if (out->capacity < array_1->cardinality) + array_container_grow(out, array_1->cardinality, false); +#if CROARING_IS_X64 + if ((paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) && + (out != array_1) && (out != array_2)) { + out->cardinality = difference_vector16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); + } else { + out->cardinality = difference_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); + } +#else + out->cardinality = difference_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); +#endif +} + +/* Computes the symmetric difference of array1 and array2 and write the + * result + * to arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + */ +void array_container_xor(const array_container_t *array_1, const array_container_t *array_2, + array_container_t *out) { + const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; + const int32_t max_cardinality = card_1 + card_2; + if (out->capacity < max_cardinality) { + array_container_grow(out, max_cardinality, false); + } + +#if CROARING_IS_X64 + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + out->cardinality = xor_vector16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); + } else { + out->cardinality = xor_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); + } +#else + out->cardinality = xor_uint16(array_1->array, array_1->cardinality, array_2->array, + array_2->cardinality, out->array); +#endif +} + +static inline int32_t minimum_int32(int32_t a, int32_t b) { + return (a < b) ? a : b; +} + +/* computes the intersection of array1 and array2 and write the result to + * arrayout. + * It is assumed that arrayout is distinct from both array1 and array2. + * */ +void array_container_intersection(const array_container_t *array1, const array_container_t *array2, + array_container_t *out) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality, + min_card = minimum_int32(card_1, card_2); + const int threshold = 64; // subject to tuning +#if CROARING_IS_X64 + if (out->capacity < min_card) { + array_container_grow(out, min_card + sizeof(__m128i) / sizeof(uint16_t), false); + } +#else + if (out->capacity < min_card) { + array_container_grow(out, min_card, false); + } +#endif + + if (card_1 * threshold < card_2) { + out->cardinality = + intersect_skewed_uint16(array1->array, card_1, array2->array, card_2, out->array); + } else if (card_2 * threshold < card_1) { + out->cardinality = + intersect_skewed_uint16(array2->array, card_2, array1->array, card_1, out->array); + } else { +#if CROARING_IS_X64 + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + out->cardinality = + intersect_vector16(array1->array, card_1, array2->array, card_2, out->array); + } else { + out->cardinality = + intersect_uint16(array1->array, card_1, array2->array, card_2, out->array); + } +#else + out->cardinality = + intersect_uint16(array1->array, card_1, array2->array, card_2, out->array); +#endif + } +} + +/* computes the size of the intersection of array1 and array2 + * */ +int array_container_intersection_cardinality(const array_container_t *array1, + const array_container_t *array2) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + return intersect_skewed_uint16_cardinality(array1->array, card_1, array2->array, card_2); + } else if (card_2 * threshold < card_1) { + return intersect_skewed_uint16_cardinality(array2->array, card_2, array1->array, card_1); + } else { +#if CROARING_IS_X64 + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + return intersect_vector16_cardinality(array1->array, card_1, array2->array, card_2); + } else { + return intersect_uint16_cardinality(array1->array, card_1, array2->array, card_2); + } +#else + return intersect_uint16_cardinality(array1->array, card_1, array2->array, card_2); +#endif + } +} + +bool array_container_intersect(const array_container_t *array1, const array_container_t *array2) { + int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + return intersect_skewed_uint16_nonempty(array1->array, card_1, array2->array, card_2); + } else if (card_2 * threshold < card_1) { + return intersect_skewed_uint16_nonempty(array2->array, card_2, array1->array, card_1); + } else { + // we do not bother vectorizing + return intersect_uint16_nonempty(array1->array, card_1, array2->array, card_2); + } +} + +/* computes the intersection of array1 and array2 and write the result to + * array1. + * */ +void array_container_intersection_inplace(array_container_t *src_1, + const array_container_t *src_2) { + int32_t card_1 = src_1->cardinality, card_2 = src_2->cardinality; + const int threshold = 64; // subject to tuning + if (card_1 * threshold < card_2) { + src_1->cardinality = + intersect_skewed_uint16(src_1->array, card_1, src_2->array, card_2, src_1->array); + } else if (card_2 * threshold < card_1) { + src_1->cardinality = + intersect_skewed_uint16(src_2->array, card_2, src_1->array, card_1, src_1->array); + } else { +#if CROARING_IS_X64 + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { + src_1->cardinality = + intersect_vector16_inplace(src_1->array, card_1, src_2->array, card_2); + } else { + src_1->cardinality = + intersect_uint16(src_1->array, card_1, src_2->array, card_2, src_1->array); + } +#else + src_1->cardinality = + intersect_uint16(src_1->array, card_1, src_2->array, card_2, src_1->array); +#endif + } +} + +ALLOW_UNALIGNED +int array_container_to_uint32_array(void *vout, const array_container_t *cont, uint32_t base) { +#if CROARING_IS_X64 + int support = paimon::roaring::internal::croaring_hardware_support(); +#if CROARING_COMPILER_SUPPORTS_AVX512 + if (support & ROARING_SUPPORTS_AVX512) { + return avx512_array_container_to_uint32_array(vout, cont->array, cont->cardinality, base); + } +#endif + if (support & ROARING_SUPPORTS_AVX2) { + return array_container_to_uint32_array_vector16(vout, cont->array, cont->cardinality, base); + } +#endif // CROARING_IS_X64 + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + size_t i = 0; + for (; i < (size_t)cont->cardinality; ++i) { + const uint32_t val = base + cont->array[i]; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + return outpos; +} + +void array_container_printf(const array_container_t *v) { + if (v->cardinality == 0) { + printf("{}"); + return; + } + printf("{"); + printf("%d", v->array[0]); + for (int i = 1; i < v->cardinality; ++i) { + printf(",%d", v->array[i]); + } + printf("}"); +} + +void array_container_printf_as_uint32_array(const array_container_t *v, uint32_t base) { + if (v->cardinality == 0) { + return; + } + printf("%u", v->array[0] + base); + for (int i = 1; i < v->cardinality; ++i) { + printf(",%u", v->array[i] + base); + } +} + +/* + * Validate the container. Returns true if valid. + */ +bool array_container_validate(const array_container_t *v, const char **reason) { + if (v->capacity < 0) { + *reason = "negative capacity"; + return false; + } + if (v->cardinality < 0) { + *reason = "negative cardinality"; + return false; + } + if (v->cardinality > v->capacity) { + *reason = "cardinality exceeds capacity"; + return false; + } + if (v->cardinality > DEFAULT_MAX_SIZE) { + *reason = "cardinality exceeds DEFAULT_MAX_SIZE"; + return false; + } + if (v->cardinality == 0) { + return true; + } + + if (v->array == NULL) { + *reason = "NULL array pointer"; + return false; + } + uint16_t prev = v->array[0]; + for (int i = 1; i < v->cardinality; ++i) { + if (v->array[i] <= prev) { + *reason = "array elements not strictly increasing"; + return false; + } + prev = v->array[i]; + } + + return true; +} + +/* Compute the number of runs */ +int32_t array_container_number_of_runs(const array_container_t *ac) { + // Can SIMD work here? + int32_t nr_runs = 0; + int32_t prev = -2; + for (const uint16_t *p = ac->array; p != ac->array + ac->cardinality; ++p) { + if (*p != prev + 1) nr_runs++; + prev = *p; + } + return nr_runs; +} + +/** + * Writes the underlying array to buf, outputs how many bytes were written. + * The number of bytes written should be + * array_container_size_in_bytes(container). + * + */ +int32_t array_container_write(const array_container_t *container, char *buf) { + memcpy(buf, container->array, container->cardinality * sizeof(uint16_t)); + return array_container_size_in_bytes(container); +} + +bool array_container_is_subset(const array_container_t *container1, + const array_container_t *container2) { + if (container1->cardinality > container2->cardinality) { + return false; + } + int i1 = 0, i2 = 0; + while (i1 < container1->cardinality && i2 < container2->cardinality) { + if (container1->array[i1] == container2->array[i2]) { + i1++; + i2++; + } else if (container1->array[i1] > container2->array[i2]) { + i2++; + } else { // container1->array[i1] < container2->array[i2] + return false; + } + } + if (i1 == container1->cardinality) { + return true; + } else { + return false; + } +} + +int32_t array_container_read(int32_t cardinality, array_container_t *container, const char *buf) { + if (container->capacity < cardinality) { + array_container_grow(container, cardinality, false); + } + container->cardinality = cardinality; + memcpy(container->array, buf, container->cardinality * sizeof(uint16_t)); + + return array_container_size_in_bytes(container); +} + +bool array_container_iterate(const array_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr) { + for (int i = 0; i < cont->cardinality; i++) + if (!iterator(cont->array[i] + base, ptr)) return false; + return true; +} + +bool array_container_iterate64(const array_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, void *ptr) { + for (int i = 0; i < cont->cardinality; i++) + if (!iterator(high_bits | (uint64_t)(cont->array[i] + base), ptr)) return false; + return true; +} + +} // namespace internal +} // namespace paimon::roaring +/* end file src/containers/array.c */ +/* begin file src/containers/bitset.c */ +/* + * bitset.c + * + */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif +#include +#include +#include +#include + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { +namespace internal { + +inline int bitset_container_cardinality(const bitset_container_t *bitset); +inline void bitset_container_set(bitset_container_t *bitset, uint16_t pos); +// unused at this time: +// inline void bitset_container_unset(bitset_container_t *bitset, uint16_t pos); +inline bool bitset_container_get(const bitset_container_t *bitset, uint16_t pos); +inline int32_t bitset_container_serialized_size_in_bytes(void); +inline bool bitset_container_add(bitset_container_t *bitset, uint16_t pos); +inline bool bitset_container_remove(bitset_container_t *bitset, uint16_t pos); +inline bool bitset_container_contains(const bitset_container_t *bitset, uint16_t pos); + +void bitset_container_clear(bitset_container_t *bitset) { + memset(bitset->words, 0, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + bitset->cardinality = 0; +} + +void bitset_container_set_all(bitset_container_t *bitset) { + memset(bitset->words, INT64_C(-1), sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + bitset->cardinality = (1 << 16); +} + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_container_t *bitset_container_create(void) { + bitset_container_t *bitset = (bitset_container_t *)roaring_malloc(sizeof(bitset_container_t)); + + if (!bitset) { + return NULL; + } + + size_t align_size = 32; +#if CROARING_IS_X64 + int support = paimon::roaring::internal::croaring_hardware_support(); + if (support & ROARING_SUPPORTS_AVX512) { + // sizeof(__m512i) == 64 + align_size = 64; + } else { + // sizeof(__m256i) == 32 + align_size = 32; + } +#endif + bitset->words = (uint64_t *)roaring_aligned_malloc( + align_size, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + if (!bitset->words) { + roaring_free(bitset); + return NULL; + } + bitset_container_clear(bitset); + return bitset; +} + +/* Copy one container into another. We assume that they are distinct. */ +void bitset_container_copy(const bitset_container_t *source, bitset_container_t *dest) { + dest->cardinality = source->cardinality; + memcpy(dest->words, source->words, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); +} + +void bitset_container_add_from_range(bitset_container_t *bitset, uint32_t min, uint32_t max, + uint16_t step) { + if (step == 0) return; // refuse to crash + if ((64 % step) == 0) { // step divides 64 + uint64_t mask = 0; // construct the repeated mask + for (uint32_t value = (min % step); value < 64; value += step) { + mask |= ((uint64_t)1 << value); + } + uint32_t firstword = min / 64; + uint32_t endword = (max - 1) / 64; + bitset->cardinality = (max - min + step - 1) / step; + if (firstword == endword) { + bitset->words[firstword] |= + mask & (((~UINT64_C(0)) << (min % 64)) & ((~UINT64_C(0)) >> ((~max + 1) % 64))); + return; + } + bitset->words[firstword] = mask & ((~UINT64_C(0)) << (min % 64)); + for (uint32_t i = firstword + 1; i < endword; i++) bitset->words[i] = mask; + bitset->words[endword] = mask & ((~UINT64_C(0)) >> ((~max + 1) % 64)); + } else { + for (uint32_t value = min; value < max; value += step) { + bitset_container_add(bitset, value); + } + } +} + +/* Free memory. */ +void bitset_container_free(bitset_container_t *bitset) { + if (bitset->words != NULL) { // Jon Strabala reports that some tools complain otherwise + roaring_aligned_free(bitset->words); + bitset->words = NULL; // pedantic + } + roaring_free(bitset); +} + +/* duplicate container. */ +bitset_container_t *bitset_container_clone(const bitset_container_t *src) { + bitset_container_t *bitset = (bitset_container_t *)roaring_malloc(sizeof(bitset_container_t)); + + if (!bitset) { + return NULL; + } + + size_t align_size = 32; +#if CROARING_IS_X64 + if (paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX512) { + // sizeof(__m512i) == 64 + align_size = 64; + } else { + // sizeof(__m256i) == 32 + align_size = 32; + } +#endif + bitset->words = (uint64_t *)roaring_aligned_malloc( + align_size, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + if (!bitset->words) { + roaring_free(bitset); + return NULL; + } + bitset->cardinality = src->cardinality; + memcpy(bitset->words, src->words, sizeof(uint64_t) * BITSET_CONTAINER_SIZE_IN_WORDS); + return bitset; +} + +void bitset_container_offset(const bitset_container_t *c, container_t **loc, container_t **hic, + uint16_t offset) { + bitset_container_t *bc = NULL; + uint64_t val; + uint16_t b, i, end; + + b = offset >> 6; + i = offset % 64; + end = 1024 - b; + + if (loc != NULL) { + bc = bitset_container_create(); + if (i == 0) { + memcpy(bc->words + b, c->words, 8 * end); + } else { + bc->words[b] = c->words[0] << i; + for (uint32_t k = 1; k < end; ++k) { + val = c->words[k] << i; + val |= c->words[k - 1] >> (64 - i); + bc->words[b + k] = val; + } + } + + bc->cardinality = bitset_container_compute_cardinality(bc); + if (bc->cardinality != 0) { + *loc = bc; + } + if (bc->cardinality == c->cardinality) { + return; + } + } + + if (hic == NULL) { + // Both hic and loc can't be NULL, so bc is never NULL here + if (bc->cardinality == 0) { + bitset_container_free(bc); + } + return; + } + + if (bc == NULL || bc->cardinality != 0) { + bc = bitset_container_create(); + } + + if (i == 0) { + memcpy(bc->words, c->words + end, 8 * b); + } else { + for (uint32_t k = end; k < 1024; ++k) { + val = c->words[k] << i; + val |= c->words[k - 1] >> (64 - i); + bc->words[k - end] = val; + } + bc->words[b] = c->words[1023] >> (64 - i); + } + + bc->cardinality = bitset_container_compute_cardinality(bc); + if (bc->cardinality == 0) { + bitset_container_free(bc); + return; + } + *hic = bc; +} + +void bitset_container_set_range(bitset_container_t *bitset, uint32_t begin, uint32_t end) { + bitset_set_range(bitset->words, begin, end); + bitset->cardinality = bitset_container_compute_cardinality(bitset); // could be smarter +} + +bool bitset_container_intersect(const bitset_container_t *src_1, const bitset_container_t *src_2) { + // could vectorize, but this is probably already quite fast in practice + const uint64_t *__restrict__ words_1 = src_1->words; + const uint64_t *__restrict__ words_2 = src_2->words; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) { + if ((words_1[i] & words_2[i]) != 0) return true; + } + return false; +} + +#if CROARING_IS_X64 +#ifndef WORDS_IN_AVX2_REG +#define WORDS_IN_AVX2_REG sizeof(__m256i) / sizeof(uint64_t) +#endif +#ifndef WORDS_IN_AVX512_REG +#define WORDS_IN_AVX512_REG sizeof(__m512i) / sizeof(uint64_t) +#endif +/* Get the number of bits set (force computation) */ +static inline int _scalar_bitset_container_compute_cardinality(const bitset_container_t *bitset) { + const uint64_t *words = bitset->words; + int32_t sum = 0; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 4) { + sum += roaring_hamming(words[i]); + sum += roaring_hamming(words[i + 1]); + sum += roaring_hamming(words[i + 2]); + sum += roaring_hamming(words[i + 3]); + } + return sum; +} +/* Get the number of bits set (force computation) */ +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + int support = paimon::roaring::internal::croaring_hardware_support(); +#if CROARING_COMPILER_SUPPORTS_AVX512 + if (support & ROARING_SUPPORTS_AVX512) { + return (int)avx512_vpopcount((const __m512i *)bitset->words, + BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX512_REG)); + } else +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + if (support & ROARING_SUPPORTS_AVX2) { + return (int)avx2_harley_seal_popcount256( + (const __m256i *)bitset->words, + BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG)); + } else { + return _scalar_bitset_container_compute_cardinality(bitset); + } +} + +#elif defined(CROARING_USENEON) +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + uint16x8_t n0 = vdupq_n_u16(0); + uint16x8_t n1 = vdupq_n_u16(0); + uint16x8_t n2 = vdupq_n_u16(0); + uint16x8_t n3 = vdupq_n_u16(0); + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { + uint64x2_t c0 = vld1q_u64(&bitset->words[i + 0]); + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); + uint64x2_t c1 = vld1q_u64(&bitset->words[i + 2]); + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); + uint64x2_t c2 = vld1q_u64(&bitset->words[i + 4]); + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); + uint64x2_t c3 = vld1q_u64(&bitset->words[i + 6]); + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); + } + uint64x2_t n = vdupq_n_u64(0); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); + return vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); +} + +#else // CROARING_IS_X64 + +/* Get the number of bits set (force computation) */ +int bitset_container_compute_cardinality(const bitset_container_t *bitset) { + const uint64_t *words = bitset->words; + int32_t sum = 0; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 4) { + sum += roaring_hamming(words[i]); + sum += roaring_hamming(words[i + 1]); + sum += roaring_hamming(words[i + 2]); + sum += roaring_hamming(words[i + 3]); + } + return sum; +} + +#endif // CROARING_IS_X64 + +#if CROARING_IS_X64 + +#define BITSET_CONTAINER_FN_REPEAT 8 +#ifndef WORDS_IN_AVX512_REG +#define WORDS_IN_AVX512_REG sizeof(__m512i) / sizeof(uint64_t) +#endif // WORDS_IN_AVX512_REG + +/* Computes a binary operation (eg union) on bitset1 and bitset2 and write the + result to bitsetout */ +// clang-format off +#define AVX512_BITSET_CONTAINER_FN1(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + static inline int _avx512_bitset_container_##opname##_nocard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint8_t * __restrict__ words_1 = (const uint8_t *)src_1->words; \ + const uint8_t * __restrict__ words_2 = (const uint8_t *)src_2->words; \ + /* not using the blocking optimization for some reason*/ \ + uint8_t *out = (uint8_t*)dst->words; \ + const int innerloop = 8; \ + for (size_t i = 0; \ + i < BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX512_REG); \ + i+=innerloop) { \ + __m512i A1, A2, AO; \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)out, AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 64)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 64)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+64), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 128)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 128)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+128), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 192)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 192)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+192), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 256)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 256)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+256), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 320)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 320)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+320), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 384)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 384)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+384), AO); \ + A1 = _mm512_loadu_si512((const __m512i *)(words_1 + 448)); \ + A2 = _mm512_loadu_si512((const __m512i *)(words_2 + 448)); \ + AO = avx_intrinsic(A2, A1); \ + _mm512_storeu_si512((__m512i *)(out+448), AO); \ + out+=512; \ + words_1 += 512; \ + words_2 += 512; \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ + } + +#define AVX512_BITSET_CONTAINER_FN2(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + /* next, a version that updates cardinality*/ \ + static inline int _avx512_bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const __m512i * __restrict__ words_1 = (const __m512i *) src_1->words; \ + const __m512i * __restrict__ words_2 = (const __m512i *) src_2->words; \ + __m512i *out = (__m512i *) dst->words; \ + dst->cardinality = (int32_t)avx512_harley_seal_popcount512andstore_##opname(words_2,\ + words_1, out,BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX512_REG)); \ + return dst->cardinality; \ + } + +#define AVX512_BITSET_CONTAINER_FN3(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + /* next, a version that just computes the cardinality*/ \ + static inline int _avx512_bitset_container_##opname##_justcard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2) { \ + const __m512i * __restrict__ data1 = (const __m512i *) src_1->words; \ + const __m512i * __restrict__ data2 = (const __m512i *) src_2->words; \ + return (int)avx512_harley_seal_popcount512_##opname(data2, \ + data1, BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX512_REG)); \ + } + + +// we duplicate the function because other containers use the "or" term, makes API more consistent +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, or, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, union, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, and, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, intersection, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, xor, ^, _mm512_xor_si512, veorq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX512, andnot, &~, _mm512_andnot_si512, vbicq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +// we duplicate the function because other containers use the "or" term, makes API more consistent +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, or, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, union, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, and, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, intersection, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, xor, ^, _mm512_xor_si512, veorq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX512, andnot, &~, _mm512_andnot_si512, vbicq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +// we duplicate the function because other containers use the "or" term, makes API more consistent +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, or, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, union, |, _mm512_or_si512, vorrq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, and, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, intersection, &, _mm512_and_si512, vandq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 + +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, xor, ^, _mm512_xor_si512, veorq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +CROARING_TARGET_AVX512 +AVX512_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX512, andnot, &~, _mm512_andnot_si512, vbicq_u64, CROARING_UNTARGET_AVX512) +CROARING_UNTARGET_AVX512 +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + +#ifndef WORDS_IN_AVX2_REG +#define WORDS_IN_AVX2_REG sizeof(__m256i) / sizeof(uint64_t) +#endif // WORDS_IN_AVX2_REG +#define LOOP_SIZE \ + BITSET_CONTAINER_SIZE_IN_WORDS / \ + ((WORDS_IN_AVX2_REG)*BITSET_CONTAINER_FN_REPEAT) + +/* Computes a binary operation (eg union) on bitset1 and bitset2 and write the + result to bitsetout */ +// clang-format off +#define AVX_BITSET_CONTAINER_FN1(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + static inline int _avx2_bitset_container_##opname##_nocard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint8_t *__restrict__ words_1 = (const uint8_t *)src_1->words; \ + const uint8_t *__restrict__ words_2 = (const uint8_t *)src_2->words; \ + /* not using the blocking optimization for some reason*/ \ + uint8_t *out = (uint8_t *)dst->words; \ + const int innerloop = 8; \ + for (size_t i = 0; \ + i < BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG); \ + i += innerloop) { \ + __m256i A1, A2, AO; \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)out, AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 32)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 32)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 32), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 64)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 64)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 64), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 96)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 96)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 96), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 128)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 128)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 128), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 160)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 160)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 160), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 192)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 192)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 192), AO); \ + A1 = _mm256_lddqu_si256((const __m256i *)(words_1 + 224)); \ + A2 = _mm256_lddqu_si256((const __m256i *)(words_2 + 224)); \ + AO = avx_intrinsic(A2, A1); \ + _mm256_storeu_si256((__m256i *)(out + 224), AO); \ + out += 256; \ + words_1 += 256; \ + words_2 += 256; \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ + } + +#define AVX_BITSET_CONTAINER_FN2(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + /* next, a version that updates cardinality*/ \ + static inline int _avx2_bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const __m256i *__restrict__ words_1 = (const __m256i *)src_1->words; \ + const __m256i *__restrict__ words_2 = (const __m256i *)src_2->words; \ + __m256i *out = (__m256i *)dst->words; \ + dst->cardinality = (int32_t)avx2_harley_seal_popcount256andstore_##opname( \ + words_2, words_1, out, \ + BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG)); \ + return dst->cardinality; \ + } \ + +#define AVX_BITSET_CONTAINER_FN3(before, opname, opsymbol, avx_intrinsic, \ + neon_intrinsic, after) \ + /* next, a version that just computes the cardinality*/ \ + static inline int _avx2_bitset_container_##opname##_justcard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2) { \ + const __m256i *__restrict__ data1 = (const __m256i *)src_1->words; \ + const __m256i *__restrict__ data2 = (const __m256i *)src_2->words; \ + return (int)avx2_harley_seal_popcount256_##opname( \ + data2, data1, BITSET_CONTAINER_SIZE_IN_WORDS / (WORDS_IN_AVX2_REG)); \ + } + + +// we duplicate the function because other containers use the "or" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, or, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, union, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, and, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, intersection, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, xor, ^, _mm256_xor_si256, veorq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN1(CROARING_TARGET_AVX2, andnot, &~, _mm256_andnot_si256, vbicq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +// we duplicate the function because other containers use the "or" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, or, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, union, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, and, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, intersection, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, xor, ^, _mm256_xor_si256, veorq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN2(CROARING_TARGET_AVX2, andnot, &~, _mm256_andnot_si256, vbicq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +// we duplicate the function because other containers use the "or" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, or, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, union, |, _mm256_or_si256, vorrq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, and, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, intersection, &, _mm256_and_si256, vandq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, xor, ^, _mm256_xor_si256, veorq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 +CROARING_TARGET_AVX2 +AVX_BITSET_CONTAINER_FN3(CROARING_TARGET_AVX2, andnot, &~, _mm256_andnot_si256, vbicq_u64, CROARING_UNTARGET_AVX2) +CROARING_UNTARGET_AVX2 + + +#define SCALAR_BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, \ + neon_intrinsic) \ + static inline int _scalar_bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t *__restrict__ words_1 = src_1->words; \ + const uint64_t *__restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (words_1[i])opsymbol(words_2[i]), \ + word_2 = (words_1[i + 1]) opsymbol(words_2[i + 1]); \ + out[i] = word_1; \ + out[i + 1] = word_2; \ + sum += roaring_hamming(word_1); \ + sum += roaring_hamming(word_2); \ + } \ + dst->cardinality = sum; \ + return dst->cardinality; \ + } \ + static inline int _scalar_bitset_container_##opname##_nocard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t *__restrict__ words_1 = src_1->words; \ + const uint64_t *__restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) { \ + out[i] = (words_1[i])opsymbol(words_2[i]); \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ + } \ + static inline int _scalar_bitset_container_##opname##_justcard( \ + const bitset_container_t *src_1, const bitset_container_t *src_2) { \ + const uint64_t *__restrict__ words_1 = src_1->words; \ + const uint64_t *__restrict__ words_2 = src_2->words; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (words_1[i])opsymbol(words_2[i]), \ + word_2 = (words_1[i + 1]) opsymbol(words_2[i + 1]); \ + sum += roaring_hamming(word_1); \ + sum += roaring_hamming(word_2); \ + } \ + return sum; \ + } + +// we duplicate the function because other containers use the "or" term, makes API more consistent +SCALAR_BITSET_CONTAINER_FN(or, |, _mm256_or_si256, vorrq_u64) +SCALAR_BITSET_CONTAINER_FN(union, |, _mm256_or_si256, vorrq_u64) + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +SCALAR_BITSET_CONTAINER_FN(and, &, _mm256_and_si256, vandq_u64) +SCALAR_BITSET_CONTAINER_FN(intersection, &, _mm256_and_si256, vandq_u64) + +SCALAR_BITSET_CONTAINER_FN(xor, ^, _mm256_xor_si256, veorq_u64) +SCALAR_BITSET_CONTAINER_FN(andnot, &~, _mm256_andnot_si256, vbicq_u64) + +#if CROARING_COMPILER_SUPPORTS_AVX512 +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ + int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + int support = paimon::roaring::internal::croaring_hardware_support(); \ + if ( support & ROARING_SUPPORTS_AVX512 ) { \ + return _avx512_bitset_container_##opname(src_1, src_2, dst); \ + } \ + else if ( support & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname(src_1, src_2, dst); \ + } else { \ + return _scalar_bitset_container_##opname(src_1, src_2, dst); \ + } \ + } \ + int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + int support = paimon::roaring::internal::croaring_hardware_support(); \ + if ( support & ROARING_SUPPORTS_AVX512 ) { \ + return _avx512_bitset_container_##opname##_nocard(src_1, src_2, dst); \ + } \ + else if ( support & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname##_nocard(src_1, src_2, dst); \ + } else { \ + return _scalar_bitset_container_##opname##_nocard(src_1, src_2, dst); \ + } \ + } \ + int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + int support = paimon::roaring::internal::croaring_hardware_support(); \ + if ( support & ROARING_SUPPORTS_AVX512 ) { \ + return _avx512_bitset_container_##opname##_justcard(src_1, src_2); \ + } \ + else if ( support & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname##_justcard(src_1, src_2); \ + } else { \ + return _scalar_bitset_container_##opname##_justcard(src_1, src_2); \ + } \ + } + +#else // CROARING_COMPILER_SUPPORTS_AVX512 + + +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ + int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + if ( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname(src_1, src_2, dst); \ + } else { \ + return _scalar_bitset_container_##opname(src_1, src_2, dst); \ + } \ + } \ + int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + if ( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname##_nocard(src_1, src_2, dst); \ + } else { \ + return _scalar_bitset_container_##opname##_nocard(src_1, src_2, dst); \ + } \ + } \ + int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + if ( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2 ) { \ + return _avx2_bitset_container_##opname##_justcard(src_1, src_2); \ + } else { \ + return _scalar_bitset_container_##opname##_justcard(src_1, src_2); \ + } \ + } + +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + +#elif defined(CROARING_USENEON) + +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ +int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + uint16x8_t n0 = vdupq_n_u16(0); \ + uint16x8_t n1 = vdupq_n_u16(0); \ + uint16x8_t n2 = vdupq_n_u16(0); \ + uint16x8_t n3 = vdupq_n_u16(0); \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + uint64x2_t c0 = neon_intrinsic(vld1q_u64(&words_1[i + 0]), \ + vld1q_u64(&words_2[i + 0])); \ + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); \ + vst1q_u64(&out[i + 0], c0); \ + uint64x2_t c1 = neon_intrinsic(vld1q_u64(&words_1[i + 2]), \ + vld1q_u64(&words_2[i + 2])); \ + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); \ + vst1q_u64(&out[i + 2], c1); \ + uint64x2_t c2 = neon_intrinsic(vld1q_u64(&words_1[i + 4]), \ + vld1q_u64(&words_2[i + 4])); \ + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); \ + vst1q_u64(&out[i + 4], c2); \ + uint64x2_t c3 = neon_intrinsic(vld1q_u64(&words_1[i + 6]), \ + vld1q_u64(&words_2[i + 6])); \ + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); \ + vst1q_u64(&out[i + 6], c3); \ + } \ + uint64x2_t n = vdupq_n_u64(0); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); \ + dst->cardinality = vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + vst1q_u64(&out[i + 0], neon_intrinsic(vld1q_u64(&words_1[i + 0]), \ + vld1q_u64(&words_2[i + 0]))); \ + vst1q_u64(&out[i + 2], neon_intrinsic(vld1q_u64(&words_1[i + 2]), \ + vld1q_u64(&words_2[i + 2]))); \ + vst1q_u64(&out[i + 4], neon_intrinsic(vld1q_u64(&words_1[i + 4]), \ + vld1q_u64(&words_2[i + 4]))); \ + vst1q_u64(&out[i + 6], neon_intrinsic(vld1q_u64(&words_1[i + 6]), \ + vld1q_u64(&words_2[i + 6]))); \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + uint16x8_t n0 = vdupq_n_u16(0); \ + uint16x8_t n1 = vdupq_n_u16(0); \ + uint16x8_t n2 = vdupq_n_u16(0); \ + uint16x8_t n3 = vdupq_n_u16(0); \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 8) { \ + uint64x2_t c0 = neon_intrinsic(vld1q_u64(&words_1[i + 0]), \ + vld1q_u64(&words_2[i + 0])); \ + n0 = vaddq_u16(n0, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c0)))); \ + uint64x2_t c1 = neon_intrinsic(vld1q_u64(&words_1[i + 2]), \ + vld1q_u64(&words_2[i + 2])); \ + n1 = vaddq_u16(n1, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c1)))); \ + uint64x2_t c2 = neon_intrinsic(vld1q_u64(&words_1[i + 4]), \ + vld1q_u64(&words_2[i + 4])); \ + n2 = vaddq_u16(n2, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c2)))); \ + uint64x2_t c3 = neon_intrinsic(vld1q_u64(&words_1[i + 6]), \ + vld1q_u64(&words_2[i + 6])); \ + n3 = vaddq_u16(n3, vpaddlq_u8(vcntq_u8(vreinterpretq_u8_u64(c3)))); \ + } \ + uint64x2_t n = vdupq_n_u64(0); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n0))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n1))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n2))); \ + n = vaddq_u64(n, vpaddlq_u32(vpaddlq_u16(n3))); \ + return vgetq_lane_u64(n, 0) + vgetq_lane_u64(n, 1); \ +} + +#else + +#define BITSET_CONTAINER_FN(opname, opsymbol, avx_intrinsic, neon_intrinsic) \ +int bitset_container_##opname(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (words_1[i])opsymbol(words_2[i]), \ + word_2 = (words_1[i + 1])opsymbol(words_2[i + 1]); \ + out[i] = word_1; \ + out[i + 1] = word_2; \ + sum += roaring_hamming(word_1); \ + sum += roaring_hamming(word_2); \ + } \ + dst->cardinality = sum; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_nocard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2, \ + bitset_container_t *dst) { \ + const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + uint64_t *out = dst->words; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) { \ + out[i] = (words_1[i])opsymbol(words_2[i]); \ + } \ + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; \ + return dst->cardinality; \ +} \ +int bitset_container_##opname##_justcard(const bitset_container_t *src_1, \ + const bitset_container_t *src_2) { \ + printf("A1\n"); const uint64_t * __restrict__ words_1 = src_1->words; \ + const uint64_t * __restrict__ words_2 = src_2->words; \ + int32_t sum = 0; \ + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 2) { \ + const uint64_t word_1 = (words_1[i])opsymbol(words_2[i]), \ + word_2 = (words_1[i + 1])opsymbol(words_2[i + 1]); \ + sum += roaring_hamming(word_1); \ + sum += roaring_hamming(word_2); \ + } \ + return sum; \ +} + +#endif // CROARING_IS_X64 + +// we duplicate the function because other containers use the "or" term, makes API more consistent +BITSET_CONTAINER_FN(or, |, _mm256_or_si256, vorrq_u64) +BITSET_CONTAINER_FN(union, |, _mm256_or_si256, vorrq_u64) + +// we duplicate the function because other containers use the "intersection" term, makes API more consistent +BITSET_CONTAINER_FN(and, &, _mm256_and_si256, vandq_u64) +BITSET_CONTAINER_FN(intersection, &, _mm256_and_si256, vandq_u64) + +BITSET_CONTAINER_FN(xor, ^, _mm256_xor_si256, veorq_u64) +BITSET_CONTAINER_FN(andnot, &~, _mm256_andnot_si256, vbicq_u64) +// clang-format On + + +ALLOW_UNALIGNED +int bitset_container_to_uint32_array( + uint32_t *out, + const bitset_container_t *bc, + uint32_t base +){ +#if CROARING_IS_X64 + int support = paimon::roaring::internal::croaring_hardware_support(); +#if CROARING_COMPILER_SUPPORTS_AVX512 + if(( support & ROARING_SUPPORTS_AVX512 ) && (bc->cardinality >= 8192)) // heuristic + return (int) bitset_extract_setbits_avx512(bc->words, + BITSET_CONTAINER_SIZE_IN_WORDS, out, bc->cardinality, base); + else +#endif + if(( support & ROARING_SUPPORTS_AVX2 ) && (bc->cardinality >= 8192)) // heuristic + return (int) bitset_extract_setbits_avx2(bc->words, + BITSET_CONTAINER_SIZE_IN_WORDS, out, bc->cardinality, base); + else + return (int) bitset_extract_setbits(bc->words, + BITSET_CONTAINER_SIZE_IN_WORDS, out, base); +#else + return (int) bitset_extract_setbits(bc->words, + BITSET_CONTAINER_SIZE_IN_WORDS, out, base); +#endif +} + +/* + * Print this container using printf (useful for debugging). + */ +void bitset_container_printf(const bitset_container_t * v) { + printf("{"); + uint32_t base = 0; + bool iamfirst = true;// TODO: rework so that this is not necessary yet still readable + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = v->words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if(iamfirst) {// predicted to be false + printf("%u",base + r); + iamfirst = false; + } else { + printf(",%u",base + r); + } + w ^= t; + } + base += 64; + } + printf("}"); +} + + +/* + * Print this container using printf as a comma-separated list of 32-bit integers starting at base. + */ +void bitset_container_printf_as_uint32_array(const bitset_container_t * v, uint32_t base) { + bool iamfirst = true;// TODO: rework so that this is not necessary yet still readable + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = v->words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if(iamfirst) {// predicted to be false + printf("%u", r + base); + iamfirst = false; + } else { + printf(",%u",r + base); + } + w ^= t; + } + base += 64; + } +} + +/* + * Validate the container. Returns true if valid. + */ +bool bitset_container_validate(const bitset_container_t *v, const char **reason) { + if (v->words == NULL) { + *reason = "words is NULL"; + return false; + } + if (v->cardinality != bitset_container_compute_cardinality(v)) { + *reason = "cardinality is incorrect"; + return false; + } + // Attempt to forcibly load the first and last words, hopefully causing + // a segfault or an address sanitizer error if words is not allocated. + volatile uint64_t *words = v->words; + (void) words[0]; + (void) words[BITSET_CONTAINER_SIZE_IN_WORDS - 1]; + return true; +} + + +// TODO: use the fast lower bound, also +int bitset_container_number_of_runs(bitset_container_t *bc) { + int num_runs = 0; + uint64_t next_word = bc->words[0]; + + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS-1; ++i) { + uint64_t word = next_word; + next_word = bc->words[i+1]; + num_runs += roaring_hamming((~word) & (word << 1)) + ( (word >> 63) & ~next_word); + } + + uint64_t word = next_word; + num_runs += roaring_hamming((~word) & (word << 1)); + if((word & 0x8000000000000000ULL) != 0) + num_runs++; + return num_runs; +} + + +int32_t bitset_container_write(const bitset_container_t *container, + char *buf) { + memcpy(buf, container->words, BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + return bitset_container_size_in_bytes(container); +} + + +int32_t bitset_container_read(int32_t cardinality, bitset_container_t *container, + const char *buf) { + container->cardinality = cardinality; + memcpy(container->words, buf, BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + return bitset_container_size_in_bytes(container); +} + +bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base, roaring_iterator iterator, void *ptr) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = cont->words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if(!iterator(r + base, ptr)) return false; + w ^= t; + } + base += 64; + } + return true; +} + +bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base, roaring_iterator64 iterator, uint64_t high_bits, void *ptr) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = cont->words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if(!iterator(high_bits | (uint64_t)(r + base), ptr)) return false; + w ^= t; + } + base += 64; + } + return true; +} + +#if CROARING_IS_X64 +#if CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX512 +ALLOW_UNALIGNED +static inline bool _avx512_bitset_container_equals(const bitset_container_t *container1, const bitset_container_t *container2) { + const __m512i *ptr1 = (const __m512i*)container1->words; + const __m512i *ptr2 = (const __m512i*)container2->words; + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS*sizeof(uint64_t)/64; i++) { + __m512i r1 = _mm512_loadu_si512(ptr1+i); + __m512i r2 = _mm512_loadu_si512(ptr2+i); + __mmask64 mask = _mm512_cmpeq_epi8_mask(r1, r2); + if ((uint64_t)mask != UINT64_MAX) { + return false; + } + } + return true; +} +CROARING_UNTARGET_AVX512 +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +CROARING_TARGET_AVX2 +ALLOW_UNALIGNED +static inline bool _avx2_bitset_container_equals(const bitset_container_t *container1, const bitset_container_t *container2) { + const __m256i *ptr1 = (const __m256i*)container1->words; + const __m256i *ptr2 = (const __m256i*)container2->words; + for (size_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS*sizeof(uint64_t)/32; i++) { + __m256i r1 = _mm256_loadu_si256(ptr1+i); + __m256i r2 = _mm256_loadu_si256(ptr2+i); + int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(r1, r2)); + if ((uint32_t)mask != UINT32_MAX) { + return false; + } + } + return true; +} +CROARING_UNTARGET_AVX2 +#endif // CROARING_IS_X64 + +ALLOW_UNALIGNED +bool bitset_container_equals(const bitset_container_t *container1, const bitset_container_t *container2) { + if((container1->cardinality != BITSET_UNKNOWN_CARDINALITY) && (container2->cardinality != BITSET_UNKNOWN_CARDINALITY)) { + if(container1->cardinality != container2->cardinality) { + return false; + } + if (container1->cardinality == INT32_C(0x10000)) { + return true; + } + } +#if CROARING_IS_X64 + int support = paimon::roaring::internal::croaring_hardware_support(); +#if CROARING_COMPILER_SUPPORTS_AVX512 + if( support & ROARING_SUPPORTS_AVX512 ) { + return _avx512_bitset_container_equals(container1, container2); + } + else +#endif + if( support & ROARING_SUPPORTS_AVX2 ) { + return _avx2_bitset_container_equals(container1, container2); + } +#endif + return memcmp(container1->words, + container2->words, + BITSET_CONTAINER_SIZE_IN_WORDS*sizeof(uint64_t)) == 0; +} + +bool bitset_container_is_subset(const bitset_container_t *container1, + const bitset_container_t *container2) { + if((container1->cardinality != BITSET_UNKNOWN_CARDINALITY) && (container2->cardinality != BITSET_UNKNOWN_CARDINALITY)) { + if(container1->cardinality > container2->cardinality) { + return false; + } + } + for(int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + if((container1->words[i] & container2->words[i]) != container1->words[i]) { + return false; + } + } + return true; +} + +bool bitset_container_select(const bitset_container_t *container, uint32_t *start_rank, uint32_t rank, uint32_t *element) { + int card = bitset_container_cardinality(container); + if(rank >= *start_rank + card) { + *start_rank += card; + return false; + } + const uint64_t *words = container->words; + int32_t size; + for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i += 1) { + size = roaring_hamming(words[i]); + if(rank <= *start_rank + size) { + uint64_t w = container->words[i]; + uint16_t base = i*64; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if(*start_rank == rank) { + *element = r+base; + return true; + } + w ^= t; + *start_rank += 1; + } + } + else + *start_rank += size; + } + assert(false); + roaring_unreachable; +} + + +/* Returns the smallest value (assumes not empty) */ +uint16_t bitset_container_minimum(const bitset_container_t *container) { + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) { + uint64_t w = container->words[i]; + if (w != 0) { + int r = roaring_trailing_zeroes(w); + return r + i * 64; + } + } + return UINT16_MAX; +} + +/* Returns the largest value (assumes not empty) */ +uint16_t bitset_container_maximum(const bitset_container_t *container) { + for (int32_t i = BITSET_CONTAINER_SIZE_IN_WORDS - 1; i > 0; --i ) { + uint64_t w = container->words[i]; + if (w != 0) { + int r = roaring_leading_zeroes(w); + return i * 64 + 63 - r; + } + } + return 0; +} + +/* Returns the number of values equal or smaller than x */ +int bitset_container_rank(const bitset_container_t *container, uint16_t x) { + // credit: aqrit + int sum = 0; + int i = 0; + for (int end = x / 64; i < end; i++){ + sum += roaring_hamming(container->words[i]); + } + uint64_t lastword = container->words[i]; + uint64_t lastpos = UINT64_C(1) << (x % 64); + uint64_t mask = lastpos + lastpos - 1; // smear right + sum += roaring_hamming(lastword & mask); + return sum; +} + +uint32_t bitset_container_rank_many(const bitset_container_t *container, uint64_t start_rank, const uint32_t* begin, const uint32_t* end, uint64_t* ans){ + const uint16_t high = (uint16_t)((*begin) >> 16); + int i = 0; + int sum = 0; + const uint32_t* iter = begin; + for(; iter != end; iter++) { + uint32_t x = *iter; + uint16_t xhigh = (uint16_t)(x >> 16); + if(xhigh != high) return iter - begin; // stop at next container + + uint16_t xlow = (uint16_t)x; + for(int count = xlow / 64; i < count; i++){ + sum += roaring_hamming(container->words[i]); + } + uint64_t lastword = container->words[i]; + uint64_t lastpos = UINT64_C(1) << (xlow % 64); + uint64_t mask = lastpos + lastpos - 1; // smear right + *(ans++) = start_rank + sum + roaring_hamming(lastword & mask); + } + return iter - begin; +} + + +/* Returns the index of x , if not exsist return -1 */ +int bitset_container_get_index(const bitset_container_t *container, uint16_t x) { + if (bitset_container_get(container, x)) { + // credit: aqrit + int sum = 0; + int i = 0; + for (int end = x / 64; i < end; i++){ + sum += roaring_hamming(container->words[i]); + } + uint64_t lastword = container->words[i]; + uint64_t lastpos = UINT64_C(1) << (x % 64); + uint64_t mask = lastpos + lastpos - 1; // smear right + sum += roaring_hamming(lastword & mask); + return sum - 1; + } else { + return -1; + } +} + +/* Returns the index of the first value equal or larger than x, or -1 */ +int bitset_container_index_equalorlarger(const bitset_container_t *container, uint16_t x) { + uint32_t x32 = x; + uint32_t k = x32 / 64; + uint64_t word = container->words[k]; + const int diff = x32 - k * 64; // in [0,64) + word = (word >> diff) << diff; // a mask is faster, but we don't care + while(word == 0) { + k++; + if(k == BITSET_CONTAINER_SIZE_IN_WORDS) return -1; + word = container->words[k]; + } + return k * 64 + roaring_trailing_zeroes(word); +} + +}} +/* end file src/containers/bitset.c */ +/* begin file src/containers/containers.c */ + + +namespace paimon::roaring { namespace internal { + +inline const container_t *container_unwrap_shared( + const container_t *candidate_shared_container, uint8_t *type); + +inline container_t *container_mutable_unwrap_shared( + container_t *candidate_shared_container, uint8_t *type); + +inline int container_get_cardinality( + const container_t *c, uint8_t typecode); + +inline container_t *container_iand( + container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_ior( + container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_ixor( + container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_iandnot( + container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +void container_free(container_t *c, uint8_t type) { + switch (type) { + case BITSET_CONTAINER_TYPE: + bitset_container_free(CAST_bitset(c)); + break; + case ARRAY_CONTAINER_TYPE: + array_container_free(CAST_array(c)); + break; + case RUN_CONTAINER_TYPE: + run_container_free(CAST_run(c)); + break; + case SHARED_CONTAINER_TYPE: + shared_container_free(CAST_shared(c)); + break; + default: + assert(false); + roaring_unreachable; + } +} + +void container_printf(const container_t *c, uint8_t type) { + c = container_unwrap_shared(c, &type); + switch (type) { + case BITSET_CONTAINER_TYPE: + bitset_container_printf(const_CAST_bitset(c)); + return; + case ARRAY_CONTAINER_TYPE: + array_container_printf(const_CAST_array(c)); + return; + case RUN_CONTAINER_TYPE: + run_container_printf(const_CAST_run(c)); + return; + default: + roaring_unreachable; + } +} + +void container_printf_as_uint32_array( + const container_t *c, uint8_t typecode, + uint32_t base +){ + c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + bitset_container_printf_as_uint32_array( + const_CAST_bitset(c), base); + return; + case ARRAY_CONTAINER_TYPE: + array_container_printf_as_uint32_array( + const_CAST_array(c), base); + return; + case RUN_CONTAINER_TYPE: + run_container_printf_as_uint32_array( + const_CAST_run(c), base); + return; + default: + roaring_unreachable; + } +} + +bool container_internal_validate(const container_t *container, + uint8_t typecode, const char **reason) { + if (container == NULL) { + *reason = "container is NULL"; + return false; + } + // Not using container_unwrap_shared because it asserts if shared containers are nested + if (typecode == SHARED_CONTAINER_TYPE) { + const shared_container_t *shared_container = const_CAST_shared(container); + if (croaring_refcount_get(&shared_container->counter) == 0) { + *reason = "shared container has zero refcount"; + return false; + } + if (shared_container->typecode == SHARED_CONTAINER_TYPE) { + *reason = "shared container is nested"; + return false; + } + if (shared_container->container == NULL) { + *reason = "shared container has NULL container"; + return false; + } + container = shared_container->container; + typecode = shared_container->typecode; + } + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_validate(const_CAST_bitset(container), reason); + case ARRAY_CONTAINER_TYPE: + return array_container_validate(const_CAST_array(container), reason); + case RUN_CONTAINER_TYPE: + return run_container_validate(const_CAST_run(container), reason); + default: + *reason = "invalid typecode"; + return false; + } +} + +inline bool container_nonzero_cardinality( + const container_t *c, uint8_t typecode); + +inline int container_to_uint32_array( + uint32_t *output, + const container_t *c, uint8_t typecode, + uint32_t base); + +inline container_t *container_add( + container_t *c, + uint16_t val, + uint8_t typecode, // !!! 2nd arg? + uint8_t *new_typecode); + +inline bool container_contains( + const container_t *c, + uint16_t val, + uint8_t typecode); // !!! 2nd arg? + +inline container_t *container_and( + const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_or( + const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_xor( + const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +container_t *get_copy_of_container( + container_t *c, uint8_t *typecode, + bool copy_on_write +){ + if (copy_on_write) { + shared_container_t *shared_container; + if (*typecode == SHARED_CONTAINER_TYPE) { + shared_container = CAST_shared(c); + croaring_refcount_inc(&shared_container->counter); + return shared_container; + } + assert(*typecode != SHARED_CONTAINER_TYPE); + + if ((shared_container = (shared_container_t *)roaring_malloc( + sizeof(shared_container_t))) == NULL) { + return NULL; + } + + shared_container->container = c; + shared_container->typecode = *typecode; + // At this point, we are creating new shared container + // so there should be no other references, and setting + // the counter to 2 - even non-atomically - is safe as + // long as the value is set before the return statement. + shared_container->counter = 2; + *typecode = SHARED_CONTAINER_TYPE; + + return shared_container; + } // copy_on_write + // otherwise, no copy on write... + const container_t *actual_container = container_unwrap_shared(c, typecode); + assert(*typecode != SHARED_CONTAINER_TYPE); + return container_clone(actual_container, *typecode); +} + +/** + * Copies a container, requires a typecode. This allocates new memory, caller + * is responsible for deallocation. + */ +container_t *container_clone(const container_t *c, uint8_t typecode) { + // We do not want to allow cloning of shared containers. + // c = container_unwrap_shared(c, &typecode); + switch (typecode) { + case BITSET_CONTAINER_TYPE: + return bitset_container_clone(const_CAST_bitset(c)); + case ARRAY_CONTAINER_TYPE: + return array_container_clone(const_CAST_array(c)); + case RUN_CONTAINER_TYPE: + return run_container_clone(const_CAST_run(c)); + case SHARED_CONTAINER_TYPE: + // Shared containers are not cloneable. Are you mixing COW and non-COW bitmaps? + return NULL; + default: + assert(false); + roaring_unreachable; + return NULL; + } +} + +container_t *shared_container_extract_copy( + shared_container_t *sc, uint8_t *typecode +){ + assert(sc->typecode != SHARED_CONTAINER_TYPE); + *typecode = sc->typecode; + container_t *answer; + if (croaring_refcount_dec(&sc->counter)) { + answer = sc->container; + sc->container = NULL; // paranoid + roaring_free(sc); + } else { + answer = container_clone(sc->container, *typecode); + } + assert(*typecode != SHARED_CONTAINER_TYPE); + return answer; +} + +void shared_container_free(shared_container_t *container) { + if (croaring_refcount_dec(&container->counter)) { + assert(container->typecode != SHARED_CONTAINER_TYPE); + container_free(container->container, container->typecode); + container->container = NULL; // paranoid + roaring_free(container); + } +} + +inline container_t *container_not( + const container_t *c1, uint8_t type1, + uint8_t *result_type); + +inline container_t *container_not_range( + const container_t *c1, uint8_t type1, + uint32_t range_start, uint32_t range_end, + uint8_t *result_type); + +inline container_t *container_inot( + container_t *c1, uint8_t type1, + uint8_t *result_type); + +inline container_t *container_inot_range( + container_t *c1, uint8_t type1, + uint32_t range_start, uint32_t range_end, + uint8_t *result_type); + +inline container_t *container_range_of_ones( + uint32_t range_start, uint32_t range_end, + uint8_t *result_type); + +// where are the correponding things for union and intersection?? +inline container_t *container_lazy_xor( + const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_lazy_ixor( + container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +inline container_t *container_andnot( + const container_t *c1, uint8_t type1, + const container_t *c2, uint8_t type2, + uint8_t *result_type); + +}} +/* end file src/containers/containers.c */ +/* begin file src/containers/convert.c */ +#include + + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { namespace internal { + +// file contains grubby stuff that must know impl. details of all container +// types. +bitset_container_t *bitset_container_from_array(const array_container_t *ac) { + bitset_container_t *ans = bitset_container_create(); + int limit = array_container_cardinality(ac); + for (int i = 0; i < limit; ++i) bitset_container_set(ans, ac->array[i]); + return ans; +} + +bitset_container_t *bitset_container_from_run(const run_container_t *arr) { + int card = run_container_cardinality(arr); + bitset_container_t *answer = bitset_container_create(); + for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) { + rle16_t vl = arr->runs[rlepos]; + bitset_set_lenrange(answer->words, vl.value, vl.length); + } + answer->cardinality = card; + return answer; +} + +array_container_t *array_container_from_run(const run_container_t *arr) { + array_container_t *answer = + array_container_create_given_capacity(run_container_cardinality(arr)); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) { + int run_start = arr->runs[rlepos].value; + int run_end = run_start + arr->runs[rlepos].length; + + for (int run_value = run_start; run_value <= run_end; ++run_value) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + return answer; +} + +array_container_t *array_container_from_bitset(const bitset_container_t *bits) { + array_container_t *result = + array_container_create_given_capacity(bits->cardinality); + result->cardinality = bits->cardinality; +#if CROARING_IS_X64 +#if CROARING_COMPILER_SUPPORTS_AVX512 + if( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX512 ) { + bitset_extract_setbits_avx512_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS, + result->array, bits->cardinality , 0); + } else +#endif + { + // sse version ends up being slower here + // (bitset_extract_setbits_sse_uint16) + // because of the sparsity of the data + bitset_extract_setbits_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS, + result->array, 0); + } +#else + // If the system is not x64, then we have no accelerated function. + bitset_extract_setbits_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS, + result->array, 0); +#endif + + + return result; +} + +/* assumes that container has adequate space. Run from [s,e] (inclusive) */ +static void add_run(run_container_t *rc, int s, int e) { + rc->runs[rc->n_runs].value = s; + rc->runs[rc->n_runs].length = e - s; + rc->n_runs++; +} + +run_container_t *run_container_from_array(const array_container_t *c) { + int32_t n_runs = array_container_number_of_runs(c); + run_container_t *answer = run_container_create_given_capacity(n_runs); + int prev = -2; + int run_start = -1; + int32_t card = c->cardinality; + if (card == 0) return answer; + for (int i = 0; i < card; ++i) { + const uint16_t cur_val = c->array[i]; + if (cur_val != prev + 1) { + // new run starts; flush old one, if any + if (run_start != -1) add_run(answer, run_start, prev); + run_start = cur_val; + } + prev = c->array[i]; + } + // now prev is the last seen value + add_run(answer, run_start, prev); + // assert(run_container_cardinality(answer) == c->cardinality); + return answer; +} + +/** + * Convert the runcontainer to either a Bitmap or an Array Container, depending + * on the cardinality. Frees the container. + * Allocates and returns new container, which caller is responsible for freeing. + * It does not free the run container. + */ +container_t *convert_to_bitset_or_array_container( + run_container_t *rc, int32_t card, + uint8_t *resulttype +){ + if (card <= DEFAULT_MAX_SIZE) { + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < rc->n_runs; ++rlepos) { + uint16_t run_start = rc->runs[rlepos].value; + uint16_t run_end = run_start + rc->runs[rlepos].length; + for (uint16_t run_value = run_start; run_value < run_end; + ++run_value) { + answer->array[answer->cardinality++] = run_value; + } + answer->array[answer->cardinality++] = run_end; + } + assert(card == answer->cardinality); + *resulttype = ARRAY_CONTAINER_TYPE; + //run_container_free(r); + return answer; + } + bitset_container_t *answer = bitset_container_create(); + for (int rlepos = 0; rlepos < rc->n_runs; ++rlepos) { + uint16_t run_start = rc->runs[rlepos].value; + bitset_set_lenrange(answer->words, run_start, rc->runs[rlepos].length); + } + answer->cardinality = card; + *resulttype = BITSET_CONTAINER_TYPE; + //run_container_free(r); + return answer; +} + +/* Converts a run container to either an array or a bitset, IF it saves space. + */ +/* If a conversion occurs, the caller is responsible to free the original + * container and + * he becomes responsible to free the new one. */ +container_t *convert_run_to_efficient_container( + run_container_t *c, + uint8_t *typecode_after +){ + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(c->n_runs); + + int32_t size_as_bitset_container = + bitset_container_serialized_size_in_bytes(); + int32_t card = run_container_cardinality(c); + int32_t size_as_array_container = + array_container_serialized_size_in_bytes(card); + + int32_t min_size_non_run = + size_as_bitset_container < size_as_array_container + ? size_as_bitset_container + : size_as_array_container; + if (size_as_run_container <= min_size_non_run) { // no conversion + *typecode_after = RUN_CONTAINER_TYPE; + return c; + } + if (card <= DEFAULT_MAX_SIZE) { + // to array + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) { + int run_start = c->runs[rlepos].value; + int run_end = run_start + c->runs[rlepos].length; + + for (int run_value = run_start; run_value <= run_end; ++run_value) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + *typecode_after = ARRAY_CONTAINER_TYPE; + return answer; + } + + // else to bitset + bitset_container_t *answer = bitset_container_create(); + + for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) { + int start = c->runs[rlepos].value; + int end = start + c->runs[rlepos].length; + bitset_set_range(answer->words, start, end + 1); + } + answer->cardinality = card; + *typecode_after = BITSET_CONTAINER_TYPE; + return answer; +} + +// like convert_run_to_efficient_container but frees the old result if needed +container_t *convert_run_to_efficient_container_and_free( + run_container_t *c, + uint8_t *typecode_after +){ + container_t *answer = convert_run_to_efficient_container(c, typecode_after); + if (answer != c) run_container_free(c); + return answer; +} + +/* once converted, the original container is disposed here, rather than + in roaring_array +*/ + +// TODO: split into run- array- and bitset- subfunctions for sanity; +// a few function calls won't really matter. + +container_t *convert_run_optimize( + container_t *c, uint8_t typecode_original, + uint8_t *typecode_after +){ + if (typecode_original == RUN_CONTAINER_TYPE) { + container_t *newc = convert_run_to_efficient_container( + CAST_run(c), typecode_after); + if (newc != c) { + container_free(c, typecode_original); + } + return newc; + } else if (typecode_original == ARRAY_CONTAINER_TYPE) { + // it might need to be converted to a run container. + array_container_t *c_qua_array = CAST_array(c); + int32_t n_runs = array_container_number_of_runs(c_qua_array); + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(n_runs); + int32_t card = array_container_cardinality(c_qua_array); + int32_t size_as_array_container = + array_container_serialized_size_in_bytes(card); + + if (size_as_run_container >= size_as_array_container) { + *typecode_after = ARRAY_CONTAINER_TYPE; + return c; + } + // else convert array to run container + run_container_t *answer = run_container_create_given_capacity(n_runs); + int prev = -2; + int run_start = -1; + + assert(card > 0); + for (int i = 0; i < card; ++i) { + uint16_t cur_val = c_qua_array->array[i]; + if (cur_val != prev + 1) { + // new run starts; flush old one, if any + if (run_start != -1) add_run(answer, run_start, prev); + run_start = cur_val; + } + prev = c_qua_array->array[i]; + } + assert(run_start >= 0); + // now prev is the last seen value + add_run(answer, run_start, prev); + *typecode_after = RUN_CONTAINER_TYPE; + array_container_free(c_qua_array); + return answer; + } else if (typecode_original == + BITSET_CONTAINER_TYPE) { // run conversions on bitset + // does bitset need conversion to run? + bitset_container_t *c_qua_bitset = CAST_bitset(c); + int32_t n_runs = bitset_container_number_of_runs(c_qua_bitset); + int32_t size_as_run_container = + run_container_serialized_size_in_bytes(n_runs); + int32_t size_as_bitset_container = + bitset_container_serialized_size_in_bytes(); + + if (size_as_bitset_container <= size_as_run_container) { + // no conversion needed. + *typecode_after = BITSET_CONTAINER_TYPE; + return c; + } + // bitset to runcontainer (ported from Java RunContainer( + // BitmapContainer bc, int nbrRuns)) + assert(n_runs > 0); // no empty bitmaps + run_container_t *answer = run_container_create_given_capacity(n_runs); + + int long_ctr = 0; + uint64_t cur_word = c_qua_bitset->words[0]; + while (true) { + while (cur_word == UINT64_C(0) && + long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1) + cur_word = c_qua_bitset->words[++long_ctr]; + + if (cur_word == UINT64_C(0)) { + bitset_container_free(c_qua_bitset); + *typecode_after = RUN_CONTAINER_TYPE; + return answer; + } + + int local_run_start = roaring_trailing_zeroes(cur_word); + int run_start = local_run_start + 64 * long_ctr; + uint64_t cur_word_with_1s = cur_word | (cur_word - 1); + + int run_end = 0; + while (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF) && + long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1) + cur_word_with_1s = c_qua_bitset->words[++long_ctr]; + + if (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF)) { + run_end = 64 + long_ctr * 64; // exclusive, I guess + add_run(answer, run_start, run_end - 1); + bitset_container_free(c_qua_bitset); + *typecode_after = RUN_CONTAINER_TYPE; + return answer; + } + int local_run_end = roaring_trailing_zeroes(~cur_word_with_1s); + run_end = local_run_end + long_ctr * 64; + add_run(answer, run_start, run_end - 1); + cur_word = cur_word_with_1s & (cur_word_with_1s + 1); + } + return answer; + } else { + assert(false); + roaring_unreachable; + return NULL; + } +} + +container_t *container_from_run_range( + const run_container_t *run, + uint32_t min, uint32_t max, uint8_t *typecode_after +){ + // We expect most of the time to end up with a bitset container + bitset_container_t *bitset = bitset_container_create(); + *typecode_after = BITSET_CONTAINER_TYPE; + int32_t union_cardinality = 0; + for (int32_t i = 0; i < run->n_runs; ++i) { + uint32_t rle_min = run->runs[i].value; + uint32_t rle_max = rle_min + run->runs[i].length; + bitset_set_lenrange(bitset->words, rle_min, rle_max - rle_min); + union_cardinality += run->runs[i].length + 1; + } + union_cardinality += max - min + 1; + union_cardinality -= bitset_lenrange_cardinality(bitset->words, min, max-min); + bitset_set_lenrange(bitset->words, min, max - min); + bitset->cardinality = union_cardinality; + if(bitset->cardinality <= DEFAULT_MAX_SIZE) { + // we need to convert to an array container + array_container_t * array = array_container_from_bitset(bitset); + *typecode_after = ARRAY_CONTAINER_TYPE; + bitset_container_free(bitset); + return array; + } + return bitset; +} + +}} +/* end file src/containers/convert.c */ +/* begin file src/containers/mixed_andnot.c */ +/* + * mixed_andnot.c. More methods since operation is not symmetric, + * except no "wide" andnot , so no lazy options motivated. + */ + +#include +#include + + +namespace paimon::roaring { namespace internal { + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, a valid array container that could be the same as dst.*/ +void array_bitset_container_andnot(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst) { + // follows Java implementation as of June 2016 + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + int32_t newcard = 0; + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + dst->array[newcard] = key; + newcard += 1 - bitset_container_contains(src_2, key); + } + dst->cardinality = newcard; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * src_1 */ + +void array_bitset_container_iandnot(array_container_t *src_1, + const bitset_container_t *src_2) { + array_bitset_container_andnot(src_1, src_2, src_1); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst, which does not initially have a valid container. + * Return true for a bitset result; false for array + */ + +bool bitset_array_container_andnot( + const bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + // Java did this directly, but we have option of asm or avx + bitset_container_t *result = bitset_container_create(); + bitset_container_copy(src_1, result); + result->cardinality = + (int32_t)bitset_clear_list(result->words, (uint64_t)result->cardinality, + src_2->array, (uint64_t)src_2->cardinality); + + // do required type conversions. + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; + } + *dst = result; + return true; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_iandnot( + bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + *dst = src_1; + src_1->cardinality = + (int32_t)bitset_clear_list(src_1->words, (uint64_t)src_1->cardinality, + src_2->array, (uint64_t)src_2->cardinality); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_andnot( + const run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + // follows the Java implementation as of June 2016 + int card = run_container_cardinality(src_1); + if (card <= DEFAULT_MAX_SIZE) { + // must be an array + array_container_t *answer = array_container_create_given_capacity(card); + answer->cardinality = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + for (int run_value = rle.value; run_value <= rle.value + rle.length; + ++run_value) { + if (!bitset_container_get(src_2, (uint16_t)run_value)) { + answer->array[answer->cardinality++] = (uint16_t)run_value; + } + } + } + *dst = answer; + return false; + } else { // we guess it will be a bitset, though have to check guess when + // done + bitset_container_t *answer = bitset_container_clone(src_2); + + uint32_t last_pos = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + + uint32_t start = rle.value; + uint32_t end = start + rle.length + 1; + bitset_reset_range(answer->words, last_pos, start); + bitset_flip_range(answer->words, start, end); + last_pos = end; + } + bitset_reset_range(answer->words, last_pos, (uint32_t)(1 << 16)); + + answer->cardinality = bitset_container_compute_cardinality(answer); + + if (answer->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(answer); + bitset_container_free(answer); + return false; // not bitset + } + *dst = answer; + return true; // bitset + } +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_iandnot( + run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + // dummy implementation + bool ans = run_bitset_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool bitset_run_container_andnot( + const bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + // follows Java implementation + bitset_container_t *result = bitset_container_create(); + + bitset_container_copy(src_1, result); + for (int32_t rlepos = 0; rlepos < src_2->n_runs; ++rlepos) { + rle16_t rle = src_2->runs[rlepos]; + bitset_reset_range(result->words, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + result->cardinality = bitset_container_compute_cardinality(result); + + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_run_container_iandnot( + bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + *dst = src_1; + + for (int32_t rlepos = 0; rlepos < src_2->n_runs; ++rlepos) { + rle16_t rle = src_2->runs[rlepos]; + bitset_reset_range(src_1->words, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + src_1->cardinality = bitset_container_compute_cardinality(src_1); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* helper. a_out must be a valid array container with adequate capacity. + * Returns the cardinality of the output container. Partly Based on Java + * implementation Util.unsignedDifference. + * + * TODO: Util.unsignedDifference does not use advanceUntil. Is it cheaper + * to avoid advanceUntil? + */ + +static int run_array_array_subtract(const run_container_t *rc, + const array_container_t *a_in, + array_container_t *a_out) { + int out_card = 0; + int32_t in_array_pos = + -1; // since advanceUntil always assumes we start the search AFTER this + + for (int rlepos = 0; rlepos < rc->n_runs; rlepos++) { + int32_t start = rc->runs[rlepos].value; + int32_t end = start + rc->runs[rlepos].length + 1; + + in_array_pos = advanceUntil(a_in->array, in_array_pos, + a_in->cardinality, (uint16_t)start); + + if (in_array_pos >= a_in->cardinality) { // run has no items subtracted + for (int32_t i = start; i < end; ++i) + a_out->array[out_card++] = (uint16_t)i; + } else { + uint16_t next_nonincluded = a_in->array[in_array_pos]; + if (next_nonincluded >= end) { + // another case when run goes unaltered + for (int32_t i = start; i < end; ++i) + a_out->array[out_card++] = (uint16_t)i; + in_array_pos--; // ensure we see this item again if necessary + } else { + for (int32_t i = start; i < end; ++i) + if (i != next_nonincluded) + a_out->array[out_card++] = (uint16_t)i; + else // 0 should ensure we don't match + next_nonincluded = + (in_array_pos + 1 >= a_in->cardinality) + ? 0 + : a_in->array[++in_array_pos]; + in_array_pos--; // see again + } + } + } + return out_card; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any type of container. + */ + +int run_array_container_andnot( + const run_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + // follows the Java impl as of June 2016 + + int card = run_container_cardinality(src_1); + const int arbitrary_threshold = 32; + + if (card <= arbitrary_threshold) { + if (src_2->cardinality == 0) { + *dst = run_container_clone(src_1); + return RUN_CONTAINER_TYPE; + } + // Java's "lazyandNot.toEfficientContainer" thing + run_container_t *answer = run_container_create_given_capacity( + card + array_container_cardinality(src_2)); + + int rlepos = 0; + int xrlepos = 0; // "x" is src_2 + rle16_t rle = src_1->runs[rlepos]; + int32_t start = rle.value; + int32_t end = start + rle.length + 1; + int32_t xstart = src_2->array[xrlepos]; + + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->cardinality)) { + if (end <= xstart) { + // output the first run + answer->runs[answer->n_runs++] = + MAKE_RLE16(start, end - start - 1); + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xstart + 1 <= start) { + // exit the second run + xrlepos++; + if (xrlepos < src_2->cardinality) { + xstart = src_2->array[xrlepos]; + } + } else { + if (start < xstart) { + answer->runs[answer->n_runs++] = + MAKE_RLE16(start, xstart - start - 1); + } + if (xstart + 1 < end) { + start = xstart + 1; + } else { + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } + } + } + if (rlepos < src_1->n_runs) { + answer->runs[answer->n_runs++] = MAKE_RLE16(start, end - start - 1); + rlepos++; + if (rlepos < src_1->n_runs) { + memcpy(answer->runs + answer->n_runs, src_1->runs + rlepos, + (src_1->n_runs - rlepos) * sizeof(rle16_t)); + answer->n_runs += (src_1->n_runs - rlepos); + } + } + uint8_t return_type; + *dst = convert_run_to_efficient_container(answer, &return_type); + if (answer != *dst) run_container_free(answer); + return return_type; + } + // else it's a bitmap or array + + if (card <= DEFAULT_MAX_SIZE) { + array_container_t *ac = array_container_create_given_capacity(card); + // nb Java code used a generic iterator-based merge to compute + // difference + ac->cardinality = run_array_array_subtract(src_1, src_2, ac); + *dst = ac; + return ARRAY_CONTAINER_TYPE; + } + bitset_container_t *ans = bitset_container_from_run(src_1); + bool result_is_bitset = bitset_array_container_iandnot(ans, src_2, dst); + return (result_is_bitset ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_array_container_iandnot( + run_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + // dummy implementation same as June 2016 Java + int ans = run_array_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* dst must be a valid array container, allowed to be src_1 */ + +void array_run_container_andnot(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst) { + // basically following Java impl as of June 2016 + if (src_1->cardinality > dst->capacity) { + array_container_grow(dst, src_1->cardinality, false); + } + + if (src_2->n_runs == 0) { + memmove(dst->array, src_1->array, + sizeof(uint16_t) * src_1->cardinality); + dst->cardinality = src_1->cardinality; + return; + } + int32_t run_start = src_2->runs[0].value; + int32_t run_end = run_start + src_2->runs[0].length; + int which_run = 0; + + uint16_t val = 0; + int dest_card = 0; + for (int i = 0; i < src_1->cardinality; ++i) { + val = src_1->array[i]; + if (val < run_start) + dst->array[dest_card++] = val; + else if (val <= run_end) { + ; // omitted item + } else { + do { + if (which_run + 1 < src_2->n_runs) { + ++which_run; + run_start = src_2->runs[which_run].value; + run_end = run_start + src_2->runs[which_run].length; + + } else + run_start = run_end = (1 << 16) + 1; + } while (val > run_end); + --i; + } + } + dst->cardinality = dest_card; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +void array_run_container_iandnot(array_container_t *src_1, + const run_container_t *src_2) { + array_run_container_andnot(src_1, src_2, src_1); +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_andnot( + const run_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + run_container_t *ans = run_container_create(); + run_container_andnot(src_1, src_2, ans); + uint8_t typecode_after; + *dst = convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +int run_run_container_iandnot( + run_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + // following Java impl as of June 2016 (dummy) + int ans = run_run_container_andnot(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +/* + * dst is a valid array container and may be the same as src_1 + */ + +void array_array_container_andnot(const array_container_t *src_1, + const array_container_t *src_2, + array_container_t *dst) { + array_container_andnot(src_1, src_2, dst); +} + +/* inplace array-array andnot will always be able to reuse the space of + * src_1 */ +void array_array_container_iandnot(array_container_t *src_1, + const array_container_t *src_2) { + array_container_andnot(src_1, src_2, src_1); +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_andnot( + const bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bitset_container_t *ans = bitset_container_create(); + int card = bitset_container_andnot(src_1, src_2, ans); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(ans); + bitset_container_free(ans); + return false; // not bitset + } else { + *dst = ans; + return true; + } +} + +/* Compute the andnot of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_bitset_container_iandnot( + bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + int card = bitset_container_andnot(src_1, src_2, src_1); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else { + *dst = src_1; + return true; + } +} + +}} +/* end file src/containers/mixed_andnot.c */ +/* begin file src/containers/mixed_equal.c */ + +namespace paimon::roaring { namespace internal { + +bool array_container_equal_bitset(const array_container_t* container1, + const bitset_container_t* container2) { + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality != container1->cardinality) { + return false; + } + } + int32_t pos = 0; + for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i) { + uint64_t w = container2->words[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + uint16_t r = i * 64 + roaring_trailing_zeroes(w); + if (pos >= container1->cardinality) { + return false; + } + if (container1->array[pos] != r) { + return false; + } + ++pos; + w ^= t; + } + } + return (pos == container1->cardinality); +} + +bool run_container_equals_array(const run_container_t* container1, + const array_container_t* container2) { + if (run_container_cardinality(container1) != container2->cardinality) + return false; + int32_t pos = 0; + for (int i = 0; i < container1->n_runs; ++i) { + const uint32_t run_start = container1->runs[i].value; + const uint32_t le = container1->runs[i].length; + + if (container2->array[pos] != run_start) { + return false; + } + + if (container2->array[pos + le] != run_start + le) { + return false; + } + + pos += le + 1; + } + return true; +} + +bool run_container_equals_bitset(const run_container_t* container1, + const bitset_container_t* container2) { + + int run_card = run_container_cardinality(container1); + int bitset_card = (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) ? + container2->cardinality : + bitset_container_compute_cardinality(container2); + if (bitset_card != run_card) { + return false; + } + + for (int32_t i = 0; i < container1->n_runs; i++) { + uint32_t begin = container1->runs[i].value; + if (container1->runs[i].length) { + uint32_t end = begin + container1->runs[i].length + 1; + if (!bitset_container_contains_range(container2, begin, end)) { + return false; + } + } else { + if (!bitset_container_contains(container2, begin)) { + return false; + } + } + } + + return true; +} + +}} +/* end file src/containers/mixed_equal.c */ +/* begin file src/containers/mixed_intersection.c */ +/* + * mixed_intersection.c + * + */ + + +namespace paimon::roaring { namespace internal { + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. */ +void array_bitset_container_intersection(const array_container_t *src_1, + const bitset_container_t *src_2, + array_container_t *dst) { + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + int32_t newcard = 0; // dst could be src_1 + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + // this branchless approach is much faster... + dst->array[newcard] = key; + newcard += bitset_container_contains(src_2, key); + /** + * we could do it this way instead... + * if (bitset_container_contains(src_2, key)) { + * dst->array[newcard++] = key; + * } + * but if the result is unpredictible, the processor generates + * many mispredicted branches. + * Difference can be huge (from 3 cycles when predictible all the way + * to 16 cycles when unpredictible. + * See + * https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/master/extra/bitset/c/arraybitsetintersection.c + */ + } + dst->cardinality = newcard; +} + +/* Compute the size of the intersection of src_1 and src_2. */ +int array_bitset_container_intersection_cardinality( + const array_container_t *src_1, const bitset_container_t *src_2) { + int32_t newcard = 0; + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + newcard += bitset_container_contains(src_2, key); + } + return newcard; +} + + +bool array_bitset_container_intersect(const array_container_t *src_1, + const bitset_container_t *src_2) { + const int32_t origcard = src_1->cardinality; + for (int i = 0; i < origcard; ++i) { + uint16_t key = src_1->array[i]; + if(bitset_container_contains(src_2, key)) return true; + } + return false; +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is allowed for dst to be equal to src_1. We assume that dst is a + * valid container. */ +void array_run_container_intersection(const array_container_t *src_1, + const run_container_t *src_2, + array_container_t *dst) { + if (run_container_is_full(src_2)) { + if (dst != src_1) array_container_copy(src_1, dst); + return; + } + if (dst->capacity < src_1->cardinality) { + array_container_grow(dst, src_1->cardinality, false); + } + if (src_2->n_runs == 0) { + return; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + int32_t newcard = 0; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + dst->cardinality = newcard; + return; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + dst->array[newcard] = arrayval; + newcard++; + arraypos++; + } + } + dst->cardinality = newcard; +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * *dst. If the result is true then the result is a bitset_container_t + * otherwise is a array_container_t. If *dst == src_2, an in-place processing + * is attempted.*/ +bool run_bitset_container_intersection( + const run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + if (run_container_is_full(src_1)) { + if (*dst != src_2) *dst = bitset_container_clone(src_2); + return true; + } + int32_t card = run_container_cardinality(src_1); + if (card <= DEFAULT_MAX_SIZE) { + // result can only be an array (assuming that we never make a + // RunContainer) + if (card > src_2->cardinality) { + card = src_2->cardinality; + } + array_container_t *answer = array_container_create_given_capacity(card); + *dst = answer; + if (*dst == NULL) { + return false; + } + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + uint32_t endofrun = (uint32_t)rle.value + rle.length; + for (uint32_t runValue = rle.value; runValue <= endofrun; + ++runValue) { + answer->array[answer->cardinality] = (uint16_t)runValue; + answer->cardinality += + bitset_container_contains(src_2, runValue); + } + } + return false; + } + if (*dst == src_2) { // we attempt in-place + bitset_container_t *answer = CAST_bitset(*dst); + uint32_t start = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + const rle16_t rle = src_1->runs[rlepos]; + uint32_t end = rle.value; + bitset_reset_range(src_2->words, start, end); + + start = end + rle.length + 1; + } + bitset_reset_range(src_2->words, start, UINT32_C(1) << 16); + answer->cardinality = bitset_container_compute_cardinality(answer); + if (src_2->cardinality > DEFAULT_MAX_SIZE) { + return true; + } else { + array_container_t *newanswer = array_container_from_bitset(src_2); + if (newanswer == NULL) { + *dst = NULL; + return false; + } + *dst = newanswer; + return false; + } + } else { // no inplace + // we expect the answer to be a bitmap (if we are lucky) + bitset_container_t *answer = bitset_container_clone(src_2); + + *dst = answer; + if (answer == NULL) { + return true; + } + uint32_t start = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + const rle16_t rle = src_1->runs[rlepos]; + uint32_t end = rle.value; + bitset_reset_range(answer->words, start, end); + start = end + rle.length + 1; + } + bitset_reset_range(answer->words, start, UINT32_C(1) << 16); + answer->cardinality = bitset_container_compute_cardinality(answer); + + if (answer->cardinality > DEFAULT_MAX_SIZE) { + return true; + } else { + array_container_t *newanswer = array_container_from_bitset(answer); + bitset_container_free(CAST_bitset(*dst)); + if (newanswer == NULL) { + *dst = NULL; + return false; + } + *dst = newanswer; + return false; + } + } +} + +/* Compute the size of the intersection between src_1 and src_2 . */ +int array_run_container_intersection_cardinality(const array_container_t *src_1, + const run_container_t *src_2) { + if (run_container_is_full(src_2)) { + return src_1->cardinality; + } + if (src_2->n_runs == 0) { + return 0; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + int32_t newcard = 0; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + return newcard; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + newcard++; + arraypos++; + } + } + return newcard; +} + +/* Compute the intersection between src_1 and src_2 + **/ +int run_bitset_container_intersection_cardinality( + const run_container_t *src_1, const bitset_container_t *src_2) { + if (run_container_is_full(src_1)) { + return bitset_container_cardinality(src_2); + } + int answer = 0; + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + answer += + bitset_lenrange_cardinality(src_2->words, rle.value, rle.length); + } + return answer; +} + + +bool array_run_container_intersect(const array_container_t *src_1, + const run_container_t *src_2) { + if( run_container_is_full(src_2) ) { + return !array_container_empty(src_1); + } + if (src_2->n_runs == 0) { + return false; + } + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t rle = src_2->runs[rlepos]; + while (arraypos < src_1->cardinality) { + const uint16_t arrayval = src_1->array[arraypos]; + while (rle.value + rle.length < + arrayval) { // this will frequently be false + ++rlepos; + if (rlepos == src_2->n_runs) { + return false; // we are done + } + rle = src_2->runs[rlepos]; + } + if (rle.value > arrayval) { + arraypos = advanceUntil(src_1->array, arraypos, src_1->cardinality, + rle.value); + } else { + return true; + } + } + return false; +} + +/* Compute the intersection between src_1 and src_2 + **/ +bool run_bitset_container_intersect(const run_container_t *src_1, + const bitset_container_t *src_2) { + if( run_container_is_full(src_1) ) { + return !bitset_container_empty(src_2); + } + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + if(!bitset_lenrange_empty(src_2->words, rle.value,rle.length)) return true; + } + return false; +} + +/* + * Compute the intersection between src_1 and src_2 and write the result + * to *dst. If the return function is true, the result is a bitset_container_t + * otherwise is a array_container_t. + */ +bool bitset_bitset_container_intersection( + const bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + const int newCardinality = bitset_container_and_justcard(src_1, src_2); + if (newCardinality > DEFAULT_MAX_SIZE) { + *dst = bitset_container_create(); + if (*dst != NULL) { + bitset_container_and_nocard(src_1, src_2, CAST_bitset(*dst)); + CAST_bitset(*dst)->cardinality = newCardinality; + } + return true; // it is a bitset + } + *dst = array_container_create_given_capacity(newCardinality); + if (*dst != NULL) { + CAST_array(*dst)->cardinality = newCardinality; + bitset_extract_intersection_setbits_uint16( + src_1->words, src_2->words, BITSET_CONTAINER_SIZE_IN_WORDS, + CAST_array(*dst)->array, 0); + } + return false; // not a bitset +} + +bool bitset_bitset_container_intersection_inplace( + bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + const int newCardinality = bitset_container_and_justcard(src_1, src_2); + if (newCardinality > DEFAULT_MAX_SIZE) { + *dst = src_1; + bitset_container_and_nocard(src_1, src_2, src_1); + CAST_bitset(*dst)->cardinality = newCardinality; + return true; // it is a bitset + } + *dst = array_container_create_given_capacity(newCardinality); + if (*dst != NULL) { + CAST_array(*dst)->cardinality = newCardinality; + bitset_extract_intersection_setbits_uint16( + src_1->words, src_2->words, BITSET_CONTAINER_SIZE_IN_WORDS, + CAST_array(*dst)->array, 0); + } + return false; // not a bitset +} + +}} +/* end file src/containers/mixed_intersection.c */ +/* begin file src/containers/mixed_negation.c */ +/* + * mixed_negation.c + * + */ + +#include +#include + + +namespace paimon::roaring { namespace internal { + +// TODO: make simplified and optimized negation code across +// the full range. + +/* Negation across the entire range of the container. + * Compute the negation of src and write the result + * to *dst. The complement of a + * sufficiently sparse set will always be dense and a hence a bitmap +' * We assume that dst is pre-allocated and a valid bitset container + * There can be no in-place version. + */ +void array_container_negation(const array_container_t *src, + bitset_container_t *dst) { + uint64_t card = UINT64_C(1 << 16); + bitset_container_set_all(dst); + + if (src->cardinality == 0) { + return; + } + + dst->cardinality = (int32_t)bitset_clear_list(dst->words, card, src->array, + (uint64_t)src->cardinality); +} + +/* Negation across the entire range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation( + const bitset_container_t *src, container_t **dst +){ + return bitset_container_negation_range(src, 0, (1 << 16), dst); +} + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_inplace( + bitset_container_t *src, container_t **dst +){ + return bitset_container_negation_range_inplace(src, 0, (1 << 16), dst); +} + +/* Negation across the entire range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation(const run_container_t *src, container_t **dst) { + return run_container_negation_range(src, 0, (1 << 16), dst); +} + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_inplace(run_container_t *src, container_t **dst) { + return run_container_negation_range_inplace(src, 0, (1 << 16), dst); +} + +/* Negation across a range of the container. + * Compute the negation of src and write the result + * to *dst. Returns true if the result is a bitset container + * and false for an array container. *dst is not preallocated. + */ +bool array_container_negation_range( + const array_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + /* close port of the Java implementation */ + if (range_start >= range_end) { + *dst = array_container_clone(src); + return false; + } + + int32_t start_index = + binarySearch(src->array, src->cardinality, (uint16_t)range_start); + if (start_index < 0) start_index = -start_index - 1; + + int32_t last_index = + binarySearch(src->array, src->cardinality, (uint16_t)(range_end - 1)); + if (last_index < 0) last_index = -last_index - 2; + + const int32_t current_values_in_range = last_index - start_index + 1; + const int32_t span_to_be_flipped = range_end - range_start; + const int32_t new_values_in_range = + span_to_be_flipped - current_values_in_range; + const int32_t cardinality_change = + new_values_in_range - current_values_in_range; + const int32_t new_cardinality = src->cardinality + cardinality_change; + + if (new_cardinality > DEFAULT_MAX_SIZE) { + bitset_container_t *temp = bitset_container_from_array(src); + bitset_flip_range(temp->words, (uint32_t)range_start, + (uint32_t)range_end); + temp->cardinality = new_cardinality; + *dst = temp; + return true; + } + + array_container_t *arr = + array_container_create_given_capacity(new_cardinality); + *dst = (container_t *)arr; + if(new_cardinality == 0) { + arr->cardinality = new_cardinality; + return false; // we are done. + } + // copy stuff before the active area + memcpy(arr->array, src->array, start_index * sizeof(uint16_t)); + + // work on the range + int32_t out_pos = start_index, in_pos = start_index; + int32_t val_in_range = range_start; + for (; val_in_range < range_end && in_pos <= last_index; ++val_in_range) { + if ((uint16_t)val_in_range != src->array[in_pos]) { + arr->array[out_pos++] = (uint16_t)val_in_range; + } else { + ++in_pos; + } + } + for (; val_in_range < range_end; ++val_in_range) + arr->array[out_pos++] = (uint16_t)val_in_range; + + // content after the active range + memcpy(arr->array + out_pos, src->array + (last_index + 1), + (src->cardinality - (last_index + 1)) * sizeof(uint16_t)); + arr->cardinality = new_cardinality; + return false; +} + +/* Even when the result would fit, it is unclear how to make an + * inplace version without inefficient copying. + */ + +bool array_container_negation_range_inplace( + array_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + bool ans = array_container_negation_range(src, range_start, range_end, dst); + // TODO : try a real inplace version + array_container_free(src); + return ans; +} + +/* Negation across a range of the container + * Compute the negation of src and write the result + * to *dst. A true return value indicates a bitset result, + * otherwise the result is an array container. + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +bool bitset_container_negation_range( + const bitset_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + // TODO maybe consider density-based estimate + // and sometimes build result directly as array, with + // conversion back to bitset if wrong. Or determine + // actual result cardinality, then go directly for the known final cont. + + // keep computation using bitsets as long as possible. + bitset_container_t *t = bitset_container_clone(src); + bitset_flip_range(t->words, (uint32_t)range_start, (uint32_t)range_end); + t->cardinality = bitset_container_compute_cardinality(t); + + if (t->cardinality > DEFAULT_MAX_SIZE) { + *dst = t; + return true; + } else { + *dst = array_container_from_bitset(t); + bitset_container_free(t); + return false; + } +} + +/* inplace version */ +/* + * Same as bitset_container_negation except that if the output is to + * be a + * bitset_container_t, then src is modified and no allocation is made. + * If the output is to be an array_container_t, then caller is responsible + * to free the container. + * In all cases, the result is in *dst. + */ +bool bitset_container_negation_range_inplace( + bitset_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + bitset_flip_range(src->words, (uint32_t)range_start, (uint32_t)range_end); + src->cardinality = bitset_container_compute_cardinality(src); + if (src->cardinality > DEFAULT_MAX_SIZE) { + *dst = src; + return true; + } + *dst = array_container_from_bitset(src); + bitset_container_free(src); + return false; +} + +/* Negation across a range of container + * Compute the negation of src and write the result + * to *dst. Return values are the *_TYPECODES as defined * in containers.h + * We assume that dst is not pre-allocated. In + * case of failure, *dst will be NULL. + */ +int run_container_negation_range( + const run_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + uint8_t return_typecode; + + // follows the Java implementation + if (range_end <= range_start) { + *dst = run_container_clone(src); + return RUN_CONTAINER_TYPE; + } + + run_container_t *ans = run_container_create_given_capacity( + src->n_runs + 1); // src->n_runs + 1); + int k = 0; + for (; k < src->n_runs && src->runs[k].value < range_start; ++k) { + ans->runs[k] = src->runs[k]; + ans->n_runs++; + } + + run_container_smart_append_exclusive( + ans, (uint16_t)range_start, (uint16_t)(range_end - range_start - 1)); + + for (; k < src->n_runs; ++k) { + run_container_smart_append_exclusive(ans, src->runs[k].value, + src->runs[k].length); + } + + *dst = convert_run_to_efficient_container(ans, &return_typecode); + if (return_typecode != RUN_CONTAINER_TYPE) run_container_free(ans); + + return return_typecode; +} + +/* + * Same as run_container_negation except that if the output is to + * be a + * run_container_t, and has the capacity to hold the result, + * then src is modified and no allocation is made. + * In all cases, the result is in *dst. + */ +int run_container_negation_range_inplace( + run_container_t *src, + const int range_start, const int range_end, + container_t **dst +){ + uint8_t return_typecode; + + if (range_end <= range_start) { + *dst = src; + return RUN_CONTAINER_TYPE; + } + + // TODO: efficient special case when range is 0 to 65535 inclusive + + if (src->capacity == src->n_runs) { + // no excess room. More checking to see if result can fit + bool last_val_before_range = false; + bool first_val_in_range = false; + bool last_val_in_range = false; + bool first_val_past_range = false; + + if (range_start > 0) + last_val_before_range = + run_container_contains(src, (uint16_t)(range_start - 1)); + first_val_in_range = run_container_contains(src, (uint16_t)range_start); + + if (last_val_before_range == first_val_in_range) { + last_val_in_range = + run_container_contains(src, (uint16_t)(range_end - 1)); + if (range_end != 0x10000) + first_val_past_range = + run_container_contains(src, (uint16_t)range_end); + + if (last_val_in_range == + first_val_past_range) { // no space for inplace + int ans = run_container_negation_range(src, range_start, + range_end, dst); + run_container_free(src); + return ans; + } + } + } + // all other cases: result will fit + + run_container_t *ans = src; + int my_nbr_runs = src->n_runs; + + ans->n_runs = 0; + int k = 0; + for (; (k < my_nbr_runs) && (src->runs[k].value < range_start); ++k) { + // ans->runs[k] = src->runs[k]; (would be self-copy) + ans->n_runs++; + } + + // as with Java implementation, use locals to give self a buffer of depth 1 + rle16_t buffered = MAKE_RLE16(0, 0); + rle16_t next = buffered; + if (k < my_nbr_runs) buffered = src->runs[k]; + + run_container_smart_append_exclusive( + ans, (uint16_t)range_start, (uint16_t)(range_end - range_start - 1)); + + for (; k < my_nbr_runs; ++k) { + if (k + 1 < my_nbr_runs) next = src->runs[k + 1]; + + run_container_smart_append_exclusive(ans, buffered.value, + buffered.length); + buffered = next; + } + + *dst = convert_run_to_efficient_container(ans, &return_typecode); + if (return_typecode != RUN_CONTAINER_TYPE) run_container_free(ans); + + return return_typecode; +} + +}} +/* end file src/containers/mixed_negation.c */ +/* begin file src/containers/mixed_subset.c */ + +namespace paimon::roaring { namespace internal { + +bool array_container_is_subset_bitset(const array_container_t* container1, + const bitset_container_t* container2) { + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality < container1->cardinality) { + return false; + } + } + for (int i = 0; i < container1->cardinality; ++i) { + if (!bitset_container_contains(container2, container1->array[i])) { + return false; + } + } + return true; +} + +bool run_container_is_subset_array(const run_container_t* container1, + const array_container_t* container2) { + if (run_container_cardinality(container1) > container2->cardinality) + return false; + int32_t start_pos = -1, stop_pos = -1; + for (int i = 0; i < container1->n_runs; ++i) { + int32_t start = container1->runs[i].value; + int32_t stop = start + container1->runs[i].length; + start_pos = advanceUntil(container2->array, stop_pos, + container2->cardinality, start); + stop_pos = advanceUntil(container2->array, stop_pos, + container2->cardinality, stop); + if (stop_pos == container2->cardinality) { + return false; + } else if (stop_pos - start_pos != stop - start || + container2->array[start_pos] != start || + container2->array[stop_pos] != stop) { + return false; + } + } + return true; +} + +bool array_container_is_subset_run(const array_container_t* container1, + const run_container_t* container2) { + if (container1->cardinality > run_container_cardinality(container2)) + return false; + int i_array = 0, i_run = 0; + while (i_array < container1->cardinality && i_run < container2->n_runs) { + uint32_t start = container2->runs[i_run].value; + uint32_t stop = start + container2->runs[i_run].length; + if (container1->array[i_array] < start) { + return false; + } else if (container1->array[i_array] > stop) { + i_run++; + } else { // the value of the array is in the run + i_array++; + } + } + if (i_array == container1->cardinality) { + return true; + } else { + return false; + } +} + +bool run_container_is_subset_bitset(const run_container_t* container1, + const bitset_container_t* container2) { + // todo: this code could be much faster + if (container2->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container2->cardinality < run_container_cardinality(container1)) { + return false; + } + } else { + int32_t card = bitset_container_compute_cardinality( + container2); // modify container2? + if (card < run_container_cardinality(container1)) { + return false; + } + } + for (int i = 0; i < container1->n_runs; ++i) { + uint32_t run_start = container1->runs[i].value; + uint32_t le = container1->runs[i].length; + for (uint32_t j = run_start; j <= run_start + le; ++j) { + if (!bitset_container_contains(container2, j)) { + return false; + } + } + } + return true; +} + +bool bitset_container_is_subset_run(const bitset_container_t* container1, + const run_container_t* container2) { + // todo: this code could be much faster + if (container1->cardinality != BITSET_UNKNOWN_CARDINALITY) { + if (container1->cardinality > run_container_cardinality(container2)) { + return false; + } + } + int32_t i_bitset = 0, i_run = 0; + while (i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS && + i_run < container2->n_runs) { + uint64_t w = container1->words[i_bitset]; + while (w != 0 && i_run < container2->n_runs) { + uint32_t start = container2->runs[i_run].value; + uint32_t stop = start + container2->runs[i_run].length; + uint64_t t = w & (~w + 1); + uint16_t r = i_bitset * 64 + roaring_trailing_zeroes(w); + if (r < start) { + return false; + } else if (r > stop) { + i_run++; + continue; + } else { + w ^= t; + } + } + if (w == 0) { + i_bitset++; + } else { + return false; + } + } + if (i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS) { + // terminated iterating on the run containers, check that rest of bitset + // is empty + for (; i_bitset < BITSET_CONTAINER_SIZE_IN_WORDS; i_bitset++) { + if (container1->words[i_bitset] != 0) { + return false; + } + } + } + return true; +} + +}} +/* end file src/containers/mixed_subset.c */ +/* begin file src/containers/mixed_union.c */ +/* + * mixed_union.c + * + */ + +#include +#include + + +namespace paimon::roaring { namespace internal { + +/* Compute the union of src_1 and src_2 and write the result to + * dst. */ +void array_bitset_container_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + dst->cardinality = (int32_t)bitset_set_list_withcard( + dst->words, dst->cardinality, src_1->array, src_1->cardinality); +} + +/* Compute the union of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). */ +void array_bitset_container_lazy_union(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + bitset_set_list(dst->words, src_1->array, src_1->cardinality); + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +void run_bitset_container_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + assert(!run_container_is_full(src_1)); // catch this case upstream + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_set_lenrange(dst->words, rle.value, rle.length); + } + dst->cardinality = bitset_container_compute_cardinality(dst); +} + +void run_bitset_container_lazy_union(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + assert(!run_container_is_full(src_1)); // catch this case upstream + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_set_lenrange(dst->words, rle.value, rle.length); + } + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +// why do we leave the result as a run container?? +void array_run_container_union(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + if (run_container_is_full(src_2)) { + run_container_copy(src_2, dst); + return; + } + // TODO: see whether the "2*" is spurious + run_container_grow(dst, 2 * (src_1->cardinality + src_2->n_runs), false); + int32_t rlepos = 0; + int32_t arraypos = 0; + rle16_t previousrle; + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + previousrle = run_container_append_first(dst, src_2->runs[rlepos]); + rlepos++; + } else { + previousrle = + run_container_append_value_first(dst, src_1->array[arraypos]); + arraypos++; + } + while ((rlepos < src_2->n_runs) && (arraypos < src_1->cardinality)) { + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + run_container_append(dst, src_2->runs[rlepos], &previousrle); + rlepos++; + } else { + run_container_append_value(dst, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } + if (arraypos < src_1->cardinality) { + while (arraypos < src_1->cardinality) { + run_container_append_value(dst, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } else { + while (rlepos < src_2->n_runs) { + run_container_append(dst, src_2->runs[rlepos], &previousrle); + rlepos++; + } + } +} + +void array_run_container_inplace_union(const array_container_t *src_1, + run_container_t *src_2) { + if (run_container_is_full(src_2)) { + return; + } + const int32_t maxoutput = src_1->cardinality + src_2->n_runs; + const int32_t neededcapacity = maxoutput + src_2->n_runs; + if (src_2->capacity < neededcapacity) + run_container_grow(src_2, neededcapacity, true); + memmove(src_2->runs + maxoutput, src_2->runs, + src_2->n_runs * sizeof(rle16_t)); + rle16_t *inputsrc2 = src_2->runs + maxoutput; + int32_t rlepos = 0; + int32_t arraypos = 0; + int src2nruns = src_2->n_runs; + src_2->n_runs = 0; + + rle16_t previousrle; + + if (inputsrc2[rlepos].value <= src_1->array[arraypos]) { + previousrle = run_container_append_first(src_2, inputsrc2[rlepos]); + rlepos++; + } else { + previousrle = + run_container_append_value_first(src_2, src_1->array[arraypos]); + arraypos++; + } + + while ((rlepos < src2nruns) && (arraypos < src_1->cardinality)) { + if (inputsrc2[rlepos].value <= src_1->array[arraypos]) { + run_container_append(src_2, inputsrc2[rlepos], &previousrle); + rlepos++; + } else { + run_container_append_value(src_2, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } + if (arraypos < src_1->cardinality) { + while (arraypos < src_1->cardinality) { + run_container_append_value(src_2, src_1->array[arraypos], + &previousrle); + arraypos++; + } + } else { + while (rlepos < src2nruns) { + run_container_append(src_2, inputsrc2[rlepos], &previousrle); + rlepos++; + } + } +} + +bool array_array_container_union( + const array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = src_1->cardinality + src_2->cardinality; + if (totalCardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) { + array_container_union(src_1, src_2, CAST_array(*dst)); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = CAST_bitset(*dst); + bitset_set_list(ourbitset->words, src_1->array, src_1->cardinality); + ourbitset->cardinality = (int32_t)bitset_set_list_withcard( + ourbitset->words, src_1->cardinality, src_2->array, + src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + *dst = array_container_from_bitset(ourbitset); + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + } + return returnval; +} + +bool array_array_container_inplace_union( + array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = src_1->cardinality + src_2->cardinality; + *dst = NULL; + if (totalCardinality <= DEFAULT_MAX_SIZE) { + if(src_1->capacity < totalCardinality) { + *dst = array_container_create_given_capacity(2 * totalCardinality); // be purposefully generous + if (*dst != NULL) { + array_container_union(src_1, src_2, CAST_array(*dst)); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } else { + memmove(src_1->array + src_2->cardinality, src_1->array, src_1->cardinality * sizeof(uint16_t)); + // In theory, we could use fast_union_uint16, but it is unsafe. It fails + // with Intel compilers in particular. + // https://github.com/RoaringBitmap/CRoaring/pull/452 + // See report https://github.com/RoaringBitmap/CRoaring/issues/476 + src_1->cardinality = (int32_t)union_uint16(src_1->array + src_2->cardinality, src_1->cardinality, + src_2->array, src_2->cardinality, src_1->array); + return false; // not a bitset + } + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = CAST_bitset(*dst); + bitset_set_list(ourbitset->words, src_1->array, src_1->cardinality); + ourbitset->cardinality = (int32_t)bitset_set_list_withcard( + ourbitset->words, src_1->cardinality, src_2->array, + src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + if(src_1->capacity < ourbitset->cardinality) { + array_container_grow(src_1, ourbitset->cardinality, false); + } + + bitset_extract_setbits_uint16(ourbitset->words, BITSET_CONTAINER_SIZE_IN_WORDS, + src_1->array, 0); + src_1->cardinality = ourbitset->cardinality; + *dst = src_1; + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + } + return returnval; +} + + +bool array_array_container_lazy_union( + const array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = src_1->cardinality + src_2->cardinality; + // + // We assume that operations involving bitset containers will be faster than + // operations involving solely array containers, except maybe when array containers + // are small. Indeed, for example, it is cheap to compute the union between an array and + // a bitset container, generally more so than between a large array and another array. + // So it is advantageous to favour bitset containers during the computation. + // Of course, if we convert array containers eagerly to bitset containers, we may later + // need to revert the bitset containers to array containerr to satisfy the Roaring format requirements, + // but such one-time conversions at the end may not be overly expensive. We arrived to this design + // based on extensive benchmarking. + // + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) { + array_container_union(src_1, src_2, CAST_array(*dst)); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = CAST_bitset(*dst); + bitset_set_list(ourbitset->words, src_1->array, src_1->cardinality); + bitset_set_list(ourbitset->words, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} + + +bool array_array_container_lazy_inplace_union( + array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = src_1->cardinality + src_2->cardinality; + *dst = NULL; + // + // We assume that operations involving bitset containers will be faster than + // operations involving solely array containers, except maybe when array containers + // are small. Indeed, for example, it is cheap to compute the union between an array and + // a bitset container, generally more so than between a large array and another array. + // So it is advantageous to favour bitset containers during the computation. + // Of course, if we convert array containers eagerly to bitset containers, we may later + // need to revert the bitset containers to array containerr to satisfy the Roaring format requirements, + // but such one-time conversions at the end may not be overly expensive. We arrived to this design + // based on extensive benchmarking. + // + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + if(src_1->capacity < totalCardinality) { + *dst = array_container_create_given_capacity(2 * totalCardinality); // be purposefully generous + if (*dst != NULL) { + array_container_union(src_1, src_2, CAST_array(*dst)); + } else { + return true; // otherwise failure won't be caught + } + return false; // not a bitset + } else { + memmove(src_1->array + src_2->cardinality, src_1->array, src_1->cardinality * sizeof(uint16_t)); + /* + Next line is safe: + + We just need to focus on the reading and writing performed on array1. In `union_vector16`, both vectorized and scalar code still obey the basic rule: read from two inputs, do the union, and then write the output. + + Let's say the length(cardinality) of input2 is L2: + ``` + |<- L2 ->| + array1: [output--- |input 1---|---] + array2: [input 2---] + ``` + Let's define 3 __m128i pointers, `pos1` starts from `input1`, `pos2` starts from `input2`, these 2 point at the next byte to read, `out` starts from `output`, pointing at the next byte to overwrite. + ``` + array1: [output--- |input 1---|---] + ^ ^ + out pos1 + array2: [input 2---] + ^ + pos2 + ``` + The union output always contains less or equal number of elements than all inputs added, so we have: + ``` + out <= pos1 + pos2 + ``` + therefore: + ``` + out <= pos1 + L2 + ``` + which means you will not overwrite data beyond pos1, so the data haven't read is safe, and we don't care the data already read. + */ + src_1->cardinality = (int32_t)fast_union_uint16(src_1->array + src_2->cardinality, src_1->cardinality, + src_2->array, src_2->cardinality, src_1->array); + return false; // not a bitset + } + } + *dst = bitset_container_create(); + bool returnval = true; // expect a bitset + if (*dst != NULL) { + bitset_container_t *ourbitset = CAST_bitset(*dst); + bitset_set_list(ourbitset->words, src_1->array, src_1->cardinality); + bitset_set_list(ourbitset->words, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} + +}} +/* end file src/containers/mixed_union.c */ +/* begin file src/containers/mixed_xor.c */ +/* + * mixed_xor.c + */ + +#include +#include + + +namespace paimon::roaring { namespace internal { + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). + * Result is true iff dst is a bitset */ +bool array_bitset_container_xor( + const array_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bitset_container_t *result = bitset_container_create(); + bitset_container_copy(src_2, result); + result->cardinality = (int32_t)bitset_flip_list_withcard( + result->words, result->cardinality, src_1->array, src_1->cardinality); + + // do required type conversions. + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. It is allowed for src_2 to be dst. This version does not + * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). + */ + +void array_bitset_container_lazy_xor(const array_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + bitset_flip_list(dst->words, src_1->array, src_1->cardinality); + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_xor( + const run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bitset_container_t *result = bitset_container_create(); + + bitset_container_copy(src_2, result); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_flip_range(result->words, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + result->cardinality = bitset_container_compute_cardinality(result); + + if (result->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(result); + bitset_container_free(result); + return false; // not bitset + } + *dst = result; + return true; // bitset +} + +/* lazy xor. Dst is initialized and may be equal to src_2. + * Result is left as a bitset container, even if actual + * cardinality would dictate an array container. + */ + +void run_bitset_container_lazy_xor(const run_container_t *src_1, + const bitset_container_t *src_2, + bitset_container_t *dst) { + if (src_2 != dst) bitset_container_copy(src_2, dst); + for (int32_t rlepos = 0; rlepos < src_1->n_runs; ++rlepos) { + rle16_t rle = src_1->runs[rlepos]; + bitset_flip_range(dst->words, rle.value, + rle.value + rle.length + UINT32_C(1)); + } + dst->cardinality = BITSET_UNKNOWN_CARDINALITY; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_xor( + const array_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + // semi following Java XOR implementation as of May 2016 + // the C OR implementation works quite differently and can return a run + // container + // TODO could optimize for full run containers. + + // use of lazy following Java impl. + const int arbitrary_threshold = 32; + if (src_1->cardinality < arbitrary_threshold) { + run_container_t *ans = run_container_create(); + array_run_container_lazy_xor(src_1, src_2, ans); // keeps runs. + uint8_t typecode_after; + *dst = + convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; + } + + int card = run_container_cardinality(src_2); + if (card <= DEFAULT_MAX_SIZE) { + // Java implementation works with the array, xoring the run elements via + // iterator + array_container_t *temp = array_container_from_run(src_2); + bool ret_is_bitset = array_array_container_xor(temp, src_1, dst); + array_container_free(temp); + return ret_is_bitset ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE; + + } else { // guess that it will end up as a bitset + bitset_container_t *result = bitset_container_from_run(src_2); + bool is_bitset = bitset_array_container_ixor(result, src_1, dst); + // any necessary type conversion has been done by the ixor + int retval = (is_bitset ? BITSET_CONTAINER_TYPE + : ARRAY_CONTAINER_TYPE); + return retval; + } +} + +/* Dst is a valid run container. (Can it be src_2? Let's say not.) + * Leaves result as run container, even if other options are + * smaller. + */ + +void array_run_container_lazy_xor(const array_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + run_container_grow(dst, src_1->cardinality + src_2->n_runs, false); + int32_t rlepos = 0; + int32_t arraypos = 0; + dst->n_runs = 0; + + while ((rlepos < src_2->n_runs) && (arraypos < src_1->cardinality)) { + if (src_2->runs[rlepos].value <= src_1->array[arraypos]) { + run_container_smart_append_exclusive(dst, src_2->runs[rlepos].value, + src_2->runs[rlepos].length); + rlepos++; + } else { + run_container_smart_append_exclusive(dst, src_1->array[arraypos], + 0); + arraypos++; + } + } + while (arraypos < src_1->cardinality) { + run_container_smart_append_exclusive(dst, src_1->array[arraypos], 0); + arraypos++; + } + while (rlepos < src_2->n_runs) { + run_container_smart_append_exclusive(dst, src_2->runs[rlepos].value, + src_2->runs[rlepos].length); + rlepos++; + } +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int run_run_container_xor( + const run_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + run_container_t *ans = run_container_create(); + run_container_xor(src_1, src_2, ans); + uint8_t typecode_after; + *dst = convert_run_to_efficient_container_and_free(ans, &typecode_after); + return typecode_after; +} + +/* + * Java implementation (as of May 2016) for array_run, run_run + * and bitset_run don't do anything different for inplace. + * Could adopt the mixed_union.c approach instead (ie, using + * smart_append_exclusive) + * + */ + +bool array_array_container_xor( + const array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = + src_1->cardinality + src_2->cardinality; // upper bound + if (totalCardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_create_given_capacity(totalCardinality); + array_container_xor(src_1, src_2, CAST_array(*dst)); + return false; // not a bitset + } + *dst = bitset_container_from_array(src_1); + bool returnval = true; // expect a bitset + bitset_container_t *ourbitset = CAST_bitset(*dst); + ourbitset->cardinality = (uint32_t)bitset_flip_list_withcard( + ourbitset->words, src_1->cardinality, src_2->array, src_2->cardinality); + if (ourbitset->cardinality <= DEFAULT_MAX_SIZE) { + // need to convert! + *dst = array_container_from_bitset(ourbitset); + bitset_container_free(ourbitset); + returnval = false; // not going to be a bitset + } + + return returnval; +} + +bool array_array_container_lazy_xor( + const array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int totalCardinality = src_1->cardinality + src_2->cardinality; + // + // We assume that operations involving bitset containers will be faster than + // operations involving solely array containers, except maybe when array containers + // are small. Indeed, for example, it is cheap to compute the exclusive union between an array and + // a bitset container, generally more so than between a large array and another array. + // So it is advantageous to favour bitset containers during the computation. + // Of course, if we convert array containers eagerly to bitset containers, we may later + // need to revert the bitset containers to array containerr to satisfy the Roaring format requirements, + // but such one-time conversions at the end may not be overly expensive. We arrived to this design + // based on extensive benchmarking on unions. + // For XOR/exclusive union, we simply followed the heuristic used by the unions (see mixed_union.c). + // Further tuning is possible. + // + if (totalCardinality <= ARRAY_LAZY_LOWERBOUND) { + *dst = array_container_create_given_capacity(totalCardinality); + if (*dst != NULL) + array_container_xor(src_1, src_2, CAST_array(*dst)); + return false; // not a bitset + } + *dst = bitset_container_from_array(src_1); + bool returnval = true; // expect a bitset (maybe, for XOR??) + if (*dst != NULL) { + bitset_container_t *ourbitset = CAST_bitset(*dst); + bitset_flip_list(ourbitset->words, src_2->array, src_2->cardinality); + ourbitset->cardinality = BITSET_UNKNOWN_CARDINALITY; + } + return returnval; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). Return value is + * "dst is a bitset" + */ + +bool bitset_bitset_container_xor( + const bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bitset_container_t *ans = bitset_container_create(); + int card = bitset_container_xor(src_1, src_2, ans); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(ans); + bitset_container_free(ans); + return false; // not bitset + } else { + *dst = ans; + return true; + } +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst (which has no container initially). It will modify src_1 + * to be dst if the result is a bitset. Otherwise, it will + * free src_1 and dst will be a new array container. In both + * cases, the caller is responsible for deallocating dst. + * Returns true iff dst is a bitset */ + +bool bitset_array_container_ixor( + bitset_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + *dst = src_1; + src_1->cardinality = (uint32_t)bitset_flip_list_withcard( + src_1->words, src_1->cardinality, src_2->array, src_2->cardinality); + + if (src_1->cardinality <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else + return true; +} + +/* a bunch of in-place, some of which may not *really* be inplace. + * TODO: write actual inplace routine if efficiency warrants it + * Anything inplace with a bitset is a good candidate + */ + +bool bitset_bitset_container_ixor( + bitset_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + int card = bitset_container_xor(src_1, src_2, src_1); + if (card <= DEFAULT_MAX_SIZE) { + *dst = array_container_from_bitset(src_1); + bitset_container_free(src_1); + return false; // not bitset + } else { + *dst = src_1; + return true; + } +} + +bool array_bitset_container_ixor( + array_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bool ans = array_bitset_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +/* Compute the xor of src_1 and src_2 and write the result to + * dst. Result may be either a bitset or an array container + * (returns "result is bitset"). dst does not initially have + * any container, but becomes either a bitset container (return + * result true) or an array container. + */ + +bool run_bitset_container_ixor( + run_container_t *src_1, const bitset_container_t *src_2, + container_t **dst +){ + bool ans = run_bitset_container_xor(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +bool bitset_run_container_ixor( + bitset_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + bool ans = run_bitset_container_xor(src_2, src_1, dst); + bitset_container_free(src_1); + return ans; +} + +/* dst does not indicate a valid container initially. Eventually it + * can become any kind of container. + */ + +int array_run_container_ixor( + array_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + int ans = array_run_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +int run_array_container_ixor( + run_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + int ans = array_run_container_xor(src_2, src_1, dst); + run_container_free(src_1); + return ans; +} + +bool array_array_container_ixor( + array_container_t *src_1, const array_container_t *src_2, + container_t **dst +){ + bool ans = array_array_container_xor(src_1, src_2, dst); + array_container_free(src_1); + return ans; +} + +int run_run_container_ixor( + run_container_t *src_1, const run_container_t *src_2, + container_t **dst +){ + int ans = run_run_container_xor(src_1, src_2, dst); + run_container_free(src_1); + return ans; +} + +}} +/* end file src/containers/mixed_xor.c */ +/* begin file src/containers/run.c */ +#include +#include + + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { namespace internal { + +inline uint16_t run_container_minimum(const run_container_t *run); +inline uint16_t run_container_maximum(const run_container_t *run); +inline int32_t interleavedBinarySearch(const rle16_t *array, + int32_t lenarray, uint16_t ikey); +inline bool run_container_contains(const run_container_t *run, + uint16_t pos); +inline int run_container_index_equalorlarger(const run_container_t *arr, uint16_t x); +inline bool run_container_is_full(const run_container_t *run); +inline bool run_container_nonzero_cardinality(const run_container_t *rc); +inline int32_t run_container_serialized_size_in_bytes(int32_t num_runs); +inline run_container_t *run_container_create_range(uint32_t start, + uint32_t stop); +inline int run_container_cardinality(const run_container_t *run); + + +bool run_container_add(run_container_t *run, uint16_t pos) { + int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos); + if (index >= 0) return false; // already there + index = -index - 2; // points to preceding value, possibly -1 + if (index >= 0) { // possible match + int32_t offset = pos - run->runs[index].value; + int32_t le = run->runs[index].length; + if (offset <= le) return false; // already there + if (offset == le + 1) { + // we may need to fuse + if (index + 1 < run->n_runs) { + if (run->runs[index + 1].value == pos + 1) { + // indeed fusion is needed + run->runs[index].length = run->runs[index + 1].value + + run->runs[index + 1].length - + run->runs[index].value; + recoverRoomAtIndex(run, (uint16_t)(index + 1)); + return true; + } + } + run->runs[index].length++; + return true; + } + if (index + 1 < run->n_runs) { + // we may need to fuse + if (run->runs[index + 1].value == pos + 1) { + // indeed fusion is needed + run->runs[index + 1].value = pos; + run->runs[index + 1].length = run->runs[index + 1].length + 1; + return true; + } + } + } + if (index == -1) { + // we may need to extend the first run + if (0 < run->n_runs) { + if (run->runs[0].value == pos + 1) { + run->runs[0].length++; + run->runs[0].value--; + return true; + } + } + } + makeRoomAtIndex(run, (uint16_t)(index + 1)); + run->runs[index + 1].value = pos; + run->runs[index + 1].length = 0; + return true; +} + +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create_given_capacity(int32_t size) { + run_container_t *run; + /* Allocate the run container itself. */ + if ((run = (run_container_t *)roaring_malloc(sizeof(run_container_t))) == NULL) { + return NULL; + } + if (size <= 0 ) { // we don't want to rely on malloc(0) + run->runs = NULL; + } else if ((run->runs = (rle16_t *)roaring_malloc(sizeof(rle16_t) * size)) == NULL) { + roaring_free(run); + return NULL; + } + run->capacity = size; + run->n_runs = 0; + return run; +} + +int run_container_shrink_to_fit(run_container_t *src) { + if (src->n_runs == src->capacity) return 0; // nothing to do + int savings = src->capacity - src->n_runs; + src->capacity = src->n_runs; + rle16_t *oldruns = src->runs; + src->runs = (rle16_t *)roaring_realloc(oldruns, src->capacity * sizeof(rle16_t)); + if (src->runs == NULL) roaring_free(oldruns); // should never happen? + return savings; +} +/* Create a new run container. Return NULL in case of failure. */ +run_container_t *run_container_create(void) { + return run_container_create_given_capacity(RUN_DEFAULT_INIT_SIZE); +} + +run_container_t *run_container_clone(const run_container_t *src) { + run_container_t *run = run_container_create_given_capacity(src->capacity); + if (run == NULL) return NULL; + run->capacity = src->capacity; + run->n_runs = src->n_runs; + memcpy(run->runs, src->runs, src->n_runs * sizeof(rle16_t)); + return run; +} + +void run_container_offset(const run_container_t *c, + container_t **loc, container_t **hic, + uint16_t offset) { + run_container_t *lo = NULL, *hi = NULL; + + bool split; + int lo_cap, hi_cap; + int top, pivot; + + top = (1 << 16) - offset; + pivot = run_container_index_equalorlarger(c, top); + + if (pivot == -1) { + split = false; + lo_cap = c->n_runs; + hi_cap = 0; + } else { + split = c->runs[pivot].value < top; + lo_cap = pivot + (split ? 1 : 0); + hi_cap = c->n_runs - pivot; + } + + if (loc && lo_cap) { + lo = run_container_create_given_capacity(lo_cap); + memcpy(lo->runs, c->runs, lo_cap*sizeof(rle16_t)); + lo->n_runs = lo_cap; + for (int i = 0; i < lo_cap; ++i) { + lo->runs[i].value += offset; + } + *loc = (container_t*)lo; + } + + if (hic && hi_cap) { + hi = run_container_create_given_capacity(hi_cap); + memcpy(hi->runs, c->runs+pivot, hi_cap*sizeof(rle16_t)); + hi->n_runs = hi_cap; + for (int i = 0; i < hi_cap; ++i) { + hi->runs[i].value += offset; + } + *hic = (container_t*)hi; + } + + // Fix the split. + if (split) { + if (lo != NULL) { + // Add the missing run to 'lo', exhausting length. + lo->runs[lo->n_runs-1].length = (1 << 16) - lo->runs[lo->n_runs-1].value - 1; + } + + if (hi != NULL) { + // Fix the first run in 'hi'. + hi->runs[0].length -= UINT16_MAX - hi->runs[0].value + 1; + hi->runs[0].value = 0; + } + } +} + +/* Free memory. */ +void run_container_free(run_container_t *run) { + if(run->runs != NULL) {// Jon Strabala reports that some tools complain otherwise + roaring_free(run->runs); + run->runs = NULL; // pedantic + } + roaring_free(run); +} + +void run_container_grow(run_container_t *run, int32_t min, bool copy) { + int32_t newCapacity = + (run->capacity == 0) + ? RUN_DEFAULT_INIT_SIZE + : run->capacity < 64 ? run->capacity * 2 + : run->capacity < 1024 ? run->capacity * 3 / 2 + : run->capacity * 5 / 4; + if (newCapacity < min) newCapacity = min; + run->capacity = newCapacity; + assert(run->capacity >= min); + if (copy) { + rle16_t *oldruns = run->runs; + run->runs = + (rle16_t *)roaring_realloc(oldruns, run->capacity * sizeof(rle16_t)); + if (run->runs == NULL) roaring_free(oldruns); + } else { + // Jon Strabala reports that some tools complain otherwise + if (run->runs != NULL) { + roaring_free(run->runs); + } + run->runs = (rle16_t *)roaring_malloc(run->capacity * sizeof(rle16_t)); + } + // We may have run->runs == NULL. +} + +/* copy one container into another */ +void run_container_copy(const run_container_t *src, run_container_t *dst) { + const int32_t n_runs = src->n_runs; + if (src->n_runs > dst->capacity) { + run_container_grow(dst, n_runs, false); + } + dst->n_runs = n_runs; + memcpy(dst->runs, src->runs, sizeof(rle16_t) * n_runs); +} + +/* Compute the union of `src_1' and `src_2' and write the result to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_union(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // TODO: this could be a lot more efficient + + // we start out with inexpensive checks + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + run_container_copy(src_1, dst); + return; + } + if (if2) { + run_container_copy(src_2, dst); + return; + } + } + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + dst->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + + rle16_t previousrle; + if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) { + previousrle = run_container_append_first(dst, src_1->runs[rlepos]); + rlepos++; + } else { + previousrle = run_container_append_first(dst, src_2->runs[xrlepos]); + xrlepos++; + } + + while ((xrlepos < src_2->n_runs) && (rlepos < src_1->n_runs)) { + rle16_t newrl; + if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) { + newrl = src_1->runs[rlepos]; + rlepos++; + } else { + newrl = src_2->runs[xrlepos]; + xrlepos++; + } + run_container_append(dst, newrl, &previousrle); + } + while (xrlepos < src_2->n_runs) { + run_container_append(dst, src_2->runs[xrlepos], &previousrle); + xrlepos++; + } + while (rlepos < src_1->n_runs) { + run_container_append(dst, src_1->runs[rlepos], &previousrle); + rlepos++; + } +} + +/* Compute the union of `src_1' and `src_2' and write the result to `src_1' + */ +void run_container_union_inplace(run_container_t *src_1, + const run_container_t *src_2) { + // TODO: this could be a lot more efficient + + // we start out with inexpensive checks + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return; + } + if (if2) { + run_container_copy(src_2, src_1); + return; + } + } + // we move the data to the end of the current array + const int32_t maxoutput = src_1->n_runs + src_2->n_runs; + const int32_t neededcapacity = maxoutput + src_1->n_runs; + if (src_1->capacity < neededcapacity) + run_container_grow(src_1, neededcapacity, true); + memmove(src_1->runs + maxoutput, src_1->runs, + src_1->n_runs * sizeof(rle16_t)); + rle16_t *inputsrc1 = src_1->runs + maxoutput; + const int32_t input1nruns = src_1->n_runs; + src_1->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + + rle16_t previousrle; + if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) { + previousrle = run_container_append_first(src_1, inputsrc1[rlepos]); + rlepos++; + } else { + previousrle = run_container_append_first(src_1, src_2->runs[xrlepos]); + xrlepos++; + } + while ((xrlepos < src_2->n_runs) && (rlepos < input1nruns)) { + rle16_t newrl; + if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) { + newrl = inputsrc1[rlepos]; + rlepos++; + } else { + newrl = src_2->runs[xrlepos]; + xrlepos++; + } + run_container_append(src_1, newrl, &previousrle); + } + while (xrlepos < src_2->n_runs) { + run_container_append(src_1, src_2->runs[xrlepos], &previousrle); + xrlepos++; + } + while (rlepos < input1nruns) { + run_container_append(src_1, inputsrc1[rlepos], &previousrle); + rlepos++; + } +} + +/* Compute the symmetric difference of `src_1' and `src_2' and write the result + * to `dst' + * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */ +void run_container_xor(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // don't bother to convert xor with full range into negation + // since negation is implemented similarly + + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + + int32_t pos1 = 0; + int32_t pos2 = 0; + dst->n_runs = 0; + + while ((pos1 < src_1->n_runs) && (pos2 < src_2->n_runs)) { + if (src_1->runs[pos1].value <= src_2->runs[pos2].value) { + run_container_smart_append_exclusive(dst, src_1->runs[pos1].value, + src_1->runs[pos1].length); + pos1++; + } else { + run_container_smart_append_exclusive(dst, src_2->runs[pos2].value, + src_2->runs[pos2].length); + pos2++; + } + } + while (pos1 < src_1->n_runs) { + run_container_smart_append_exclusive(dst, src_1->runs[pos1].value, + src_1->runs[pos1].length); + pos1++; + } + + while (pos2 < src_2->n_runs) { + run_container_smart_append_exclusive(dst, src_2->runs[pos2].value, + src_2->runs[pos2].length); + pos2++; + } +} + +/* Compute the intersection of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_intersection(const run_container_t *src_1, + const run_container_t *src_2, + run_container_t *dst) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + run_container_copy(src_2, dst); + return; + } + if (if2) { + run_container_copy(src_1, dst); + return; + } + } + // TODO: this could be a lot more efficient, could use SIMD optimizations + const int32_t neededcapacity = src_1->n_runs + src_2->n_runs; + if (dst->capacity < neededcapacity) + run_container_grow(dst, neededcapacity, false); + dst->n_runs = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + const int32_t lateststart = start > xstart ? start : xstart; + int32_t earliestend; + if (end == xend) { // improbable + earliestend = end; + rlepos++; + xrlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else if (end < xend) { + earliestend = end; + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + + } else { // end > xend + earliestend = xend; + xrlepos++; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } + dst->runs[dst->n_runs].value = (uint16_t)lateststart; + dst->runs[dst->n_runs].length = + (uint16_t)(earliestend - lateststart - 1); + dst->n_runs++; + } + } +} + +/* Compute the size of the intersection of src_1 and src_2 . */ +int run_container_intersection_cardinality(const run_container_t *src_1, + const run_container_t *src_2) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return run_container_cardinality(src_2); + } + if (if2) { + return run_container_cardinality(src_1); + } + } + int answer = 0; + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + const int32_t lateststart = start > xstart ? start : xstart; + int32_t earliestend; + if (end == xend) { // improbable + earliestend = end; + rlepos++; + xrlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else if (end < xend) { + earliestend = end; + rlepos++; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + + } else { // end > xend + earliestend = xend; + xrlepos++; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } + answer += earliestend - lateststart; + } + } + return answer; +} + +bool run_container_intersect(const run_container_t *src_1, + const run_container_t *src_2) { + const bool if1 = run_container_is_full(src_1); + const bool if2 = run_container_is_full(src_2); + if (if1 || if2) { + if (if1) { + return !run_container_empty(src_2); + } + if (if2) { + return !run_container_empty(src_1); + } + } + int32_t rlepos = 0; + int32_t xrlepos = 0; + int32_t start = src_1->runs[rlepos].value; + int32_t end = start + src_1->runs[rlepos].length + 1; + int32_t xstart = src_2->runs[xrlepos].value; + int32_t xend = xstart + src_2->runs[xrlepos].length + 1; + while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) { + if (end <= xstart) { + ++rlepos; + if (rlepos < src_1->n_runs) { + start = src_1->runs[rlepos].value; + end = start + src_1->runs[rlepos].length + 1; + } + } else if (xend <= start) { + ++xrlepos; + if (xrlepos < src_2->n_runs) { + xstart = src_2->runs[xrlepos].value; + xend = xstart + src_2->runs[xrlepos].length + 1; + } + } else { // they overlap + return true; + } + } + return false; +} + + +/* Compute the difference of src_1 and src_2 and write the result to + * dst. It is assumed that dst is distinct from both src_1 and src_2. */ +void run_container_andnot(const run_container_t *src_1, + const run_container_t *src_2, run_container_t *dst) { + // following Java implementation as of June 2016 + + if (dst->capacity < src_1->n_runs + src_2->n_runs) + run_container_grow(dst, src_1->n_runs + src_2->n_runs, false); + + dst->n_runs = 0; + + int rlepos1 = 0; + int rlepos2 = 0; + int32_t start = src_1->runs[rlepos1].value; + int32_t end = start + src_1->runs[rlepos1].length + 1; + int32_t start2 = src_2->runs[rlepos2].value; + int32_t end2 = start2 + src_2->runs[rlepos2].length + 1; + + while ((rlepos1 < src_1->n_runs) && (rlepos2 < src_2->n_runs)) { + if (end <= start2) { + // output the first run + dst->runs[dst->n_runs++] = MAKE_RLE16(start, end - start - 1); + rlepos1++; + if (rlepos1 < src_1->n_runs) { + start = src_1->runs[rlepos1].value; + end = start + src_1->runs[rlepos1].length + 1; + } + } else if (end2 <= start) { + // exit the second run + rlepos2++; + if (rlepos2 < src_2->n_runs) { + start2 = src_2->runs[rlepos2].value; + end2 = start2 + src_2->runs[rlepos2].length + 1; + } + } else { + if (start < start2) { + dst->runs[dst->n_runs++] = + MAKE_RLE16(start, start2 - start - 1); + } + if (end2 < end) { + start = end2; + } else { + rlepos1++; + if (rlepos1 < src_1->n_runs) { + start = src_1->runs[rlepos1].value; + end = start + src_1->runs[rlepos1].length + 1; + } + } + } + } + if (rlepos1 < src_1->n_runs) { + dst->runs[dst->n_runs++] = MAKE_RLE16(start, end - start - 1); + rlepos1++; + if (rlepos1 < src_1->n_runs) { + memcpy(dst->runs + dst->n_runs, src_1->runs + rlepos1, + sizeof(rle16_t) * (src_1->n_runs - rlepos1)); + dst->n_runs += src_1->n_runs - rlepos1; + } + } +} + +ALLOW_UNALIGNED +int run_container_to_uint32_array(void *vout, const run_container_t *cont, + uint32_t base) { + int outpos = 0; + uint32_t *out = (uint32_t *)vout; + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + for (int j = 0; j <= le; ++j) { + uint32_t val = run_start + j; + memcpy(out + outpos, &val, + sizeof(uint32_t)); // should be compiled as a MOV on x64 + outpos++; + } + } + return outpos; +} + +/* + * Print this container using printf (useful for debugging). + */ +void run_container_printf(const run_container_t *cont) { + for (int i = 0; i < cont->n_runs; ++i) { + uint16_t run_start = cont->runs[i].value; + uint16_t le = cont->runs[i].length; + printf("[%d,%d]", run_start, run_start + le); + } +} + +/* + * Print this container using printf as a comma-separated list of 32-bit + * integers starting at base. + */ +void run_container_printf_as_uint32_array(const run_container_t *cont, + uint32_t base) { + if (cont->n_runs == 0) return; + { + uint32_t run_start = base + cont->runs[0].value; + uint16_t le = cont->runs[0].length; + printf("%u", run_start); + for (uint32_t j = 1; j <= le; ++j) printf(",%u", run_start + j); + } + for (int32_t i = 1; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + for (uint32_t j = 0; j <= le; ++j) printf(",%u", run_start + j); + } +} + +/* + * Validate the container. Returns true if valid. + */ +bool run_container_validate(const run_container_t *run, const char **reason) { + if (run->n_runs < 0) { + *reason = "negative run count"; + return false; + } + if (run->capacity < 0) { + *reason = "negative run capacity"; + return false; + } + if (run->capacity < run->n_runs) { + *reason = "capacity less than run count"; + return false; + } + + if (run->n_runs == 0) { + return true; + } + if (run->runs == NULL) { + *reason = "NULL runs"; + return false; + } + + // Use uint32_t to avoid overflow issues on ranges that contain UINT16_MAX. + uint32_t last_end = 0; + for (int i = 0; i < run->n_runs; ++i) { + uint32_t start = run->runs[i].value; + uint32_t end = start + run->runs[i].length + 1; + if (end <= start) { + *reason = "run start + length overflow"; + return false; + } + if (end > (1<<16)) { + *reason = "run start + length too large"; + return false; + } + if (start < last_end) { + *reason = "run start less than last end"; + return false; + } + if (start == last_end && last_end != 0) { + *reason = "run start equal to last end, should have combined"; + return false; + } + last_end = end; + } + return true; +} + +int32_t run_container_write(const run_container_t *container, char *buf) { + uint16_t cast_16 = container->n_runs; + memcpy(buf, &cast_16, sizeof(uint16_t)); + memcpy(buf + sizeof(uint16_t), container->runs, + container->n_runs * sizeof(rle16_t)); + return run_container_size_in_bytes(container); +} + +int32_t run_container_read(int32_t cardinality, run_container_t *container, + const char *buf) { + (void)cardinality; + uint16_t cast_16; + memcpy(&cast_16, buf, sizeof(uint16_t)); + container->n_runs = cast_16; + if (container->n_runs > container->capacity) + run_container_grow(container, container->n_runs, false); + if(container->n_runs > 0) { + memcpy(container->runs, buf + sizeof(uint16_t), + container->n_runs * sizeof(rle16_t)); + } + return run_container_size_in_bytes(container); +} + +bool run_container_iterate(const run_container_t *cont, uint32_t base, + roaring_iterator iterator, void *ptr) { + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + + for (int j = 0; j <= le; ++j) + if (!iterator(run_start + j, ptr)) return false; + } + return true; +} + +bool run_container_iterate64(const run_container_t *cont, uint32_t base, + roaring_iterator64 iterator, uint64_t high_bits, + void *ptr) { + for (int i = 0; i < cont->n_runs; ++i) { + uint32_t run_start = base + cont->runs[i].value; + uint16_t le = cont->runs[i].length; + + for (int j = 0; j <= le; ++j) + if (!iterator(high_bits | (uint64_t)(run_start + j), ptr)) + return false; + } + return true; +} + +bool run_container_is_subset(const run_container_t *container1, + const run_container_t *container2) { + int i1 = 0, i2 = 0; + while (i1 < container1->n_runs && i2 < container2->n_runs) { + int start1 = container1->runs[i1].value; + int stop1 = start1 + container1->runs[i1].length; + int start2 = container2->runs[i2].value; + int stop2 = start2 + container2->runs[i2].length; + if (start1 < start2) { + return false; + } else { // start1 >= start2 + if (stop1 < stop2) { + i1++; + } else if (stop1 == stop2) { + i1++; + i2++; + } else { // stop1 > stop2 + i2++; + } + } + } + if (i1 == container1->n_runs) { + return true; + } else { + return false; + } +} + +// TODO: write smart_append_exclusive version to match the overloaded 1 param +// Java version (or is it even used?) + +// follows the Java implementation closely +// length is the rle-value. Ie, run [10,12) uses a length value 1. +void run_container_smart_append_exclusive(run_container_t *src, + const uint16_t start, + const uint16_t length) { + int old_end; + rle16_t *last_run = src->n_runs ? src->runs + (src->n_runs - 1) : NULL; + rle16_t *appended_last_run = src->runs + src->n_runs; + + if (!src->n_runs || + (start > (old_end = last_run->value + last_run->length + 1))) { + *appended_last_run = MAKE_RLE16(start, length); + src->n_runs++; + return; + } + if (old_end == start) { + // we merge + last_run->length += (length + 1); + return; + } + int new_end = start + length + 1; + + if (start == last_run->value) { + // wipe out previous + if (new_end < old_end) { + *last_run = MAKE_RLE16(new_end, old_end - new_end - 1); + return; + } else if (new_end > old_end) { + *last_run = MAKE_RLE16(old_end, new_end - old_end - 1); + return; + } else { + src->n_runs--; + return; + } + } + last_run->length = start - last_run->value - 1; + if (new_end < old_end) { + *appended_last_run = MAKE_RLE16(new_end, old_end - new_end - 1); + src->n_runs++; + } else if (new_end > old_end) { + *appended_last_run = MAKE_RLE16(old_end, new_end - old_end - 1); + src->n_runs++; + } +} + +bool run_container_select(const run_container_t *container, + uint32_t *start_rank, uint32_t rank, + uint32_t *element) { + for (int i = 0; i < container->n_runs; i++) { + uint16_t length = container->runs[i].length; + if (rank <= *start_rank + length) { + uint16_t value = container->runs[i].value; + *element = value + rank - (*start_rank); + return true; + } else + *start_rank += length + 1; + } + return false; +} + +int run_container_rank(const run_container_t *container, uint16_t x) { + int sum = 0; + uint32_t x32 = x; + for (int i = 0; i < container->n_runs; i++) { + uint32_t startpoint = container->runs[i].value; + uint32_t length = container->runs[i].length; + uint32_t endpoint = length + startpoint; + if (x <= endpoint) { + if (x < startpoint) break; + return sum + (x32 - startpoint) + 1; + } else { + sum += length + 1; + } + } + return sum; +} +uint32_t run_container_rank_many(const run_container_t *container, uint64_t start_rank, const uint32_t* begin, const uint32_t* end, uint64_t* ans){ + const uint16_t high = (uint16_t)((*begin) >> 16); + const uint32_t* iter = begin; + int sum = 0; + int i = 0; + for(;iter != end; iter++) { + uint32_t x = *iter; + uint16_t xhigh = (uint16_t)(x >> 16); + if(xhigh != high) return iter - begin; // stop at next container + + uint32_t x32 = x & 0xFFFF; + while(i < container->n_runs) { + uint32_t startpoint = container->runs[i].value; + uint32_t length = container->runs[i].length; + uint32_t endpoint = length + startpoint; + if (x32 <= endpoint) { + if (x32 < startpoint) { + *(ans++) = start_rank + sum; + } else { + *(ans++) = start_rank + sum + (x32 - startpoint) + 1; + } + break; + } else { + sum += length + 1; + i++; + } + } + if (i >= container->n_runs) *(ans++) = start_rank + sum; + } + + return iter - begin; +} + + +int run_container_get_index(const run_container_t *container, uint16_t x) { + if (run_container_contains(container, x)) { + int sum = 0; + uint32_t x32 = x; + for (int i = 0; i < container->n_runs; i++) { + uint32_t startpoint = container->runs[i].value; + uint32_t length = container->runs[i].length; + uint32_t endpoint = length + startpoint; + if (x <= endpoint) { + if (x < startpoint) break; + return sum + (x32 - startpoint); + } else { + sum += length + 1; + } + } + return sum - 1; + } else { + return -1; + } +} + +#if defined(CROARING_IS_X64) && CROARING_COMPILER_SUPPORTS_AVX512 + +CROARING_TARGET_AVX512 +ALLOW_UNALIGNED +/* Get the cardinality of `run'. Requires an actual computation. */ +static inline int _avx512_run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + int32_t k = 0; + const int32_t step = sizeof(__m512i) / sizeof(rle16_t); + if (n_runs > step) { + __m512i total = _mm512_setzero_si512(); + for (; k + step <= n_runs; k += step) { + __m512i ymm1 = _mm512_loadu_si512((const __m512i *)(runs + k)); + __m512i justlengths = _mm512_srli_epi32(ymm1, 16); + total = _mm512_add_epi32(total, justlengths); + } + + __m256i lo = _mm512_extracti32x8_epi32(total, 0); + __m256i hi = _mm512_extracti32x8_epi32(total, 1); + + // a store might be faster than extract? + uint32_t buffer[sizeof(__m256i) / sizeof(rle16_t)]; + _mm256_storeu_si256((__m256i *)buffer, lo); + sum += (buffer[0] + buffer[1]) + (buffer[2] + buffer[3]) + + (buffer[4] + buffer[5]) + (buffer[6] + buffer[7]); + + _mm256_storeu_si256((__m256i *)buffer, hi); + sum += (buffer[0] + buffer[1]) + (buffer[2] + buffer[3]) + + (buffer[4] + buffer[5]) + (buffer[6] + buffer[7]); + + } + for (; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} + +CROARING_UNTARGET_AVX512 + +CROARING_TARGET_AVX2 +ALLOW_UNALIGNED +/* Get the cardinality of `run'. Requires an actual computation. */ +static inline int _avx2_run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + int32_t k = 0; + const int32_t step = sizeof(__m256i) / sizeof(rle16_t); + if (n_runs > step) { + __m256i total = _mm256_setzero_si256(); + for (; k + step <= n_runs; k += step) { + __m256i ymm1 = _mm256_lddqu_si256((const __m256i *)(runs + k)); + __m256i justlengths = _mm256_srli_epi32(ymm1, 16); + total = _mm256_add_epi32(total, justlengths); + } + // a store might be faster than extract? + uint32_t buffer[sizeof(__m256i) / sizeof(rle16_t)]; + _mm256_storeu_si256((__m256i *)buffer, total); + sum += (buffer[0] + buffer[1]) + (buffer[2] + buffer[3]) + + (buffer[4] + buffer[5]) + (buffer[6] + buffer[7]); + } + for (; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} + +CROARING_UNTARGET_AVX2 + +/* Get the cardinality of `run'. Requires an actual computation. */ +static inline int _scalar_run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + for (int k = 0; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} + +int run_container_cardinality(const run_container_t *run) { +#if CROARING_COMPILER_SUPPORTS_AVX512 + if( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX512 ) { + return _avx512_run_container_cardinality(run); + } + else +#endif + if( paimon::roaring::internal::croaring_hardware_support() & ROARING_SUPPORTS_AVX2 ) { + return _avx2_run_container_cardinality(run); + } else { + return _scalar_run_container_cardinality(run); + } +} +#else + +/* Get the cardinality of `run'. Requires an actual computation. */ +int run_container_cardinality(const run_container_t *run) { + const int32_t n_runs = run->n_runs; + const rle16_t *runs = run->runs; + + /* by initializing with n_runs, we omit counting the +1 for each pair. */ + int sum = n_runs; + for (int k = 0; k < n_runs; ++k) { + sum += runs[k].length; + } + + return sum; +} +#endif + + +}} +/* end file src/containers/run.c */ +/* begin file src/isadetection.c */ + +/* From +https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h +Highly modified. + +Copyright (c) 2016- Facebook, Inc (Adam Paszke) +Copyright (c) 2014- Facebook, Inc (Soumith Chintala) +Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) +Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) +Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) +Copyright (c) 2011-2013 NYU (Clement Farabet) +Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, +Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute +(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, +Samy Bengio, Johnny Mariethoz) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. 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. + +3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories +America and IDIAP Research Institute 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. +*/ + +#include +#include +#include + +// We need portability.h to be included first, see +// https://github.com/RoaringBitmap/CRoaring/issues/394 +#if CROARING_REGULAR_VISUAL_STUDIO +#include +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) +#include +#endif // CROARING_REGULAR_VISUAL_STUDIO + +#if CROARING_IS_X64 +#ifndef CROARING_COMPILER_SUPPORTS_AVX512 +#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." +#endif // CROARING_COMPILER_SUPPORTS_AVX512 +#endif + +namespace paimon::roaring { namespace internal { +enum croaring_instruction_set { + CROARING_DEFAULT = 0x0, + CROARING_NEON = 0x1, + CROARING_AVX2 = 0x4, + CROARING_SSE42 = 0x8, + CROARING_PCLMULQDQ = 0x10, + CROARING_BMI1 = 0x20, + CROARING_BMI2 = 0x40, + CROARING_ALTIVEC = 0x80, + CROARING_AVX512F = 0x100, + CROARING_AVX512DQ = 0x200, + CROARING_AVX512BW = 0x400, + CROARING_AVX512VBMI2 = 0x800, + CROARING_AVX512BITALG = 0x1000, + CROARING_AVX512VPOPCNTDQ = 0x2000, + CROARING_UNINITIALIZED = 0x8000 +}; + +#if CROARING_COMPILER_SUPPORTS_AVX512 +unsigned int CROARING_AVX512_REQUIRED = (CROARING_AVX512F | CROARING_AVX512DQ | CROARING_AVX512BW | CROARING_AVX512VBMI2 | CROARING_AVX512BITALG | CROARING_AVX512VPOPCNTDQ); +#endif + +#if defined(__x86_64__) || defined(_M_AMD64) // x64 + + +static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, + uint32_t *edx) { +#if CROARING_REGULAR_VISUAL_STUDIO + int cpu_info[4]; + __cpuidex(cpu_info, *eax, *ecx); + *eax = cpu_info[0]; + *ebx = cpu_info[1]; + *ecx = cpu_info[2]; + *edx = cpu_info[3]; +#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) + uint32_t level = *eax; + __get_cpuid(level, eax, ebx, ecx, edx); +#else + uint32_t a = *eax, b, c = *ecx, d; + __asm__("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); + *eax = a; + *ebx = b; + *ecx = c; + *edx = d; +#endif +} + + +static inline uint64_t xgetbv(void) { +#if defined(_MSC_VER) + return _xgetbv(0); +#else + uint32_t xcr0_lo, xcr0_hi; + __asm__("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0)); + return xcr0_lo | ((uint64_t)xcr0_hi << 32); +#endif +} + +/** + * This is a relatively expensive function but it will get called at most + * *once* per compilation units. Normally, the CRoaring library is built + * as one compilation unit. + */ +static inline uint32_t dynamic_croaring_detect_supported_architectures(void) { + uint32_t eax, ebx, ecx, edx; + uint32_t host_isa = 0x0; + // Can be found on Intel ISA Reference for CPUID + static uint32_t cpuid_avx2_bit = 1 << 5; ///< @private Bit 5 of EBX for EAX=0x7 + static uint32_t cpuid_bmi1_bit = 1 << 3; ///< @private bit 3 of EBX for EAX=0x7 + static uint32_t cpuid_bmi2_bit = 1 << 8; ///< @private bit 8 of EBX for EAX=0x7 + static uint32_t cpuid_avx512f_bit = 1 << 16; ///< @private bit 16 of EBX for EAX=0x7 + static uint32_t cpuid_avx512dq_bit = 1 << 17; ///< @private bit 17 of EBX for EAX=0x7 + static uint32_t cpuid_avx512bw_bit = 1 << 30; ///< @private bit 30 of EBX for EAX=0x7 + static uint32_t cpuid_avx512vbmi2_bit = 1 << 6; ///< @private bit 6 of ECX for EAX=0x7 + static uint32_t cpuid_avx512bitalg_bit = 1 << 12; ///< @private bit 12 of ECX for EAX=0x7 + static uint32_t cpuid_avx512vpopcntdq_bit = 1 << 14; ///< @private bit 14 of ECX for EAX=0x7 + static uint64_t cpuid_avx256_saved = 1 << 2; ///< @private bit 2 = AVX + static uint64_t cpuid_avx512_saved = 7 << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM + static uint32_t cpuid_sse42_bit = 1 << 20; ///< @private bit 20 of ECX for EAX=0x1 + static uint32_t cpuid_osxsave = (1 << 26) | (1 << 27); ///< @private bits 26+27 of ECX for EAX=0x1 + static uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit 1 of ECX for EAX=0x1 + + + // EBX for EAX=0x1 + eax = 0x1; + ecx = 0x0; + cpuid(&eax, &ebx, &ecx, &edx); + + if (ecx & cpuid_sse42_bit) { + host_isa |= CROARING_SSE42; + } else { + return host_isa; // everything after is redundant + } + + if (ecx & cpuid_pclmulqdq_bit) { + host_isa |= CROARING_PCLMULQDQ; + } + + if ((ecx & cpuid_osxsave) != cpuid_osxsave) { + return host_isa; + } + + // xgetbv for checking if the OS saves registers + uint64_t xcr0 = xgetbv(); + + if ((xcr0 & cpuid_avx256_saved) == 0) { + return host_isa; + } + + // ECX for EAX=0x7 + eax = 0x7; + ecx = 0x0; + cpuid(&eax, &ebx, &ecx, &edx); + if (ebx & cpuid_avx2_bit) { + host_isa |= CROARING_AVX2; + } + if (ebx & cpuid_bmi1_bit) { + host_isa |= CROARING_BMI1; + } + + if (ebx & cpuid_bmi2_bit) { + host_isa |= CROARING_BMI2; + } + + if (!((xcr0 & cpuid_avx512_saved) == cpuid_avx512_saved)) { + return host_isa; + } + + if (ebx & cpuid_avx512f_bit) { + host_isa |= CROARING_AVX512F; + } + + if (ebx & cpuid_avx512bw_bit) { + host_isa |= CROARING_AVX512BW; + } + + if (ebx & cpuid_avx512dq_bit) { + host_isa |= CROARING_AVX512DQ; + } + + if (ecx & cpuid_avx512vbmi2_bit) { + host_isa |= CROARING_AVX512VBMI2; + } + + if (ecx & cpuid_avx512bitalg_bit) { + host_isa |= CROARING_AVX512BITALG; + } + + if (ecx & cpuid_avx512vpopcntdq_bit) { + host_isa |= CROARING_AVX512VPOPCNTDQ; + } + + return host_isa; +} + +#endif // end SIMD extension detection code + + +#if defined(__x86_64__) || defined(_M_AMD64) // x64 + +#if CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_CPP +static inline uint32_t croaring_detect_supported_architectures(void) { + // thread-safe as per the C++11 standard. + static uint32_t buffer = dynamic_croaring_detect_supported_architectures(); + return buffer; +} +#elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C +static uint32_t croaring_detect_supported_architectures(void) { + // we use an atomic for thread safety + static _Atomic uint32_t buffer = CROARING_UNINITIALIZED; + if (buffer == CROARING_UNINITIALIZED) { + // atomicity is sufficient + buffer = dynamic_croaring_detect_supported_architectures(); + } + return buffer; +} +#else +// If we do not have atomics, we do the best we can. +static inline uint32_t croaring_detect_supported_architectures(void) { + static uint32_t buffer = CROARING_UNINITIALIZED; + if (buffer == CROARING_UNINITIALIZED) { + buffer = dynamic_croaring_detect_supported_architectures(); + } + return buffer; +} +#endif // CROARING_C_ATOMIC + +#ifdef ROARING_DISABLE_AVX + +int croaring_hardware_support(void) { + return 0; +} + +#elif defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512BW__) && defined(__AVX512VBMI2__) && defined(__AVX512BITALG__) && defined(__AVX512VPOPCNTDQ__) +int croaring_hardware_support(void) { + return ROARING_SUPPORTS_AVX2 | ROARING_SUPPORTS_AVX512; +} +#elif defined(__AVX2__) + +int croaring_hardware_support(void) { + static +#if CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C + _Atomic +#endif + int support = 0xFFFFFFF; + if(support == 0xFFFFFFF) { + bool avx512_support = false; +#if CROARING_COMPILER_SUPPORTS_AVX512 + avx512_support = ( (croaring_detect_supported_architectures() & CROARING_AVX512_REQUIRED) + == CROARING_AVX512_REQUIRED); +#endif + support = ROARING_SUPPORTS_AVX2 | (avx512_support ? ROARING_SUPPORTS_AVX512 : 0); + } + return support; +} +#else + +int croaring_hardware_support(void) { + static +#if CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C + _Atomic +#endif + int support = 0xFFFFFFF; + if(support == 0xFFFFFFF) { + bool has_avx2 = (croaring_detect_supported_architectures() & CROARING_AVX2) == CROARING_AVX2; + bool has_avx512 = false; +#if CROARING_COMPILER_SUPPORTS_AVX512 + has_avx512 = (croaring_detect_supported_architectures() & CROARING_AVX512_REQUIRED) == CROARING_AVX512_REQUIRED; +#endif // CROARING_COMPILER_SUPPORTS_AVX512 + support = (has_avx2 ? ROARING_SUPPORTS_AVX2 : 0) | (has_avx512 ? ROARING_SUPPORTS_AVX512 : 0); + } + return support; +} +#endif + +#endif // defined(__x86_64__) || defined(_M_AMD64) // x64 +}} +/* end file src/isadetection.c */ +/* begin file src/memory.c */ +#include + +// without the following, we get lots of warnings about posix_memalign +#ifndef __cplusplus +extern int posix_memalign(void **__memptr, size_t __alignment, size_t __size); +#endif //__cplusplus // C++ does not have a well defined signature + +// portable version of posix_memalign +static void *roaring_bitmap_aligned_malloc(size_t alignment, size_t size) { + void *p; +#ifdef _MSC_VER + p = _aligned_malloc(size, alignment); +#elif defined(__MINGW32__) || defined(__MINGW64__) + p = __mingw_aligned_malloc(size, alignment); +#else + // somehow, if this is used before including "x86intrin.h", it creates an + // implicit defined warning. + if (posix_memalign(&p, alignment, size) != 0) return NULL; +#endif + return p; +} + +static void roaring_bitmap_aligned_free(void *memblock) { +#ifdef _MSC_VER + _aligned_free(memblock); +#elif defined(__MINGW32__) || defined(__MINGW64__) + __mingw_aligned_free(memblock); +#else + free(memblock); +#endif +} + +static roaring_memory_t global_memory_hook = { + .malloc = malloc, + .realloc = realloc, + .calloc = calloc, + .free = free, + .aligned_malloc = roaring_bitmap_aligned_malloc, + .aligned_free = roaring_bitmap_aligned_free, +}; + +void roaring_init_memory_hook(roaring_memory_t memory_hook) { + global_memory_hook = memory_hook; +} + +void* roaring_malloc(size_t n) { + return global_memory_hook.malloc(n); +} + +void* roaring_realloc(void* p, size_t new_sz) { + return global_memory_hook.realloc(p, new_sz); +} + +void* roaring_calloc(size_t n_elements, size_t element_size) { + return global_memory_hook.calloc(n_elements, element_size); +} + +void roaring_free(void* p) { + global_memory_hook.free(p); +} + +void* roaring_aligned_malloc(size_t alignment, size_t size) { + return global_memory_hook.aligned_malloc(alignment, size); +} + +void roaring_aligned_free(void* p) { + global_memory_hook.aligned_free(p); +} +/* end file src/memory.c */ +/* begin file src/roaring.c */ +#include +#include +#include +#include +#include +#include + + + +namespace paimon::roaring { namespace internal { +#define CROARING_SERIALIZATION_ARRAY_UINT32 1 +#define CROARING_SERIALIZATION_CONTAINER 2 +// int roaring_trailing_zeroes(unsigned long long input_num); +// int roaring_leading_zeroes(unsigned long long input_num); +inline void roaring_bitmap_init_cleared(roaring_bitmap_t *r); +inline bool roaring_bitmap_get_copy_on_write(const roaring_bitmap_t* r); +inline void roaring_bitmap_set_copy_on_write(roaring_bitmap_t* r, bool cow); +inline roaring_bitmap_t *roaring_bitmap_create(void); +inline void roaring_bitmap_add_range(roaring_bitmap_t *r, uint64_t min, uint64_t max); +inline void roaring_bitmap_remove_range(roaring_bitmap_t *r, uint64_t min, uint64_t max); + +static inline bool is_cow(const roaring_bitmap_t *r) { + return r->high_low_container.flags & ROARING_FLAG_COW; +} +static inline bool is_frozen(const roaring_bitmap_t *r) { + return r->high_low_container.flags & ROARING_FLAG_FROZEN; +} + +// this is like roaring_bitmap_add, but it populates pointer arguments in such a +// way +// that we can recover the container touched, which, in turn can be used to +// accelerate some functions (when you repeatedly need to add to the same +// container) +static inline container_t *containerptr_roaring_bitmap_add( + roaring_bitmap_t *r, uint32_t val, + uint8_t *type, int *index +){ + roaring_array_t *ra = &r->high_low_container; + + uint16_t hb = val >> 16; + const int i = ra_get_index(ra, hb); + if (i >= 0) { + ra_unshare_container_at_index(ra, (uint16_t)i); + container_t *c = ra_get_container_at_index(ra, (uint16_t)i, type); + uint8_t new_type = *type; + container_t *c2 = container_add(c, val & 0xFFFF, *type, &new_type); + *index = i; + if (c2 != c) { + container_free(c, *type); + ra_set_container_at_index(ra, i, c2, new_type); + *type = new_type; + return c2; + } else { + return c; + } + } else { + array_container_t *new_ac = array_container_create(); + container_t *c = container_add(new_ac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE, type); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(ra, -i - 1, hb, c, *type); + *index = -i - 1; + return c; + } +} + +roaring_bitmap_t *roaring_bitmap_create_with_capacity(uint32_t cap) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)roaring_malloc(sizeof(roaring_bitmap_t)); + if (!ans) { + return NULL; + } + bool is_ok = ra_init_with_capacity(&ans->high_low_container, cap); + if (!is_ok) { + roaring_free(ans); + return NULL; + } + return ans; +} + +bool roaring_bitmap_init_with_capacity(roaring_bitmap_t *r, uint32_t cap) { + return ra_init_with_capacity(&r->high_low_container, cap); +} + +static inline void add_bulk_impl(roaring_bitmap_t *r, + roaring_bulk_context_t *context, + uint32_t val) { + uint16_t key = val >> 16; + if (context->container == NULL || context->key != key) { + uint8_t typecode; + int idx; + context->container = containerptr_roaring_bitmap_add( + r, val, &typecode, &idx); + context->typecode = typecode; + context->idx = idx; + context->key = key; + } else { + // no need to seek the container, it is at hand + // because we already have the container at hand, we can do the + // insertion directly, bypassing the roaring_bitmap_add call + uint8_t new_typecode; + container_t *container2 = container_add( + context->container, val & 0xFFFF, context->typecode, &new_typecode); + if (container2 != context->container) { + // rare instance when we need to change the container type + container_free(context->container, context->typecode); + ra_set_container_at_index(&r->high_low_container, context->idx, + container2, new_typecode); + context->typecode = new_typecode; + context->container = container2; + } + } +} + +void roaring_bitmap_add_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals) { + uint32_t val; + const uint32_t *start = vals; + const uint32_t *end = vals + n_args; + const uint32_t *current_val = start; + + if (n_args == 0) { + return; + } + + uint8_t typecode; + int idx; + container_t *container; + val = *current_val; + container = containerptr_roaring_bitmap_add(r, val, &typecode, &idx); + roaring_bulk_context_t context = {container, idx, (uint16_t)(val >> 16), typecode}; + + for (; current_val != end; current_val++) { + memcpy(&val, current_val, sizeof(val)); + add_bulk_impl(r, &context, val); + } +} + +void roaring_bitmap_add_bulk(roaring_bitmap_t *r, + roaring_bulk_context_t *context, uint32_t val) { + add_bulk_impl(r, context, val); +} + +bool roaring_bitmap_contains_bulk(const roaring_bitmap_t *r, + roaring_bulk_context_t *context, + uint32_t val) +{ + uint16_t key = val >> 16; + if (context->container == NULL || context->key != key) { + int32_t start_idx = -1; + if (context->container != NULL && context->key < key) { + start_idx = context->idx; + } + int idx = ra_advance_until(&r->high_low_container, key, start_idx); + if (idx == ra_get_size(&r->high_low_container)) { + return false; + } + uint8_t typecode; + context->container = ra_get_container_at_index(&r->high_low_container, (uint16_t)idx, &typecode); + context->typecode = typecode; + context->idx = idx; + context->key = ra_get_key_at_index(&r->high_low_container, (uint16_t)idx); + // ra_advance_until finds the next key >= the target, we found a later container. + if (context->key != key) { + return false; + } + } + // context is now set up + return container_contains(context->container, val & 0xFFFF, context->typecode); +} + +roaring_bitmap_t *roaring_bitmap_of_ptr(size_t n_args, const uint32_t *vals) { + roaring_bitmap_t *answer = roaring_bitmap_create(); + roaring_bitmap_add_many(answer, n_args, vals); + return answer; +} + +roaring_bitmap_t *roaring_bitmap_of(size_t n_args, ...) { + // todo: could be greatly optimized but we do not expect this call to ever + // include long lists + roaring_bitmap_t *answer = roaring_bitmap_create(); + roaring_bulk_context_t context = {0}; + va_list ap; + va_start(ap, n_args); + for (size_t i = 0; i < n_args; i++) { + uint32_t val = va_arg(ap, uint32_t); + roaring_bitmap_add_bulk(answer, &context, val); + } + va_end(ap); + return answer; +} + +static inline uint32_t minimum_uint32(uint32_t a, uint32_t b) { + return (a < b) ? a : b; +} + +static inline uint64_t minimum_uint64(uint64_t a, uint64_t b) { + return (a < b) ? a : b; +} + +roaring_bitmap_t *roaring_bitmap_from_range(uint64_t min, uint64_t max, + uint32_t step) { + if(max >= UINT64_C(0x100000000)) { + max = UINT64_C(0x100000000); + } + if (step == 0) return NULL; + if (max <= min) return NULL; + roaring_bitmap_t *answer = roaring_bitmap_create(); + if (step >= (1 << 16)) { + for (uint32_t value = (uint32_t)min; value < max; value += step) { + roaring_bitmap_add(answer, value); + } + return answer; + } + uint64_t min_tmp = min; + do { + uint32_t key = (uint32_t)min_tmp >> 16; + uint32_t container_min = min_tmp & 0xFFFF; + uint32_t container_max = (uint32_t)minimum_uint64(max - (key << 16), 1 << 16); + uint8_t type; + container_t *container = container_from_range(&type, container_min, + container_max, (uint16_t)step); + ra_append(&answer->high_low_container, (uint16_t)key, container, type); + uint32_t gap = container_max - container_min + step - 1; + min_tmp += gap - (gap % step); + } while (min_tmp < max); + // cardinality of bitmap will be ((uint64_t) max - min + step - 1 ) / step + return answer; +} + +void roaring_bitmap_add_range_closed(roaring_bitmap_t *r, uint32_t min, uint32_t max) { + if (min > max) { + return; + } + + roaring_array_t *ra = &r->high_low_container; + + uint32_t min_key = min >> 16; + uint32_t max_key = max >> 16; + + int32_t num_required_containers = max_key - min_key + 1; + int32_t suffix_length = count_greater(ra->keys, ra->size, (uint16_t)max_key); + int32_t prefix_length = count_less(ra->keys, ra->size - suffix_length, + (uint16_t)min_key); + int32_t common_length = ra->size - prefix_length - suffix_length; + + if (num_required_containers > common_length) { + ra_shift_tail(ra, suffix_length, + num_required_containers - common_length); + } + + int32_t src = prefix_length + common_length - 1; + int32_t dst = ra->size - suffix_length - 1; + for (uint32_t key = max_key; key != min_key-1; key--) { // beware of min_key==0 + uint32_t container_min = (min_key == key) ? (min & 0xffff) : 0; + uint32_t container_max = (max_key == key) ? (max & 0xffff) : 0xffff; + container_t* new_container; + uint8_t new_type; + + if (src >= 0 && ra->keys[src] == key) { + ra_unshare_container_at_index(ra, (uint16_t)src); + new_container = container_add_range(ra->containers[src], + ra->typecodes[src], + container_min, container_max, + &new_type); + if (new_container != ra->containers[src]) { + container_free(ra->containers[src], + ra->typecodes[src]); + } + src--; + } else { + new_container = container_from_range(&new_type, container_min, + container_max+1, 1); + } + ra_replace_key_and_container_at_index(ra, dst, (uint16_t)key, new_container, + new_type); + dst--; + } +} + +void roaring_bitmap_remove_range_closed(roaring_bitmap_t *r, uint32_t min, uint32_t max) { + if (min > max) { + return; + } + + roaring_array_t *ra = &r->high_low_container; + + uint32_t min_key = min >> 16; + uint32_t max_key = max >> 16; + + int32_t src = count_less(ra->keys, ra->size, (uint16_t)min_key); + int32_t dst = src; + while (src < ra->size && ra->keys[src] <= max_key) { + uint32_t container_min = (min_key == ra->keys[src]) ? (min & 0xffff) : 0; + uint32_t container_max = (max_key == ra->keys[src]) ? (max & 0xffff) : 0xffff; + ra_unshare_container_at_index(ra, (uint16_t)src); + container_t *new_container; + uint8_t new_type; + new_container = container_remove_range(ra->containers[src], + ra->typecodes[src], + container_min, container_max, + &new_type); + if (new_container != ra->containers[src]) { + container_free(ra->containers[src], + ra->typecodes[src]); + } + if (new_container) { + ra_replace_key_and_container_at_index(ra, dst, ra->keys[src], + new_container, new_type); + dst++; + } + src++; + } + if (src > dst) { + ra_shift_tail(ra, ra->size - src, dst - src); + } +} + +void roaring_bitmap_printf(const roaring_bitmap_t *r) { + const roaring_array_t *ra = &r->high_low_container; + + printf("{"); + for (int i = 0; i < ra->size; ++i) { + container_printf_as_uint32_array(ra->containers[i], ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + + if (i + 1 < ra->size) { + printf(","); + } + } + printf("}"); +} + +void roaring_bitmap_printf_describe(const roaring_bitmap_t *r) { + const roaring_array_t *ra = &r->high_low_container; + + printf("{"); + for (int i = 0; i < ra->size; ++i) { + printf("%d: %s (%d)", ra->keys[i], + get_full_container_name(ra->containers[i], ra->typecodes[i]), + container_get_cardinality(ra->containers[i], ra->typecodes[i])); + if (ra->typecodes[i] == SHARED_CONTAINER_TYPE) { + printf("(shared count = %" PRIu32 " )", + croaring_refcount_get( + &(CAST_shared(ra->containers[i])->counter))); + } + + if (i + 1 < ra->size) { + printf(", "); + } + } + printf("}"); +} + +typedef struct min_max_sum_s { + uint32_t min; + uint32_t max; + uint64_t sum; +} min_max_sum_t; + +static bool min_max_sum_fnc(uint32_t value, void *param) { + min_max_sum_t *mms = (min_max_sum_t *)param; + if (value > mms->max) mms->max = value; + if (value < mms->min) mms->min = value; + mms->sum += value; + return true; // we always process all data points +} + +/** +* (For advanced users.) +* Collect statistics about the bitmap +*/ +void roaring_bitmap_statistics(const roaring_bitmap_t *r, + roaring_statistics_t *stat) { + const roaring_array_t *ra = &r->high_low_container; + + memset(stat, 0, sizeof(*stat)); + stat->n_containers = ra->size; + stat->cardinality = roaring_bitmap_get_cardinality(r); + min_max_sum_t mms; + mms.min = UINT32_C(0xFFFFFFFF); + mms.max = UINT32_C(0); + mms.sum = 0; + roaring_iterate(r, &min_max_sum_fnc, &mms); + stat->min_value = mms.min; + stat->max_value = mms.max; + stat->sum_value = mms.sum; + + for (int i = 0; i < ra->size; ++i) { + uint8_t truetype = + get_container_type(ra->containers[i], ra->typecodes[i]); + uint32_t card = + container_get_cardinality(ra->containers[i], ra->typecodes[i]); + uint32_t sbytes = + container_size_in_bytes(ra->containers[i], ra->typecodes[i]); + switch (truetype) { + case BITSET_CONTAINER_TYPE: + stat->n_bitset_containers++; + stat->n_values_bitset_containers += card; + stat->n_bytes_bitset_containers += sbytes; + break; + case ARRAY_CONTAINER_TYPE: + stat->n_array_containers++; + stat->n_values_array_containers += card; + stat->n_bytes_array_containers += sbytes; + break; + case RUN_CONTAINER_TYPE: + stat->n_run_containers++; + stat->n_values_run_containers += card; + stat->n_bytes_run_containers += sbytes; + break; + default: + assert(false); + roaring_unreachable; + } + } +} + +/* + * Checks that: + * - Array containers are sorted and contain no duplicates + * - Range containers are sorted and contain no overlapping ranges + * - Roaring containers are sorted by key and there are no duplicate keys + * - The correct container type is use for each container (e.g. bitmaps aren't used for small containers) + */ +bool roaring_bitmap_internal_validate(const roaring_bitmap_t *r, const char **reason) { + const char *reason_local; + if (reason == NULL) { + // Always allow assigning through *reason + reason = &reason_local; + } + *reason = NULL; + const roaring_array_t *ra = &r->high_low_container; + if (ra->size < 0) { + *reason = "negative size"; + return false; + } + if (ra->allocation_size < 0) { + *reason = "negative allocation size"; + return false; + } + if (ra->size > ra->allocation_size) { + *reason = "more containers than allocated space"; + return false; + } + if (ra->flags & ~(ROARING_FLAG_COW | ROARING_FLAG_FROZEN)) { + *reason = "invalid flags"; + return false; + } + if (ra->size == 0) { + return true; + } + + if (ra->keys == NULL) { + *reason = "keys is NULL"; + return false; + } + if (ra->typecodes == NULL) { + *reason = "typecodes is NULL"; + return false; + } + if (ra->containers == NULL) { + *reason = "containers is NULL"; + return false; + } + + uint32_t prev_key = ra->keys[0]; + for (int32_t i = 1; i < ra->size; ++i) { + if (ra->keys[i] <= prev_key) { + *reason = "keys not strictly increasing"; + return false; + } + prev_key = ra->keys[i]; + } + + for (int32_t i = 0; i < ra->size; ++i) { + if (!container_internal_validate(ra->containers[i], ra->typecodes[i], reason)) { + // reason should already be set + if (*reason == NULL) { + *reason = "container failed to validate but no reason given"; + } + return false; + } + } + + return true; +} + +roaring_bitmap_t *roaring_bitmap_copy(const roaring_bitmap_t *r) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)roaring_malloc(sizeof(roaring_bitmap_t)); + if (!ans) { + return NULL; + } + if (!ra_init_with_capacity( // allocation of list of containers can fail + &ans->high_low_container, r->high_low_container.size) + ){ + roaring_free(ans); + return NULL; + } + if (!ra_overwrite( // memory allocation of individual containers may fail + &r->high_low_container, &ans->high_low_container, is_cow(r)) + ){ + roaring_bitmap_free(ans); // overwrite should leave in freeable state + return NULL; + } + roaring_bitmap_set_copy_on_write(ans, is_cow(r)); + return ans; +} + +bool roaring_bitmap_overwrite(roaring_bitmap_t *dest, + const roaring_bitmap_t *src) { + roaring_bitmap_set_copy_on_write(dest, is_cow(src)); + return ra_overwrite(&src->high_low_container, &dest->high_low_container, + is_cow(src)); +} + +void roaring_bitmap_free(const roaring_bitmap_t *r) { + if(r == NULL) { return; } + if (!is_frozen(r)) { + ra_clear((roaring_array_t*)&r->high_low_container); + } + roaring_free((roaring_bitmap_t*)r); +} + +void roaring_bitmap_clear(roaring_bitmap_t *r) { + ra_reset(&r->high_low_container); +} + +void roaring_bitmap_add(roaring_bitmap_t *r, uint32_t val) { + roaring_array_t *ra = &r->high_low_container; + + const uint16_t hb = val >> 16; + const int i = ra_get_index(ra, hb); + uint8_t typecode; + if (i >= 0) { + ra_unshare_container_at_index(ra, (uint16_t)i); + container_t *container = + ra_get_container_at_index(ra, (uint16_t)i, &typecode); + uint8_t newtypecode = typecode; + container_t *container2 = + container_add(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + } else { + array_container_t *newac = array_container_create(); + container_t *container = container_add(newac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE, &typecode); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(&r->high_low_container, -i - 1, hb, + container, typecode); + } +} + +bool roaring_bitmap_add_checked(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + bool result = false; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, (uint16_t)i); + container_t *container = + ra_get_container_at_index(&r->high_low_container, (uint16_t)i, &typecode); + + const int oldCardinality = + container_get_cardinality(container, typecode); + + uint8_t newtypecode = typecode; + container_t *container2 = + container_add(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + result = true; + } else { + const int newCardinality = + container_get_cardinality(container, newtypecode); + + result = oldCardinality != newCardinality; + } + } else { + array_container_t *newac = array_container_create(); + container_t *container = container_add(newac, val & 0xFFFF, + ARRAY_CONTAINER_TYPE, &typecode); + // we could just assume that it stays an array container + ra_insert_new_key_value_at(&r->high_low_container, -i - 1, hb, + container, typecode); + result = true; + } + + return result; +} + +void roaring_bitmap_remove(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, (uint16_t)i); + container_t *container = + ra_get_container_at_index(&r->high_low_container, (uint16_t)i, &typecode); + uint8_t newtypecode = typecode; + container_t *container2 = + container_remove(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + if (container_get_cardinality(container2, newtypecode) != 0) { + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } else { + ra_remove_at_index_and_free(&r->high_low_container, i); + } + } +} + +bool roaring_bitmap_remove_checked(roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + const int i = ra_get_index(&r->high_low_container, hb); + uint8_t typecode; + bool result = false; + if (i >= 0) { + ra_unshare_container_at_index(&r->high_low_container, (uint16_t)i); + container_t *container = + ra_get_container_at_index(&r->high_low_container, (uint16_t)i, &typecode); + + const int oldCardinality = + container_get_cardinality(container, typecode); + + uint8_t newtypecode = typecode; + container_t *container2 = + container_remove(container, val & 0xFFFF, typecode, &newtypecode); + if (container2 != container) { + container_free(container, typecode); + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } + + const int newCardinality = + container_get_cardinality(container2, newtypecode); + + if (newCardinality != 0) { + ra_set_container_at_index(&r->high_low_container, i, container2, + newtypecode); + } else { + ra_remove_at_index_and_free(&r->high_low_container, i); + } + + result = oldCardinality != newCardinality; + } + return result; +} + +void roaring_bitmap_remove_many(roaring_bitmap_t *r, size_t n_args, + const uint32_t *vals) { + if (n_args == 0 || r->high_low_container.size == 0) { + return; + } + int32_t pos = -1; // position of the container used in the previous iteration + for (size_t i = 0; i < n_args; i++) { + uint16_t key = (uint16_t)(vals[i] >> 16); + if (pos < 0 || key != r->high_low_container.keys[pos]) { + pos = ra_get_index(&r->high_low_container, key); + } + if (pos >= 0) { + uint8_t new_typecode; + container_t *new_container; + new_container = container_remove(r->high_low_container.containers[pos], + vals[i] & 0xffff, + r->high_low_container.typecodes[pos], + &new_typecode); + if (new_container != r->high_low_container.containers[pos]) { + container_free(r->high_low_container.containers[pos], + r->high_low_container.typecodes[pos]); + ra_replace_key_and_container_at_index(&r->high_low_container, + pos, key, new_container, + new_typecode); + } + if (!container_nonzero_cardinality(new_container, new_typecode)) { + container_free(new_container, new_typecode); + ra_remove_at_index(&r->high_low_container, pos); + pos = -1; + } + } + } +} + +// there should be some SIMD optimizations possible here +roaring_bitmap_t *roaring_bitmap_and(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint32_t neededcap = length1 > length2 ? length2 : length1; + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(neededcap); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + if (s1 == s2) { + uint8_t type1, type2; + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_and(c1, type1, c2, type2, &result_type); + + if (container_nonzero_cardinality(c, result_type)) { + ra_append(&answer->high_low_container, s1, c, result_type); + } else { + container_free(c, result_type); // otherwise: memory leak! + } + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + return answer; +} + +/** + * Compute the union of 'number' bitmaps. + */ +roaring_bitmap_t *roaring_bitmap_or_many(size_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_bitmap_t *answer = + roaring_bitmap_lazy_or(x[0], x[1], LAZY_OR_BITSET_CONVERSION); + for (size_t i = 2; i < number; i++) { + roaring_bitmap_lazy_or_inplace(answer, x[i], LAZY_OR_BITSET_CONVERSION); + } + roaring_bitmap_repair_after_lazy(answer); + return answer; +} + +/** + * Compute the xor of 'number' bitmaps. + */ +roaring_bitmap_t *roaring_bitmap_xor_many(size_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_bitmap_t *answer = roaring_bitmap_lazy_xor(x[0], x[1]); + for (size_t i = 2; i < number; i++) { + roaring_bitmap_lazy_xor_inplace(answer, x[i]); + } + roaring_bitmap_repair_after_lazy(answer); + return answer; +} + +// inplace and (modifies its first argument). +void roaring_bitmap_and_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + if (x1 == x2) return; + int pos1 = 0, pos2 = 0, intersection_size = 0; + const int length1 = ra_get_size(&x1->high_low_container); + const int length2 = ra_get_size(&x2->high_low_container); + + // any skipped-over or newly emptied containers in x1 + // have to be freed. + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + if (s1 == s2) { + uint8_t type1, type2, result_type; + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + + // We do the computation "in place" only when c1 is not a shared container. + // Rationale: using a shared container safely with in place computation would + // require making a copy and then doing the computation in place which is likely + // less efficient than avoiding in place entirely and always generating a new + // container. + container_t *c = + (type1 == SHARED_CONTAINER_TYPE) + ? container_and(c1, type1, c2, type2, &result_type) + : container_iand(c1, type1, c2, type2, &result_type); + + if (c != c1) { // in this instance a new container was created, and + // we need to free the old one + container_free(c1, type1); + } + if (container_nonzero_cardinality(c, result_type)) { + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size, s1, c, + result_type); + intersection_size++; + } else { + container_free(c, result_type); + } + ++pos1; + ++pos2; + } else if (s1 < s2) { + pos1 = ra_advance_until_freeing(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + + // if we ended early because x2 ran out, then all remaining in x1 should be + // freed + while (pos1 < length1) { + container_free(x1->high_low_container.containers[pos1], + x1->high_low_container.typecodes[pos1]); + ++pos1; + } + + // all containers after this have either been copied or freed + ra_downsize(&x1->high_low_container, intersection_size); +} + +roaring_bitmap_t *roaring_bitmap_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_or(c1, type1, c2, type2, &result_type); + + // since we assume that the initial containers are non-empty, the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + // c1 = container_clone(c1, type1); + c1 = get_copy_of_container(c1, &type1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + type1); + } + ra_append(&answer->high_low_container, s1, c1, type1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + // c2 = container_clone(c2, type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_append(&answer->high_low_container, s2, c2, type2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace or (modifies its first argument). +void roaring_bitmap_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + if (!container_is_full(c1, type1)) { + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = + (type1 == SHARED_CONTAINER_TYPE) + ? container_or(c1, type1, c2, type2, &result_type) + : container_ior(c1, type1, c2, type2, &result_type); + + if (c != c1) { // in this instance a new container was created, + // and we need to free the old one + container_free(c1, type1); + } + ra_set_container_at_index(&x1->high_low_container, pos1, c, + result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index(&x2->high_low_container, + (uint16_t)pos2, &type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + + // container_t *c2_clone = container_clone(c2, type2); + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + type2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_xor(c1, type1, c2, type2, &result_type); + + if (container_nonzero_cardinality(c, result_type)) { + ra_append(&answer->high_low_container, s1, c, result_type); + } else { + container_free(c, result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + c1 = get_copy_of_container(c1, &type1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + type1); + } + ra_append(&answer->high_low_container, s1, c1, type1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_append(&answer->high_low_container, s2, c2, type2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace xor (modifies its first argument). + +void roaring_bitmap_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + uint8_t result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + + // XOR can have new containers inserted from x2, but can also + // lose containers when x1 and x2 are nonempty and identical. + + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + + // We do the computation "in place" only when c1 is not a shared container. + // Rationale: using a shared container safely with in place computation would + // require making a copy and then doing the computation in place which is likely + // less efficient than avoiding in place entirely and always generating a new + // container. + + container_t *c; + if (type1 == SHARED_CONTAINER_TYPE) { + c = container_xor(c1, type1, c2, type2, &result_type); + shared_container_free(CAST_shared(c1)); // so release + } + else { + c = container_ixor(c1, type1, c2, type2, &result_type); + } + + if (container_nonzero_cardinality(c, result_type)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c, + result_type); + ++pos1; + } else { + container_free(c, result_type); + ra_remove_at_index(&x1->high_low_container, pos1); + --length1; + } + + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + type2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_andnot(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + roaring_bitmap_t *empty_bitmap = roaring_bitmap_create(); + roaring_bitmap_set_copy_on_write(empty_bitmap, is_cow(x1) || is_cow(x2)); + return empty_bitmap; + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(length1); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = 0; + uint16_t s2 = 0; + while (true) { + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_andnot(c1, type1, c2, type2, + &result_type); + + if (container_nonzero_cardinality(c, result_type)) { + ra_append(&answer->high_low_container, s1, c, result_type); + } else { + container_free(c, result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + } else if (s1 < s2) { // s1 < s2 + const int next_pos1 = + ra_advance_until(&x1->high_low_container, s2, pos1); + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, next_pos1, + is_cow(x1)); + // TODO : perhaps some of the copy_on_write should be based on + // answer rather than x1 (more stringent?). Many similar cases + pos1 = next_pos1; + if (pos1 == length1) break; + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + if (pos2 == length2) break; + } + } + if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +// inplace andnot (modifies its first argument). + +void roaring_bitmap_andnot_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + + uint8_t result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + int intersection_size = 0; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_clear(x1); + return; + } + + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + + // We do the computation "in place" only when c1 is not a shared container. + // Rationale: using a shared container safely with in place computation would + // require making a copy and then doing the computation in place which is likely + // less efficient than avoiding in place entirely and always generating a new + // container. + + container_t *c; + if (type1 == SHARED_CONTAINER_TYPE) { + c = container_andnot(c1, type1, c2, type2, &result_type); + shared_container_free(CAST_shared(c1)); // release + } + else { + c = container_iandnot(c1, type1, c2, type2, &result_type); + } + + if (container_nonzero_cardinality(c, result_type)) { + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size++, s1, + c, result_type); + } else { + container_free(c, result_type); + } + + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + if (pos1 != intersection_size) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + + ra_replace_key_and_container_at_index(&x1->high_low_container, + intersection_size, s1, c1, + type1); + } + intersection_size++; + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + + if (pos1 < length1) { + // all containers between intersection_size and + // pos1 are junk. However, they have either been moved + // (thus still referenced) or involved in an iandnot + // that will clean up all containers that could not be reused. + // Thus we should not free the junk containers between + // intersection_size and pos1. + if (pos1 > intersection_size) { + // left slide of remaining items + ra_copy_range(&x1->high_low_container, pos1, length1, + intersection_size); + } + // else current placement is fine + intersection_size += (length1 - pos1); + } + ra_downsize(&x1->high_low_container, intersection_size); +} + +uint64_t roaring_bitmap_get_cardinality(const roaring_bitmap_t *r) { + const roaring_array_t *ra = &r->high_low_container; + + uint64_t card = 0; + for (int i = 0; i < ra->size; ++i) + card += container_get_cardinality(ra->containers[i], ra->typecodes[i]); + return card; +} + +uint64_t roaring_bitmap_range_cardinality(const roaring_bitmap_t *r, + uint64_t range_start, + uint64_t range_end) { + const roaring_array_t *ra = &r->high_low_container; + + if (range_end > UINT32_MAX) { + range_end = UINT32_MAX + UINT64_C(1); + } + if (range_start >= range_end) { + return 0; + } + range_end--; // make range_end inclusive + // now we have: 0 <= range_start <= range_end <= UINT32_MAX + + uint16_t minhb = (uint16_t)(range_start >> 16); + uint16_t maxhb = (uint16_t)(range_end >> 16); + + uint64_t card = 0; + + int i = ra_get_index(ra, minhb); + if (i >= 0) { + if (minhb == maxhb) { + card += container_rank(ra->containers[i], ra->typecodes[i], + range_end & 0xffff); + } else { + card += container_get_cardinality(ra->containers[i], + ra->typecodes[i]); + } + if ((range_start & 0xffff) != 0) { + card -= container_rank(ra->containers[i], ra->typecodes[i], + (range_start & 0xffff) - 1); + } + i++; + } else { + i = -i - 1; + } + + for (; i < ra->size; i++) { + uint16_t key = ra->keys[i]; + if (key < maxhb) { + card += container_get_cardinality(ra->containers[i], + ra->typecodes[i]); + } else if (key == maxhb) { + card += container_rank(ra->containers[i], ra->typecodes[i], + range_end & 0xffff); + break; + } else { + break; + } + } + + return card; +} + + +bool roaring_bitmap_is_empty(const roaring_bitmap_t *r) { + return r->high_low_container.size == 0; +} + +void roaring_bitmap_to_uint32_array(const roaring_bitmap_t *r, uint32_t *ans) { + ra_to_uint32_array(&r->high_low_container, ans); +} + +bool roaring_bitmap_range_uint32_array(const roaring_bitmap_t *r, + size_t offset, size_t limit, + uint32_t *ans) { + return ra_range_uint32_array(&r->high_low_container, offset, limit, ans); +} + +/** convert array and bitmap containers to run containers when it is more + * efficient; + * also convert from run containers when more space efficient. Returns + * true if the result has at least one run container. +*/ +bool roaring_bitmap_run_optimize(roaring_bitmap_t *r) { + bool answer = false; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t type_original, type_after; + ra_unshare_container_at_index( + &r->high_low_container, (uint16_t)i); // TODO: this introduces extra cloning! + container_t *c = ra_get_container_at_index(&r->high_low_container, (uint16_t)i, + &type_original); + container_t *c1 = convert_run_optimize(c, type_original, &type_after); + if (type_after == RUN_CONTAINER_TYPE) { + answer = true; + } + ra_set_container_at_index(&r->high_low_container, i, c1, type_after); + } + return answer; +} + +size_t roaring_bitmap_shrink_to_fit(roaring_bitmap_t *r) { + size_t answer = 0; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t type_original; + container_t *c = ra_get_container_at_index(&r->high_low_container, (uint16_t)i, + &type_original); + answer += container_shrink_to_fit(c, type_original); + } + answer += ra_shrink_to_fit(&r->high_low_container); + return answer; +} + +/** + * Remove run-length encoding even when it is more space efficient + * return whether a change was applied + */ +bool roaring_bitmap_remove_run_compression(roaring_bitmap_t *r) { + bool answer = false; + for (int i = 0; i < r->high_low_container.size; i++) { + uint8_t type_original, type_after; + container_t *c = ra_get_container_at_index(&r->high_low_container, (uint16_t)i, + &type_original); + if (get_container_type(c, type_original) == RUN_CONTAINER_TYPE) { + answer = true; + if (type_original == SHARED_CONTAINER_TYPE) { + run_container_t *truec = CAST_run(CAST_shared(c)->container); + int32_t card = run_container_cardinality(truec); + container_t *c1 = convert_to_bitset_or_array_container( + truec, card, &type_after); + shared_container_free(CAST_shared(c)); // frees run as needed + ra_set_container_at_index(&r->high_low_container, i, c1, + type_after); + + } else { + int32_t card = run_container_cardinality(CAST_run(c)); + container_t *c1 = convert_to_bitset_or_array_container( + CAST_run(c), card, &type_after); + run_container_free(CAST_run(c)); + ra_set_container_at_index(&r->high_low_container, i, c1, + type_after); + } + } + } + return answer; +} + +size_t roaring_bitmap_serialize(const roaring_bitmap_t *r, char *buf) { + size_t portablesize = roaring_bitmap_portable_size_in_bytes(r); + uint64_t cardinality = roaring_bitmap_get_cardinality(r); + uint64_t sizeasarray = cardinality * sizeof(uint32_t) + sizeof(uint32_t); + if (portablesize < sizeasarray) { + buf[0] = CROARING_SERIALIZATION_CONTAINER; + return roaring_bitmap_portable_serialize(r, buf + 1) + 1; + } else { + buf[0] = CROARING_SERIALIZATION_ARRAY_UINT32; + memcpy(buf + 1, &cardinality, sizeof(uint32_t)); + roaring_bitmap_to_uint32_array( + r, (uint32_t *)(buf + 1 + sizeof(uint32_t))); + return 1 + (size_t)sizeasarray; + } +} + +size_t roaring_bitmap_size_in_bytes(const roaring_bitmap_t *r) { + size_t portablesize = roaring_bitmap_portable_size_in_bytes(r); + uint64_t sizeasarray = roaring_bitmap_get_cardinality(r) * sizeof(uint32_t) + + sizeof(uint32_t); + return portablesize < sizeasarray ? portablesize + 1 : (size_t)sizeasarray + 1; +} + +size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *r) { + return ra_portable_size_in_bytes(&r->high_low_container); +} + + +roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes) { + roaring_bitmap_t *ans = + (roaring_bitmap_t *)roaring_malloc(sizeof(roaring_bitmap_t)); + if (ans == NULL) { + return NULL; + } + size_t bytesread; + bool is_ok = ra_portable_deserialize(&ans->high_low_container, buf, maxbytes, &bytesread); + if (!is_ok) { + roaring_free(ans); + return NULL; + } + roaring_bitmap_set_copy_on_write(ans, false); + if (!is_ok) { + roaring_free(ans); + return NULL; + } + return ans; +} + +roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf) { + return roaring_bitmap_portable_deserialize_safe(buf, SIZE_MAX); +} + + +size_t roaring_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes) { + return ra_portable_deserialize_size(buf, maxbytes); +} + + +size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *r, + char *buf) { + return ra_portable_serialize(&r->high_low_container, buf); +} + +roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) { + const char *bufaschar = (const char *)buf; + if (bufaschar[0] == CROARING_SERIALIZATION_ARRAY_UINT32) { + /* This looks like a compressed set of uint32_t elements */ + uint32_t card; + + memcpy(&card, bufaschar + 1, sizeof(uint32_t)); + + const uint32_t *elems = + (const uint32_t *)(bufaschar + 1 + sizeof(uint32_t)); + + roaring_bitmap_t *bitmap = roaring_bitmap_create(); + if (bitmap == NULL) { + return NULL; + } + roaring_bulk_context_t context = {0}; + for (uint32_t i = 0; i < card; i++) { + // elems may not be aligned, read with memcpy + uint32_t elem; + memcpy(&elem, elems + i, sizeof(elem)); + roaring_bitmap_add_bulk(bitmap, &context, elem); + } + return bitmap; + + } else if (bufaschar[0] == CROARING_SERIALIZATION_CONTAINER) { + return roaring_bitmap_portable_deserialize(bufaschar + 1); + } else + return (NULL); +} + +roaring_bitmap_t* roaring_bitmap_deserialize_safe(const void *buf, size_t maxbytes) { + if (maxbytes < 1) { + return NULL; + } + + const char *bufaschar = (const char *)buf; + if (bufaschar[0] == CROARING_SERIALIZATION_ARRAY_UINT32) { + if (maxbytes < 1 + sizeof(uint32_t)) { + return NULL; + } + + /* This looks like a compressed set of uint32_t elements */ + uint32_t card; + memcpy(&card, bufaschar + 1, sizeof(uint32_t)); + + // Check the buffer is big enough to contain card uint32_t elements + if (maxbytes < 1 + sizeof(uint32_t) + card * sizeof(uint32_t)) { + return NULL; + } + + const uint32_t *elems = + (const uint32_t *)(bufaschar + 1 + sizeof(uint32_t)); + + roaring_bitmap_t *bitmap = roaring_bitmap_create(); + if (bitmap == NULL) { + return NULL; + } + roaring_bulk_context_t context = {0}; + for (uint32_t i = 0; i < card; i++) { + // elems may not be aligned, read with memcpy + uint32_t elem; + memcpy(&elem, elems + i, sizeof(elem)); + roaring_bitmap_add_bulk(bitmap, &context, elem); + } + return bitmap; + + } else if (bufaschar[0] == CROARING_SERIALIZATION_CONTAINER) { + return roaring_bitmap_portable_deserialize_safe(bufaschar + 1, maxbytes - 1); + } else + return (NULL); +} + +bool roaring_iterate(const roaring_bitmap_t *r, roaring_iterator iterator, + void *ptr) { + const roaring_array_t *ra = &r->high_low_container; + + for (int i = 0; i < ra->size; ++i) + if (!container_iterate(ra->containers[i], ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16, + iterator, ptr)) { + return false; + } + return true; +} + +bool roaring_iterate64(const roaring_bitmap_t *r, roaring_iterator64 iterator, + uint64_t high_bits, void *ptr) { + const roaring_array_t *ra = &r->high_low_container; + + for (int i = 0; i < ra->size; ++i) + if (!container_iterate64( + ra->containers[i], ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16, iterator, + high_bits, ptr)) { + return false; + } + return true; +} + +/**** +* begin roaring_uint32_iterator_t +*****/ + +// Partially initializes the roaring iterator when it begins looking at +// a new container. +static bool iter_new_container_partial_init(roaring_uint32_iterator_t *newit) { + newit->in_container_index = 0; + newit->run_index = 0; + newit->current_value = 0; + if (newit->container_index >= newit->parent->high_low_container.size || + newit->container_index < 0) { + newit->current_value = UINT32_MAX; + return (newit->has_value = false); + } + // assume not empty + newit->has_value = true; + // we precompute container, typecode and highbits so that successive + // iterators do not have to grab them from odd memory locations + // and have to worry about the (easily predicted) container_unwrap_shared + // call. + newit->container = + newit->parent->high_low_container.containers[newit->container_index]; + newit->typecode = + newit->parent->high_low_container.typecodes[newit->container_index]; + newit->highbits = + ((uint32_t) + newit->parent->high_low_container.keys[newit->container_index]) + << 16; + newit->container = + container_unwrap_shared(newit->container, &(newit->typecode)); + return newit->has_value; +} + +static bool loadfirstvalue(roaring_uint32_iterator_t *newit) { + if (!iter_new_container_partial_init(newit)) + return newit->has_value; + + switch (newit->typecode) { + case BITSET_CONTAINER_TYPE: { + const bitset_container_t *bc = const_CAST_bitset(newit->container); + + uint32_t wordindex = 0; + uint64_t word; + while ((word = bc->words[wordindex]) == 0) { + wordindex++; // advance + } + // here "word" is non-zero + newit->in_container_index = wordindex * 64 + roaring_trailing_zeroes(word); + newit->current_value = newit->highbits | newit->in_container_index; + break; } + + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = const_CAST_array(newit->container); + newit->current_value = newit->highbits | ac->array[0]; + break; } + + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(newit->container); + newit->current_value = newit->highbits | rc->runs[0].value; + break; } + + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + return true; +} + +static bool loadlastvalue(roaring_uint32_iterator_t* newit) { + if (!iter_new_container_partial_init(newit)) + return newit->has_value; + + switch(newit->typecode) { + case BITSET_CONTAINER_TYPE: { + uint32_t wordindex = BITSET_CONTAINER_SIZE_IN_WORDS - 1; + uint64_t word; + const bitset_container_t* bitset_container = (const bitset_container_t*)newit->container; + while ((word = bitset_container->words[wordindex]) == 0) + --wordindex; + + int num_leading_zeros = roaring_leading_zeroes(word); + newit->in_container_index = (wordindex * 64) + (63 - num_leading_zeros); + newit->current_value = newit->highbits | newit->in_container_index; + break; + } + case ARRAY_CONTAINER_TYPE: { + const array_container_t* array_container = (const array_container_t*)newit->container; + newit->in_container_index = array_container->cardinality - 1; + newit->current_value = newit->highbits | array_container->array[newit->in_container_index]; + break; + } + case RUN_CONTAINER_TYPE: { + const run_container_t* run_container = (const run_container_t*)newit->container; + newit->run_index = run_container->n_runs - 1; + const rle16_t* last_run = &run_container->runs[newit->run_index]; + newit->current_value = newit->highbits | (last_run->value + last_run->length); + break; + } + default: + // if this ever happens, bug! + assert(false); + } + return true; +} + +// prerequesite: the value should be in range of the container +static bool loadfirstvalue_largeorequal(roaring_uint32_iterator_t *newit, uint32_t val) { + // Don't have to check return value because of prerequisite + iter_new_container_partial_init(newit); + uint16_t lb = val & 0xFFFF; + + switch (newit->typecode) { + case BITSET_CONTAINER_TYPE: { + const bitset_container_t *bc = const_CAST_bitset(newit->container); + newit->in_container_index = + bitset_container_index_equalorlarger(bc, lb); + newit->current_value = newit->highbits | newit->in_container_index; + break; } + + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = const_CAST_array(newit->container); + newit->in_container_index = + array_container_index_equalorlarger(ac, lb); + newit->current_value = + newit->highbits | ac->array[newit->in_container_index]; + break; } + + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(newit->container); + newit->run_index = run_container_index_equalorlarger(rc, lb); + if (rc->runs[newit->run_index].value <= lb) { + newit->current_value = val; + } else { + newit->current_value = + newit->highbits | rc->runs[newit->run_index].value; + } + break; } + + default: + roaring_unreachable; + } + + return true; +} + +void roaring_init_iterator(const roaring_bitmap_t *r, + roaring_uint32_iterator_t *newit) { + newit->parent = r; + newit->container_index = 0; + newit->has_value = loadfirstvalue(newit); +} + +void roaring_init_iterator_last(const roaring_bitmap_t *r, + roaring_uint32_iterator_t *newit) { + newit->parent = r; + newit->container_index = newit->parent->high_low_container.size - 1; + newit->has_value = loadlastvalue(newit); +} + +roaring_uint32_iterator_t *roaring_create_iterator(const roaring_bitmap_t *r) { + roaring_uint32_iterator_t *newit = + (roaring_uint32_iterator_t *)roaring_malloc(sizeof(roaring_uint32_iterator_t)); + if (newit == NULL) return NULL; + roaring_init_iterator(r, newit); + return newit; +} + +roaring_uint32_iterator_t *roaring_copy_uint32_iterator( + const roaring_uint32_iterator_t *it) { + roaring_uint32_iterator_t *newit = + (roaring_uint32_iterator_t *)roaring_malloc(sizeof(roaring_uint32_iterator_t)); + memcpy(newit, it, sizeof(roaring_uint32_iterator_t)); + return newit; +} + +bool roaring_move_uint32_iterator_equalorlarger(roaring_uint32_iterator_t *it, uint32_t val) { + uint16_t hb = val >> 16; + const int i = ra_get_index(& it->parent->high_low_container, hb); + if (i >= 0) { + uint32_t lowvalue = container_maximum(it->parent->high_low_container.containers[i], it->parent->high_low_container.typecodes[i]); + uint16_t lb = val & 0xFFFF; + if(lowvalue < lb ) { + it->container_index = i+1; // will have to load first value of next container + } else {// the value is necessarily within the range of the container + it->container_index = i; + it->has_value = loadfirstvalue_largeorequal(it, val); + return it->has_value; + } + } else { + // there is no matching, so we are going for the next container + it->container_index = -i-1; + } + it->has_value = loadfirstvalue(it); + return it->has_value; +} + + +bool roaring_advance_uint32_iterator(roaring_uint32_iterator_t *it) { + if (it->container_index >= it->parent->high_low_container.size) { + return (it->has_value = false); + } + if (it->container_index < 0) { + it->container_index = 0; + return (it->has_value = loadfirstvalue(it)); + } + + switch (it->typecode) { + case BITSET_CONTAINER_TYPE: { + const bitset_container_t *bc = const_CAST_bitset(it->container); + it->in_container_index++; + + uint32_t wordindex = it->in_container_index / 64; + if (wordindex >= BITSET_CONTAINER_SIZE_IN_WORDS) break; + + uint64_t word = bc->words[wordindex] & + (UINT64_MAX << (it->in_container_index % 64)); + // next part could be optimized/simplified + while ((word == 0) && + (wordindex + 1 < BITSET_CONTAINER_SIZE_IN_WORDS)) { + wordindex++; + word = bc->words[wordindex]; + } + if (word != 0) { + it->in_container_index = wordindex * 64 + roaring_trailing_zeroes(word); + it->current_value = it->highbits | it->in_container_index; + return (it->has_value = true); + } + break; } + + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = const_CAST_array(it->container); + it->in_container_index++; + if (it->in_container_index < ac->cardinality) { + it->current_value = + it->highbits | ac->array[it->in_container_index]; + return (it->has_value = true); + } + break; } + + case RUN_CONTAINER_TYPE: { + if(it->current_value == UINT32_MAX) { // avoid overflow to zero + return (it->has_value = false); + } + + const run_container_t* rc = const_CAST_run(it->container); + uint32_t limit = (it->highbits | (rc->runs[it->run_index].value + + rc->runs[it->run_index].length)); + if (++it->current_value <= limit) { + return (it->has_value = true); + } + + if (++it->run_index < rc->n_runs) { // Assume the run has a value + it->current_value = + it->highbits | rc->runs[it->run_index].value; + return (it->has_value = true); + } + break; + } + + default: + roaring_unreachable; + } + + // moving to next container + it->container_index++; + return (it->has_value = loadfirstvalue(it)); +} + +bool roaring_previous_uint32_iterator(roaring_uint32_iterator_t *it) { + if (it->container_index < 0) { + return (it->has_value = false); + } + if (it->container_index >= it->parent->high_low_container.size) { + it->container_index = it->parent->high_low_container.size - 1; + return (it->has_value = loadlastvalue(it)); + } + + switch (it->typecode) { + case BITSET_CONTAINER_TYPE: { + if (--it->in_container_index < 0) + break; + + const bitset_container_t* bitset_container = (const bitset_container_t*)it->container; + int32_t wordindex = it->in_container_index / 64; + uint64_t word = bitset_container->words[wordindex] & (UINT64_MAX >> (63 - (it->in_container_index % 64))); + + while (word == 0 && --wordindex >= 0) { + word = bitset_container->words[wordindex]; + } + if (word == 0) + break; + + int num_leading_zeros = roaring_leading_zeroes(word); + it->in_container_index = (wordindex * 64) + (63 - num_leading_zeros); + it->current_value = it->highbits | it->in_container_index; + return (it->has_value = true); + } + case ARRAY_CONTAINER_TYPE: { + if (--it->in_container_index < 0) + break; + + const array_container_t* array_container = (const array_container_t*)it->container; + it->current_value = it->highbits | array_container->array[it->in_container_index]; + return (it->has_value = true); + } + case RUN_CONTAINER_TYPE: { + if(it->current_value == 0) + return (it->has_value = false); + + const run_container_t* run_container = (const run_container_t*)it->container; + if (--it->current_value >= (it->highbits | run_container->runs[it->run_index].value)) { + return (it->has_value = true); + } + + if (--it->run_index < 0) + break; + + it->current_value = it->highbits | (run_container->runs[it->run_index].value + + run_container->runs[it->run_index].length); + return (it->has_value = true); + } + default: + // if this ever happens, bug! + assert(false); + } // switch (typecode) + + // moving to previous container + it->container_index--; + return (it->has_value = loadlastvalue(it)); +} + +uint32_t roaring_read_uint32_iterator(roaring_uint32_iterator_t *it, uint32_t* buf, uint32_t count) { + uint32_t ret = 0; + uint32_t num_values; + uint32_t wordindex; // used for bitsets + uint64_t word; // used for bitsets + const array_container_t* acont; //TODO remove + const run_container_t* rcont; //TODO remove + const bitset_container_t* bcont; //TODO remove + + while (it->has_value && ret < count) { + switch (it->typecode) { + case BITSET_CONTAINER_TYPE: + bcont = const_CAST_bitset(it->container); + wordindex = it->in_container_index / 64; + word = bcont->words[wordindex] & (UINT64_MAX << (it->in_container_index % 64)); + do { + while (word != 0 && ret < count) { + buf[0] = it->highbits | (wordindex * 64 + roaring_trailing_zeroes(word)); + word = word & (word - 1); + buf++; + ret++; + } + while (word == 0 && wordindex+1 < BITSET_CONTAINER_SIZE_IN_WORDS) { + wordindex++; + word = bcont->words[wordindex]; + } + } while (word != 0 && ret < count); + it->has_value = (word != 0); + if (it->has_value) { + it->in_container_index = wordindex * 64 + roaring_trailing_zeroes(word); + it->current_value = it->highbits | it->in_container_index; + } + break; + case ARRAY_CONTAINER_TYPE: + acont = const_CAST_array(it->container); + num_values = minimum_uint32(acont->cardinality - it->in_container_index, count - ret); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = it->highbits | acont->array[it->in_container_index + i]; + } + buf += num_values; + ret += num_values; + it->in_container_index += num_values; + it->has_value = (it->in_container_index < acont->cardinality); + if (it->has_value) { + it->current_value = it->highbits | acont->array[it->in_container_index]; + } + break; + case RUN_CONTAINER_TYPE: + rcont = const_CAST_run(it->container); + //"in_run_index" name is misleading, read it as "max_value_in_current_run" + do { + uint32_t largest_run_value = it->highbits | (rcont->runs[it->run_index].value + rcont->runs[it->run_index].length); + num_values = minimum_uint32(largest_run_value - it->current_value + 1, count - ret); + for (uint32_t i = 0; i < num_values; i++) { + buf[i] = it->current_value + i; + } + it->current_value += num_values; // this can overflow to zero: UINT32_MAX+1=0 + buf += num_values; + ret += num_values; + + if (it->current_value > largest_run_value || it->current_value == 0) { + it->run_index++; + if (it->run_index < rcont->n_runs) { + it->current_value = it->highbits | rcont->runs[it->run_index].value; + } else { + it->has_value = false; + } + } + } while ((ret < count) && it->has_value); + break; + default: + assert(false); + } + if (it->has_value) { + assert(ret == count); + return ret; + } + it->container_index++; + it->has_value = loadfirstvalue(it); + } + return ret; +} + + + +void roaring_free_uint32_iterator(roaring_uint32_iterator_t *it) { roaring_free(it); } + +/**** +* end of roaring_uint32_iterator_t +*****/ + +bool roaring_bitmap_equals(const roaring_bitmap_t *r1, + const roaring_bitmap_t *r2) { + const roaring_array_t *ra1 = &r1->high_low_container; + const roaring_array_t *ra2 = &r2->high_low_container; + + if (ra1->size != ra2->size) { + return false; + } + for (int i = 0; i < ra1->size; ++i) { + if (ra1->keys[i] != ra2->keys[i]) { + return false; + } + } + for (int i = 0; i < ra1->size; ++i) { + bool areequal = container_equals(ra1->containers[i], + ra1->typecodes[i], + ra2->containers[i], + ra2->typecodes[i]); + if (!areequal) { + return false; + } + } + return true; +} + +bool roaring_bitmap_is_subset(const roaring_bitmap_t *r1, + const roaring_bitmap_t *r2) { + const roaring_array_t *ra1 = &r1->high_low_container; + const roaring_array_t *ra2 = &r2->high_low_container; + + const int length1 = ra1->size, + length2 = ra2->size; + + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(ra1, (uint16_t)pos1); + const uint16_t s2 = ra_get_key_at_index(ra2, (uint16_t)pos2); + + if (s1 == s2) { + uint8_t type1, type2; + container_t *c1 = ra_get_container_at_index(ra1, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index(ra2, (uint16_t)pos2, &type2); + if (!container_is_subset(c1, type1, c2, type2)) + return false; + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + return false; + } else { // s1 > s2 + pos2 = ra_advance_until(ra2, s1, pos2); + } + } + if (pos1 == length1) + return true; + else + return false; +} + +static void insert_flipped_container(roaring_array_t *ans_arr, + const roaring_array_t *x1_arr, uint16_t hb, + uint16_t lb_start, uint16_t lb_end) { + const int i = ra_get_index(x1_arr, hb); + const int j = ra_get_index(ans_arr, hb); + uint8_t ctype_in, ctype_out; + container_t *flipped_container = NULL; + if (i >= 0) { + container_t *container_to_flip = + ra_get_container_at_index(x1_arr, (uint16_t)i, &ctype_in); + flipped_container = + container_not_range(container_to_flip, ctype_in, (uint32_t)lb_start, + (uint32_t)(lb_end + 1), &ctype_out); + + if (container_get_cardinality(flipped_container, ctype_out)) + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + else { + container_free(flipped_container, ctype_out); + } + } else { + flipped_container = container_range_of_ones( + (uint32_t)lb_start, (uint32_t)(lb_end + 1), &ctype_out); + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + } +} + +static void inplace_flip_container(roaring_array_t *x1_arr, uint16_t hb, + uint16_t lb_start, uint16_t lb_end) { + const int i = ra_get_index(x1_arr, hb); + uint8_t ctype_in, ctype_out; + container_t *flipped_container = NULL; + if (i >= 0) { + container_t *container_to_flip = + ra_get_container_at_index(x1_arr, (uint16_t)i, &ctype_in); + flipped_container = container_inot_range( + container_to_flip, ctype_in, (uint32_t)lb_start, + (uint32_t)(lb_end + 1), &ctype_out); + // if a new container was created, the old one was already freed + if (container_get_cardinality(flipped_container, ctype_out)) { + ra_set_container_at_index(x1_arr, i, flipped_container, ctype_out); + } else { + container_free(flipped_container, ctype_out); + ra_remove_at_index(x1_arr, i); + } + + } else { + flipped_container = container_range_of_ones( + (uint32_t)lb_start, (uint32_t)(lb_end + 1), &ctype_out); + ra_insert_new_key_value_at(x1_arr, -i - 1, hb, flipped_container, + ctype_out); + } +} + +static void insert_fully_flipped_container(roaring_array_t *ans_arr, + const roaring_array_t *x1_arr, + uint16_t hb) { + const int i = ra_get_index(x1_arr, hb); + const int j = ra_get_index(ans_arr, hb); + uint8_t ctype_in, ctype_out; + container_t *flipped_container = NULL; + if (i >= 0) { + container_t *container_to_flip = + ra_get_container_at_index(x1_arr, (uint16_t)i, &ctype_in); + flipped_container = + container_not(container_to_flip, ctype_in, &ctype_out); + if (container_get_cardinality(flipped_container, ctype_out)) + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + else { + container_free(flipped_container, ctype_out); + } + } else { + flipped_container = container_range_of_ones(0U, 0x10000U, &ctype_out); + ra_insert_new_key_value_at(ans_arr, -j - 1, hb, flipped_container, + ctype_out); + } +} + +static void inplace_fully_flip_container(roaring_array_t *x1_arr, uint16_t hb) { + const int i = ra_get_index(x1_arr, hb); + uint8_t ctype_in, ctype_out; + container_t *flipped_container = NULL; + if (i >= 0) { + container_t *container_to_flip = + ra_get_container_at_index(x1_arr, (uint16_t)i, &ctype_in); + flipped_container = + container_inot(container_to_flip, ctype_in, &ctype_out); + + if (container_get_cardinality(flipped_container, ctype_out)) { + ra_set_container_at_index(x1_arr, i, flipped_container, ctype_out); + } else { + container_free(flipped_container, ctype_out); + ra_remove_at_index(x1_arr, i); + } + + } else { + flipped_container = container_range_of_ones(0U, 0x10000U, &ctype_out); + ra_insert_new_key_value_at(x1_arr, -i - 1, hb, flipped_container, + ctype_out); + } +} + +roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *x1, + uint64_t range_start, + uint64_t range_end) { + if (range_start >= range_end) { + return roaring_bitmap_copy(x1); + } + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + + roaring_bitmap_t *ans = roaring_bitmap_create(); + roaring_bitmap_set_copy_on_write(ans, is_cow(x1)); + + uint16_t hb_start = (uint16_t)(range_start >> 16); + const uint16_t lb_start = (uint16_t)range_start; // & 0xFFFF; + uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); + const uint16_t lb_end = (uint16_t)(range_end - 1); // & 0xFFFF; + + ra_append_copies_until(&ans->high_low_container, &x1->high_low_container, + hb_start, is_cow(x1)); + if (hb_start == hb_end) { + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_start, lb_start, + lb_end); + } else { + // start and end containers are distinct + if (lb_start > 0) { + // handle first (partial) container + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_start, + lb_start, 0xFFFF); + ++hb_start; // for the full containers. Can't wrap. + } + + if (lb_end != 0xFFFF) --hb_end; // later we'll handle the partial block + + for (uint32_t hb = hb_start; hb <= hb_end; ++hb) { + insert_fully_flipped_container(&ans->high_low_container, + &x1->high_low_container, (uint16_t)hb); + } + + // handle a partial final container + if (lb_end != 0xFFFF) { + insert_flipped_container(&ans->high_low_container, + &x1->high_low_container, hb_end + 1, 0, + lb_end); + ++hb_end; + } + } + ra_append_copies_after(&ans->high_low_container, &x1->high_low_container, + hb_end, is_cow(x1)); + return ans; +} + +void roaring_bitmap_flip_inplace(roaring_bitmap_t *x1, uint64_t range_start, + uint64_t range_end) { + if (range_start >= range_end) { + return; // empty range + } + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + + uint16_t hb_start = (uint16_t)(range_start >> 16); + const uint16_t lb_start = (uint16_t)range_start; + uint16_t hb_end = (uint16_t)((range_end - 1) >> 16); + const uint16_t lb_end = (uint16_t)(range_end - 1); + + if (hb_start == hb_end) { + inplace_flip_container(&x1->high_low_container, hb_start, lb_start, + lb_end); + } else { + // start and end containers are distinct + if (lb_start > 0) { + // handle first (partial) container + inplace_flip_container(&x1->high_low_container, hb_start, lb_start, + 0xFFFF); + ++hb_start; // for the full containers. Can't wrap. + } + + if (lb_end != 0xFFFF) --hb_end; + + for (uint32_t hb = hb_start; hb <= hb_end; ++hb) { + inplace_fully_flip_container(&x1->high_low_container, (uint16_t)hb); + } + // handle a partial final container + if (lb_end != 0xFFFF) { + inplace_flip_container(&x1->high_low_container, hb_end + 1, 0, + lb_end); + ++hb_end; + } + } +} + +static void offset_append_with_merge(roaring_array_t *ra, int k, container_t *c, uint8_t t) { + int size = ra_get_size(ra); + if (size == 0 || ra_get_key_at_index(ra, (uint16_t)(size-1)) != k) { + // No merge. + ra_append(ra, (uint16_t)k, c, t); + return; + } + + uint8_t last_t, new_t; + container_t *last_c, *new_c; + + // NOTE: we don't need to unwrap here, since we added last_c ourselves + // we have the certainty it's not a shared container. + // The same applies to c, as it's the result of calling container_offset. + last_c = ra_get_container_at_index(ra, (uint16_t)(size-1), &last_t); + new_c = container_ior(last_c, last_t, c, t, &new_t); + + ra_set_container_at_index(ra, size-1, new_c, new_t); + + // Comparison of pointers of different origin is UB (or so claim some compiler + // makers), so we compare their bit representation only. + if ((uintptr_t)last_c != (uintptr_t)new_c) { + container_free(last_c, last_t); + } + container_free(c, t); +} + +// roaring_bitmap_add_offset adds the value 'offset' to each and every value in +// a bitmap, generating a new bitmap in the process. If offset + element is +// outside of the range [0,2^32), that the element will be dropped. +// We need "offset" to be 64 bits because we want to support values +// between -0xFFFFFFFF up to +0xFFFFFFFF. +roaring_bitmap_t *roaring_bitmap_add_offset(const roaring_bitmap_t *bm, + int64_t offset) { + roaring_bitmap_t *answer; + roaring_array_t *ans_ra; + int64_t container_offset; + uint16_t in_offset; + + const roaring_array_t *bm_ra = &bm->high_low_container; + int length = bm_ra->size; + + if (offset == 0) { + return roaring_bitmap_copy(bm); + } + + container_offset = offset >> 16; + in_offset = (uint16_t)(offset - container_offset * (1 << 16)); + + answer = roaring_bitmap_create(); + roaring_bitmap_set_copy_on_write(answer, is_cow(bm)); + + ans_ra = &answer->high_low_container; + + if (in_offset == 0) { + ans_ra = &answer->high_low_container; + + for (int i = 0, j = 0; i < length; ++i) { + int64_t key = ra_get_key_at_index(bm_ra, (uint16_t)i); + key += container_offset; + + if (key < 0 || key >= (1 << 16)) { + continue; + } + + ra_append_copy(ans_ra, bm_ra, (uint16_t)i, false); + ans_ra->keys[j++] = (uint16_t)key; + } + + return answer; + } + + uint8_t t; + const container_t *c; + container_t *lo, *hi, **lo_ptr, **hi_ptr; + int64_t k; + + for (int i = 0; i < length; ++i) { + lo = hi = NULL; + lo_ptr = hi_ptr = NULL; + + k = ra_get_key_at_index(bm_ra, (uint16_t)i)+container_offset; + if (k >= 0 && k < (1 << 16)) { + lo_ptr = &lo; + } + if (k+1 >= 0 && k+1 < (1 << 16)) { + hi_ptr = &hi; + } + if (lo_ptr == NULL && hi_ptr == NULL) { + continue; + } + + c = ra_get_container_at_index(bm_ra, (uint16_t)i, &t); + c = container_unwrap_shared(c, &t); + + container_add_offset(c, t, lo_ptr, hi_ptr, in_offset); + if (lo != NULL) { + offset_append_with_merge(ans_ra, (int)k, lo, t); + } + if (hi != NULL) { + ra_append(ans_ra, (uint16_t)(k+1), hi, t); + } + } + + return answer; +} + +roaring_bitmap_t *roaring_bitmap_lazy_or(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c; + if (bitsetconversion && + (get_container_type(c1, type1) != BITSET_CONTAINER_TYPE) && + (get_container_type(c2, type2) != BITSET_CONTAINER_TYPE) + ){ + container_t *newc1 = + container_mutable_unwrap_shared(c1, &type1); + newc1 = container_to_bitset(newc1, type1); + type1 = BITSET_CONTAINER_TYPE; + c = container_lazy_ior(newc1, type1, c2, type2, + &result_type); + if (c != newc1) { // should not happen + container_free(newc1, type1); + } + } else { + c = container_lazy_or(c1, type1, c2, type2, &result_type); + } + // since we assume that the initial containers are non-empty, + // the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + c1 = get_copy_of_container(c1, &type1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + type1); + } + ra_append(&answer->high_low_container, s1, c1, type1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_append(&answer->high_low_container, s2, c2, type2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +void roaring_bitmap_lazy_or_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2, + const bool bitsetconversion) { + uint8_t result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + if (!container_is_full(c1, type1)) { + if ((bitsetconversion == false) || + (get_container_type(c1, type1) == BITSET_CONTAINER_TYPE) + ){ + c1 = get_writable_copy_if_shared(c1, &type1); + } else { + // convert to bitset + container_t *old_c1 = c1; + uint8_t old_type1 = type1; + c1 = container_mutable_unwrap_shared(c1, &type1); + c1 = container_to_bitset(c1, type1); + container_free(old_c1, old_type1); + type1 = BITSET_CONTAINER_TYPE; + } + + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_lazy_ior(c1, type1, c2, type2, + &result_type); + + if (c != c1) { // in this instance a new container was created, + // and we need to free the old one + container_free(c1, type1); + } + + ra_set_container_at_index(&x1->high_low_container, pos1, c, + result_type); + } + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + // container_t *c2_clone = container_clone(c2, type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + type2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +roaring_bitmap_t *roaring_bitmap_lazy_xor(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + if (0 == length1) { + return roaring_bitmap_copy(x2); + } + if (0 == length2) { + return roaring_bitmap_copy(x1); + } + roaring_bitmap_t *answer = + roaring_bitmap_create_with_capacity(length1 + length2); + roaring_bitmap_set_copy_on_write(answer, is_cow(x1) || is_cow(x2)); + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + container_t *c = container_lazy_xor( + c1, type1, c2, type2, &result_type); + + if (container_nonzero_cardinality(c, result_type)) { + ra_append(&answer->high_low_container, s1, c, result_type); + } else { + container_free(c, result_type); + } + + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + c1 = get_copy_of_container(c1, &type1, is_cow(x1)); + if (is_cow(x1)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c1, + type1); + } + ra_append(&answer->high_low_container, s1, c1, type1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_append(&answer->high_low_container, s2, c2, type2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2, + is_cow(x2)); + } else if (pos2 == length2) { + ra_append_copy_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1, + is_cow(x1)); + } + return answer; +} + +void roaring_bitmap_lazy_xor_inplace(roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + assert(x1 != x2); + uint8_t result_type = 0; + int length1 = x1->high_low_container.size; + const int length2 = x2->high_low_container.size; + + if (0 == length2) return; + + if (0 == length1) { + roaring_bitmap_overwrite(x1, x2); + return; + } + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + + // We do the computation "in place" only when c1 is not a shared container. + // Rationale: using a shared container safely with in place computation would + // require making a copy and then doing the computation in place which is likely + // less efficient than avoiding in place entirely and always generating a new + // container. + + container_t *c; + if (type1 == SHARED_CONTAINER_TYPE) { + c = container_lazy_xor(c1, type1, c2, type2, &result_type); + shared_container_free(CAST_shared(c1)); // release + } + else { + c = container_lazy_ixor(c1, type1, c2, type2, &result_type); + } + + if (container_nonzero_cardinality(c, result_type)) { + ra_set_container_at_index(&x1->high_low_container, pos1, c, + result_type); + ++pos1; + } else { + container_free(c, result_type); + ra_remove_at_index(&x1->high_low_container, pos1); + --length1; + } + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + // container_t *c2_clone = container_clone(c2, type2); + c2 = get_copy_of_container(c2, &type2, is_cow(x2)); + if (is_cow(x2)) { + ra_set_container_at_index(&x2->high_low_container, pos2, c2, + type2); + } + ra_insert_new_key_value_at(&x1->high_low_container, pos1, s2, c2, + type2); + pos1++; + length1++; + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_copy_range(&x1->high_low_container, &x2->high_low_container, + pos2, length2, is_cow(x2)); + } +} + +void roaring_bitmap_repair_after_lazy(roaring_bitmap_t *r) { + roaring_array_t *ra = &r->high_low_container; + + for (int i = 0; i < ra->size; ++i) { + const uint8_t old_type = ra->typecodes[i]; + container_t *old_c = ra->containers[i]; + uint8_t new_type = old_type; + container_t *new_c = container_repair_after_lazy(old_c, &new_type); + ra->containers[i] = new_c; + ra->typecodes[i] = new_type; + } +} + + + +/** +* roaring_bitmap_rank returns the number of integers that are smaller or equal +* to x. +*/ +uint64_t roaring_bitmap_rank(const roaring_bitmap_t *bm, uint32_t x) { + uint64_t size = 0; + uint32_t xhigh = x >> 16; + for (int i = 0; i < bm->high_low_container.size; i++) { + uint32_t key = bm->high_low_container.keys[i]; + if (xhigh > key) { + size += + container_get_cardinality(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i]); + } else if (xhigh == key) { + return size + container_rank(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i], + x & 0xFFFF); + } else { + return size; + } + } + return size; +} +void roaring_bitmap_rank_many(const roaring_bitmap_t *bm, const uint32_t* begin, const uint32_t* end, uint64_t* ans) { + uint64_t size = 0; + + int i = 0; + const uint32_t* iter = begin; + while(i < bm->high_low_container.size && iter != end) { + uint32_t x = *iter; + uint32_t xhigh = x >> 16; + uint32_t key = bm->high_low_container.keys[i]; + if (xhigh > key) { + size += + container_get_cardinality(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i]); + i++; + } else if (xhigh == key) { + uint32_t consumed = container_rank_many(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i], + size, iter, end, ans); + iter += consumed; + ans += consumed; + } else { + *(ans++) = size; + iter++; + } + } +} + +/** + * roaring_bitmap_get_index returns the index of x, if not exsist return -1. + */ +int64_t roaring_bitmap_get_index(const roaring_bitmap_t *bm, uint32_t x) { + int64_t index = 0; + const uint16_t xhigh = x >> 16; + int32_t high_idx = ra_get_index(&bm->high_low_container, xhigh); + if (high_idx < 0) return -1; + + for (int i = 0; i < bm->high_low_container.size; i++) { + uint32_t key = bm->high_low_container.keys[i]; + if (xhigh > key) { + index += + container_get_cardinality(bm->high_low_container.containers[i], + bm->high_low_container.typecodes[i]); + } else if (xhigh == key) { + int32_t low_idx = container_get_index( + bm->high_low_container.containers[high_idx], + bm->high_low_container.typecodes[high_idx], x & 0xFFFF); + if (low_idx < 0) return -1; + return index + low_idx; + } else { + return -1; + } + } + return index; +} + +/** +* roaring_bitmap_smallest returns the smallest value in the set. +* Returns UINT32_MAX if the set is empty. +*/ +uint32_t roaring_bitmap_minimum(const roaring_bitmap_t *bm) { + if (bm->high_low_container.size > 0) { + container_t *c = bm->high_low_container.containers[0]; + uint8_t type = bm->high_low_container.typecodes[0]; + uint32_t key = bm->high_low_container.keys[0]; + uint32_t lowvalue = container_minimum(c, type); + return lowvalue | (key << 16); + } + return UINT32_MAX; +} + +/** +* roaring_bitmap_smallest returns the greatest value in the set. +* Returns 0 if the set is empty. +*/ +uint32_t roaring_bitmap_maximum(const roaring_bitmap_t *bm) { + if (bm->high_low_container.size > 0) { + container_t *container = + bm->high_low_container.containers[bm->high_low_container.size - 1]; + uint8_t typecode = + bm->high_low_container.typecodes[bm->high_low_container.size - 1]; + uint32_t key = + bm->high_low_container.keys[bm->high_low_container.size - 1]; + uint32_t lowvalue = container_maximum(container, typecode); + return lowvalue | (key << 16); + } + return 0; +} + +bool roaring_bitmap_select(const roaring_bitmap_t *bm, uint32_t rank, + uint32_t *element) { + container_t *container; + uint8_t typecode; + uint16_t key; + uint32_t start_rank = 0; + int i = 0; + bool valid = false; + while (!valid && i < bm->high_low_container.size) { + container = bm->high_low_container.containers[i]; + typecode = bm->high_low_container.typecodes[i]; + valid = + container_select(container, typecode, &start_rank, rank, element); + i++; + } + + if (valid) { + key = bm->high_low_container.keys[i - 1]; + *element |= (((uint32_t)key) << 16); // w/o cast, key promotes signed + return true; + } else + return false; +} + +bool roaring_bitmap_intersect(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint64_t answer = 0; + int pos1 = 0, pos2 = 0; + + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(& x1->high_low_container, (uint16_t)pos1); + const uint16_t s2 = ra_get_key_at_index(& x2->high_low_container, (uint16_t)pos2); + + if (s1 == s2) { + uint8_t type1, type2; + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + if (container_intersect(c1, type1, c2, type2)) + return true; + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(& x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(& x2->high_low_container, s1, pos2); + } + } + return answer != 0; +} + +bool roaring_bitmap_intersect_with_range(const roaring_bitmap_t *bm, + uint64_t x, uint64_t y) { + if (x >= y) { + // Empty range. + return false; + } + roaring_uint32_iterator_t it; + roaring_init_iterator(bm, &it); + if (!roaring_move_uint32_iterator_equalorlarger(&it, (uint32_t)x)) { + // No values above x. + return false; + } + if (it.current_value >= y) { + // No values below y. + return false; + } + return true; +} + + +uint64_t roaring_bitmap_and_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const int length1 = x1->high_low_container.size, + length2 = x2->high_low_container.size; + uint64_t answer = 0; + int pos1 = 0, pos2 = 0; + while (pos1 < length1 && pos2 < length2) { + const uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + const uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + if (s1 == s2) { + uint8_t type1, type2; + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + answer += container_and_cardinality(c1, type1, c2, type2); + ++pos1; + ++pos2; + } else if (s1 < s2) { // s1 < s2 + pos1 = ra_advance_until(&x1->high_low_container, s2, pos1); + } else { // s1 > s2 + pos2 = ra_advance_until(&x2->high_low_container, s1, pos2); + } + } + return answer; +} + +double roaring_bitmap_jaccard_index(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return (double)inter / (double)(c1 + c2 - inter); +} + +uint64_t roaring_bitmap_or_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 + c2 - inter; +} + +uint64_t roaring_bitmap_andnot_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 - inter; +} + +uint64_t roaring_bitmap_xor_cardinality(const roaring_bitmap_t *x1, + const roaring_bitmap_t *x2) { + const uint64_t c1 = roaring_bitmap_get_cardinality(x1); + const uint64_t c2 = roaring_bitmap_get_cardinality(x2); + const uint64_t inter = roaring_bitmap_and_cardinality(x1, x2); + return c1 + c2 - 2 * inter; +} + + +bool roaring_bitmap_contains(const roaring_bitmap_t *r, uint32_t val) { + const uint16_t hb = val >> 16; + /* + * the next function call involves a binary search and lots of branching. + */ + int32_t i = ra_get_index(&r->high_low_container, hb); + if (i < 0) return false; + + uint8_t typecode; + // next call ought to be cheap + container_t *container = + ra_get_container_at_index(&r->high_low_container, (uint16_t)i, &typecode); + // rest might be a tad expensive, possibly involving another round of binary search + return container_contains(container, val & 0xFFFF, typecode); +} + + +/** + * Check whether a range of values from range_start (included) to range_end (excluded) is present + */ +bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, uint64_t range_end) { + if(range_end >= UINT64_C(0x100000000)) { + range_end = UINT64_C(0x100000000); + } + if (range_start >= range_end) return true; // empty range are always contained! + if (range_end - range_start == 1) return roaring_bitmap_contains(r, (uint32_t)range_start); + uint16_t hb_rs = (uint16_t)(range_start >> 16); + uint16_t hb_re = (uint16_t)((range_end - 1) >> 16); + const int32_t span = hb_re - hb_rs; + const int32_t hlc_sz = ra_get_size(&r->high_low_container); + if (hlc_sz < span + 1) { + return false; + } + int32_t is = ra_get_index(&r->high_low_container, hb_rs); + int32_t ie = ra_get_index(&r->high_low_container, hb_re); + if ((ie < 0) || (is < 0) || ((ie - is) != span) || ie >= hlc_sz) { + return false; + } + const uint32_t lb_rs = range_start & 0xFFFF; + const uint32_t lb_re = ((range_end - 1) & 0xFFFF) + 1; + uint8_t type; + container_t *c = ra_get_container_at_index(&r->high_low_container, (uint16_t)is, + &type); + if (hb_rs == hb_re) { + return container_contains_range(c, lb_rs, lb_re, type); + } + if (!container_contains_range(c, lb_rs, 1 << 16, type)) { + return false; + } + c = ra_get_container_at_index(&r->high_low_container, (uint16_t)ie, &type); + if (!container_contains_range(c, 0, lb_re, type)) { + return false; + } + for (int32_t i = is + 1; i < ie; ++i) { + c = ra_get_container_at_index(&r->high_low_container, (uint16_t)i, &type); + if (!container_is_full(c, type) ) { + return false; + } + } + return true; +} + + +bool roaring_bitmap_is_strict_subset(const roaring_bitmap_t *r1, + const roaring_bitmap_t *r2) { + return (roaring_bitmap_get_cardinality(r2) > + roaring_bitmap_get_cardinality(r1) && + roaring_bitmap_is_subset(r1, r2)); +} + + +/* + * FROZEN SERIALIZATION FORMAT DESCRIPTION + * + * -- (beginning must be aligned by 32 bytes) -- + * uint64_t[BITSET_CONTAINER_SIZE_IN_WORDS * num_bitset_containers] + * rle16_t[total number of rle elements in all run containers] + * uint16_t[total number of array elements in all array containers] + * uint16_t[num_containers] + * uint16_t[num_containers] + * uint8_t[num_containers] + *

uint32_t + * + *
is a 4-byte value which is a bit union of FROZEN_COOKIE (15 bits) + * and the number of containers (17 bits). + * + * stores number of elements for every container. + * Its meaning depends on container type. + * For array and bitset containers, this value is the container cardinality minus one. + * For run container, it is the number of rle_t elements (n_runs). + * + * ,, are flat arrays of elements of + * all containers of respective type. + * + * <*_data> and are kept close together because they are not accessed + * during deserilization. This may reduce IO in case of large mmaped bitmaps. + * All members have their native alignments during deserilization except
, + * which is not guaranteed to be aligned by 4 bytes. + */ + +size_t roaring_bitmap_frozen_size_in_bytes(const roaring_bitmap_t *rb) { + const roaring_array_t *ra = &rb->high_low_container; + size_t num_bytes = 0; + for (int32_t i = 0; i < ra->size; i++) { + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE: { + num_bytes += BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + } + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(ra->containers[i]); + num_bytes += rc->n_runs * sizeof(rle16_t); + break; + } + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = + const_CAST_array(ra->containers[i]); + num_bytes += ac->cardinality * sizeof(uint16_t); + break; + } + default: + roaring_unreachable; + } + } + num_bytes += (2 + 2 + 1) * ra->size; // keys, counts, typecodes + num_bytes += 4; // header + return num_bytes; +} + +inline static void *arena_alloc(char **arena, size_t num_bytes) { + char *res = *arena; + *arena += num_bytes; + return res; +} + +void roaring_bitmap_frozen_serialize(const roaring_bitmap_t *rb, char *buf) { + /* + * Note: we do not require user to supply a specifically aligned buffer. + * Thus we have to use memcpy() everywhere. + */ + + const roaring_array_t *ra = &rb->high_low_container; + + size_t bitset_zone_size = 0; + size_t run_zone_size = 0; + size_t array_zone_size = 0; + for (int32_t i = 0; i < ra->size; i++) { + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE: { + bitset_zone_size += + BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + } + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(ra->containers[i]); + run_zone_size += rc->n_runs * sizeof(rle16_t); + break; + } + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = + const_CAST_array(ra->containers[i]); + array_zone_size += ac->cardinality * sizeof(uint16_t); + break; + } + default: + roaring_unreachable; + } + } + + uint64_t *bitset_zone = (uint64_t *)arena_alloc(&buf, bitset_zone_size); + rle16_t *run_zone = (rle16_t *)arena_alloc(&buf, run_zone_size); + uint16_t *array_zone = (uint16_t *)arena_alloc(&buf, array_zone_size); + uint16_t *key_zone = (uint16_t *)arena_alloc(&buf, 2*ra->size); + uint16_t *count_zone = (uint16_t *)arena_alloc(&buf, 2*ra->size); + uint8_t *typecode_zone = (uint8_t *)arena_alloc(&buf, ra->size); + uint32_t *header_zone = (uint32_t *)arena_alloc(&buf, 4); + + for (int32_t i = 0; i < ra->size; i++) { + uint16_t count; + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE: { + const bitset_container_t *bc = + const_CAST_bitset(ra->containers[i]); + memcpy(bitset_zone, bc->words, + BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t)); + bitset_zone += BITSET_CONTAINER_SIZE_IN_WORDS; + if (bc->cardinality != BITSET_UNKNOWN_CARDINALITY) { + count = (uint16_t)(bc->cardinality - 1); + } else { + count = (uint16_t)(bitset_container_compute_cardinality(bc) - 1); + } + break; + } + case RUN_CONTAINER_TYPE: { + const run_container_t *rc = const_CAST_run(ra->containers[i]); + size_t num_bytes = rc->n_runs * sizeof(rle16_t); + memcpy(run_zone, rc->runs, num_bytes); + run_zone += rc->n_runs; + count = (uint16_t)rc->n_runs; + break; + } + case ARRAY_CONTAINER_TYPE: { + const array_container_t *ac = + const_CAST_array(ra->containers[i]); + size_t num_bytes = ac->cardinality * sizeof(uint16_t); + memcpy(array_zone, ac->array, num_bytes); + array_zone += ac->cardinality; + count = (uint16_t)(ac->cardinality - 1); + break; + } + default: + roaring_unreachable; + } + memcpy(&count_zone[i], &count, 2); + } + memcpy(key_zone, ra->keys, ra->size * sizeof(uint16_t)); + memcpy(typecode_zone, ra->typecodes, ra->size * sizeof(uint8_t)); + uint32_t header = ((uint32_t)ra->size << 15) | FROZEN_COOKIE; + memcpy(header_zone, &header, 4); +} + +const roaring_bitmap_t * +roaring_bitmap_frozen_view(const char *buf, size_t length) { + if ((uintptr_t)buf % 32 != 0) { + return NULL; + } + + // cookie and num_containers + if (length < 4) { + return NULL; + } + uint32_t header; + memcpy(&header, buf + length - 4, 4); // header may be misaligned + if ((header & 0x7FFF) != FROZEN_COOKIE) { + return NULL; + } + int32_t num_containers = (header >> 15); + + // typecodes, counts and keys + if (length < 4 + (size_t)num_containers * (1 + 2 + 2)) { + return NULL; + } + uint16_t *keys = (uint16_t *)(buf + length - 4 - num_containers * 5); + uint16_t *counts = (uint16_t *)(buf + length - 4 - num_containers * 3); + uint8_t *typecodes = (uint8_t *)(buf + length - 4 - num_containers * 1); + + // {bitset,array,run}_zone + int32_t num_bitset_containers = 0; + int32_t num_run_containers = 0; + int32_t num_array_containers = 0; + size_t bitset_zone_size = 0; + size_t run_zone_size = 0; + size_t array_zone_size = 0; + for (int32_t i = 0; i < num_containers; i++) { + switch (typecodes[i]) { + case BITSET_CONTAINER_TYPE: + num_bitset_containers++; + bitset_zone_size += BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + break; + case RUN_CONTAINER_TYPE: + num_run_containers++; + run_zone_size += counts[i] * sizeof(rle16_t); + break; + case ARRAY_CONTAINER_TYPE: + num_array_containers++; + array_zone_size += (counts[i] + UINT32_C(1)) * sizeof(uint16_t); + break; + default: + return NULL; + } + } + if (length != bitset_zone_size + run_zone_size + array_zone_size + + 5 * num_containers + 4) { + return NULL; + } + uint64_t *bitset_zone = (uint64_t*) (buf); + rle16_t *run_zone = (rle16_t*) (buf + bitset_zone_size); + uint16_t *array_zone = (uint16_t*) (buf + bitset_zone_size + run_zone_size); + + size_t alloc_size = 0; + alloc_size += sizeof(roaring_bitmap_t); + alloc_size += num_containers * sizeof(container_t*); + alloc_size += num_bitset_containers * sizeof(bitset_container_t); + alloc_size += num_run_containers * sizeof(run_container_t); + alloc_size += num_array_containers * sizeof(array_container_t); + + char *arena = (char *)roaring_malloc(alloc_size); + if (arena == NULL) { + return NULL; + } + + roaring_bitmap_t *rb = (roaring_bitmap_t *) + arena_alloc(&arena, sizeof(roaring_bitmap_t)); + rb->high_low_container.flags = ROARING_FLAG_FROZEN; + rb->high_low_container.allocation_size = num_containers; + rb->high_low_container.size = num_containers; + rb->high_low_container.keys = (uint16_t *)keys; + rb->high_low_container.typecodes = (uint8_t *)typecodes; + rb->high_low_container.containers = + (container_t **)arena_alloc(&arena, + sizeof(container_t*) * num_containers); + // Ensure offset of high_low_container.containers is known distance used in + // C++ wrapper. sizeof(roaring_bitmap_t) is used as it is the size of the + // only allocation that precedes high_low_container.containers. If this is + // changed (new allocation or changed order), this offset will also need to + // be changed in the C++ wrapper. + assert(rb == + (roaring_bitmap_t *)((char *)rb->high_low_container.containers - + sizeof(roaring_bitmap_t))); + for (int32_t i = 0; i < num_containers; i++) { + switch (typecodes[i]) { + case BITSET_CONTAINER_TYPE: { + bitset_container_t *bitset = (bitset_container_t *) + arena_alloc(&arena, sizeof(bitset_container_t)); + bitset->words = bitset_zone; + bitset->cardinality = counts[i] + UINT32_C(1); + rb->high_low_container.containers[i] = bitset; + bitset_zone += BITSET_CONTAINER_SIZE_IN_WORDS; + break; + } + case RUN_CONTAINER_TYPE: { + run_container_t *run = (run_container_t *) + arena_alloc(&arena, sizeof(run_container_t)); + run->capacity = counts[i]; + run->n_runs = counts[i]; + run->runs = run_zone; + rb->high_low_container.containers[i] = run; + run_zone += run->n_runs; + break; + } + case ARRAY_CONTAINER_TYPE: { + array_container_t *array = (array_container_t *) + arena_alloc(&arena, sizeof(array_container_t)); + array->capacity = counts[i] + UINT32_C(1); + array->cardinality = counts[i] + UINT32_C(1); + array->array = array_zone; + rb->high_low_container.containers[i] = array; + array_zone += counts[i] + UINT32_C(1); + break; + } + default: + roaring_free(arena); + return NULL; + } + } + + return rb; +} + +ALLOW_UNALIGNED +roaring_bitmap_t *roaring_bitmap_portable_deserialize_frozen(const char *buf) { + char *start_of_buf = (char *) buf; + uint32_t cookie; + int32_t num_containers; + uint16_t *descriptive_headers; + uint32_t *offset_headers = NULL; + const char *run_flag_bitset = NULL; + bool hasrun = false; + + // deserialize cookie + memcpy(&cookie, buf, sizeof(uint32_t)); + buf += sizeof(uint32_t); + if (cookie == SERIAL_COOKIE_NO_RUNCONTAINER) { + memcpy(&num_containers, buf, sizeof(int32_t)); + buf += sizeof(int32_t); + descriptive_headers = (uint16_t *) buf; + buf += num_containers * 2 * sizeof(uint16_t); + offset_headers = (uint32_t *) buf; + buf += num_containers * sizeof(uint32_t); + } else if ((cookie & 0xFFFF) == SERIAL_COOKIE) { + num_containers = (cookie >> 16) + 1; + hasrun = true; + int32_t run_flag_bitset_size = (num_containers + 7) / 8; + run_flag_bitset = buf; + buf += run_flag_bitset_size; + descriptive_headers = (uint16_t *) buf; + buf += num_containers * 2 * sizeof(uint16_t); + if(num_containers >= NO_OFFSET_THRESHOLD) { + offset_headers = (uint32_t *) buf; + buf += num_containers * sizeof(uint32_t); + } + } else { + return NULL; + } + + // calculate total size for allocation + int32_t num_bitset_containers = 0; + int32_t num_run_containers = 0; + int32_t num_array_containers = 0; + + for (int32_t i = 0; i < num_containers; i++) { + uint16_t tmp; + memcpy(&tmp, descriptive_headers + 2*i+1, sizeof(tmp)); + uint32_t cardinality = tmp + 1; + bool isbitmap = (cardinality > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((run_flag_bitset[i / 8] & (1 << (i % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + + if (isbitmap) { + num_bitset_containers++; + } else if (isrun) { + num_run_containers++; + } else { + num_array_containers++; + } + } + + size_t alloc_size = 0; + alloc_size += sizeof(roaring_bitmap_t); + alloc_size += num_containers * sizeof(container_t*); + alloc_size += num_bitset_containers * sizeof(bitset_container_t); + alloc_size += num_run_containers * sizeof(run_container_t); + alloc_size += num_array_containers * sizeof(array_container_t); + alloc_size += num_containers * sizeof(uint16_t); // keys + alloc_size += num_containers * sizeof(uint8_t); // typecodes + + // allocate bitmap and construct containers + char *arena = (char *)roaring_malloc(alloc_size); + if (arena == NULL) { + return NULL; + } + + roaring_bitmap_t *rb = (roaring_bitmap_t *) + arena_alloc(&arena, sizeof(roaring_bitmap_t)); + rb->high_low_container.flags = ROARING_FLAG_FROZEN; + rb->high_low_container.allocation_size = num_containers; + rb->high_low_container.size = num_containers; + rb->high_low_container.containers = + (container_t **)arena_alloc(&arena, + sizeof(container_t*) * num_containers); + + uint16_t *keys = (uint16_t *)arena_alloc(&arena, num_containers * sizeof(uint16_t)); + uint8_t *typecodes = (uint8_t *)arena_alloc(&arena, num_containers * sizeof(uint8_t)); + + rb->high_low_container.keys = keys; + rb->high_low_container.typecodes = typecodes; + + for (int32_t i = 0; i < num_containers; i++) { + uint16_t tmp; + memcpy(&tmp, descriptive_headers + 2*i+1, sizeof(tmp)); + int32_t cardinality = tmp + 1; + bool isbitmap = (cardinality > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((run_flag_bitset[i / 8] & (1 << (i % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + + keys[i] = descriptive_headers[2*i]; + + if (isbitmap) { + typecodes[i] = BITSET_CONTAINER_TYPE; + bitset_container_t *c = (bitset_container_t *)arena_alloc(&arena, sizeof(bitset_container_t)); + c->cardinality = cardinality; + if(offset_headers != NULL) { + c->words = (uint64_t *) (start_of_buf + offset_headers[i]); + } else { + c->words = (uint64_t *) buf; + buf += BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + } + rb->high_low_container.containers[i] = c; + } else if (isrun) { + typecodes[i] = RUN_CONTAINER_TYPE; + run_container_t *c = (run_container_t *)arena_alloc(&arena, sizeof(run_container_t)); + c->capacity = cardinality; + uint16_t n_runs; + if(offset_headers != NULL) { + memcpy(&n_runs, start_of_buf + offset_headers[i], sizeof(uint16_t)); + c->n_runs = n_runs; + c->runs = (rle16_t *) (start_of_buf + offset_headers[i] + sizeof(uint16_t)); + } else { + memcpy(&n_runs, buf, sizeof(uint16_t)); + c->n_runs = n_runs; + buf += sizeof(uint16_t); + c->runs = (rle16_t *) buf; + buf += c->n_runs * sizeof(rle16_t); + } + rb->high_low_container.containers[i] = c; + } else { + typecodes[i] = ARRAY_CONTAINER_TYPE; + array_container_t *c = (array_container_t *)arena_alloc(&arena, sizeof(array_container_t)); + c->cardinality = cardinality; + c->capacity = cardinality; + if(offset_headers != NULL) { + c->array = (uint16_t *) (start_of_buf + offset_headers[i]); + } else { + c->array = (uint16_t *) buf; + buf += cardinality * sizeof(uint16_t); + } + rb->high_low_container.containers[i] = c; + } + } + + return rb; +} + +bool roaring_bitmap_to_bitset(const roaring_bitmap_t *r, bitset_t * bitset) { + uint32_t max_value = roaring_bitmap_maximum(r); + size_t new_array_size = (size_t)(((uint64_t)max_value + 63)/64); + bool resize_ok = bitset_resize(bitset, new_array_size, true); + if(!resize_ok) { return false; } + const roaring_array_t *ra = &r->high_low_container; + for (int i = 0; i < ra->size; ++i) { + uint64_t* words = bitset->array + (ra->keys[i]<<10); + uint8_t type = ra->typecodes[i]; + const container_t *c = ra->containers[i]; + if(type == SHARED_CONTAINER_TYPE) { + c = container_unwrap_shared(c, &type); + } + switch (type) { + case BITSET_CONTAINER_TYPE: + { + size_t max_word_index = new_array_size - (ra->keys[i]<<10); + if(max_word_index > 1024) { max_word_index = 1024; } + const bitset_container_t *src = const_CAST_bitset(c); + memcpy(words, src->words, max_word_index * sizeof(uint64_t)); + } + break; + case ARRAY_CONTAINER_TYPE: + { + const array_container_t *src = const_CAST_array(c); + bitset_set_list(words, src->array, src->cardinality); + } + break; + case RUN_CONTAINER_TYPE: + { + const run_container_t *src = const_CAST_run(c); + for (int32_t rlepos = 0; rlepos < src->n_runs; ++rlepos) { + rle16_t rle = src->runs[rlepos]; + bitset_set_lenrange(words, rle.value, rle.length); + } + } + break; + default: + roaring_unreachable; + } + } + return true; +} + +}} +/* end file src/roaring.c */ +/* begin file src/roaring_array.c */ +#include +#include +#include +#include +#include +#include + + +namespace paimon::roaring { namespace internal { + +// Convention: [0,ra->size) all elements are initialized +// [ra->size, ra->allocation_size) is junk and contains nothing needing freeing + +inline int32_t ra_get_size(const roaring_array_t *ra); +inline int32_t ra_get_index(const roaring_array_t *ra, uint16_t x); + +inline container_t *ra_get_container_at_index( + const roaring_array_t *ra, uint16_t i, + uint8_t *typecode); + +inline void ra_unshare_container_at_index(roaring_array_t *ra, + uint16_t i); + +inline void ra_replace_key_and_container_at_index( + roaring_array_t *ra, int32_t i, uint16_t key, + container_t *c, uint8_t typecode); + +inline void ra_set_container_at_index( + const roaring_array_t *ra, int32_t i, + container_t *c, uint8_t typecode); + +static bool realloc_array(roaring_array_t *ra, int32_t new_capacity) { + // + // Note: not implemented using C's realloc(), because the memory layout is + // Struct-of-Arrays vs. Array-of-Structs: + // https://github.com/RoaringBitmap/CRoaring/issues/256 + + if ( new_capacity == 0 ) { + roaring_free(ra->containers); + ra->containers = NULL; + ra->keys = NULL; + ra->typecodes = NULL; + ra->allocation_size = 0; + return true; + } + const size_t memoryneeded = new_capacity * ( + sizeof(uint16_t) + sizeof(container_t *) + sizeof(uint8_t)); + void *bigalloc = roaring_malloc(memoryneeded); + if (!bigalloc) return false; + void *oldbigalloc = ra->containers; + container_t **newcontainers = (container_t **)bigalloc; + uint16_t *newkeys = (uint16_t *)(newcontainers + new_capacity); + uint8_t *newtypecodes = (uint8_t *)(newkeys + new_capacity); + assert((char *)(newtypecodes + new_capacity) == + (char *)bigalloc + memoryneeded); + if(ra->size > 0) { + memcpy(newcontainers, ra->containers, sizeof(container_t *) * ra->size); + memcpy(newkeys, ra->keys, sizeof(uint16_t) * ra->size); + memcpy(newtypecodes, ra->typecodes, sizeof(uint8_t) * ra->size); + } + ra->containers = newcontainers; + ra->keys = newkeys; + ra->typecodes = newtypecodes; + ra->allocation_size = new_capacity; + roaring_free(oldbigalloc); + return true; +} + +bool ra_init_with_capacity(roaring_array_t *new_ra, uint32_t cap) { + if (!new_ra) return false; + ra_init(new_ra); + + // Containers hold 64Ki elements, so 64Ki containers is enough to hold `0x10000 * 0x10000` (all 2^32) elements + if (cap > 0x10000) { + cap = 0x10000; + } + + if(cap > 0) { + void *bigalloc = roaring_malloc(cap * + (sizeof(uint16_t) + sizeof(container_t *) + sizeof(uint8_t))); + if( bigalloc == NULL ) return false; + new_ra->containers = (container_t **)bigalloc; + new_ra->keys = (uint16_t *)(new_ra->containers + cap); + new_ra->typecodes = (uint8_t *)(new_ra->keys + cap); + // Narrowing is safe because of above check + new_ra->allocation_size = (int32_t)cap; + } + return true; +} + +int ra_shrink_to_fit(roaring_array_t *ra) { + int savings = (ra->allocation_size - ra->size) * + (sizeof(uint16_t) + sizeof(container_t *) + sizeof(uint8_t)); + if (!realloc_array(ra, ra->size)) { + return 0; + } + ra->allocation_size = ra->size; + return savings; +} + +void ra_init(roaring_array_t *new_ra) { + if (!new_ra) { return; } + new_ra->keys = NULL; + new_ra->containers = NULL; + new_ra->typecodes = NULL; + + new_ra->allocation_size = 0; + new_ra->size = 0; + new_ra->flags = 0; +} + +bool ra_overwrite(const roaring_array_t *source, roaring_array_t *dest, + bool copy_on_write) { + ra_clear_containers(dest); // we are going to overwrite them + if (source->size == 0) { // Note: can't call memcpy(NULL), even w/size + dest->size = 0; // <--- This is important. + return true; // output was just cleared, so they match + } + if (dest->allocation_size < source->size) { + if (!realloc_array(dest, source->size)) { + return false; + } + } + dest->size = source->size; + memcpy(dest->keys, source->keys, dest->size * sizeof(uint16_t)); + // we go through the containers, turning them into shared containers... + if (copy_on_write) { + for (int32_t i = 0; i < dest->size; ++i) { + source->containers[i] = get_copy_of_container( + source->containers[i], &source->typecodes[i], copy_on_write); + } + // we do a shallow copy to the other bitmap + memcpy(dest->containers, source->containers, + dest->size * sizeof(container_t *)); + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + } else { + memcpy(dest->typecodes, source->typecodes, + dest->size * sizeof(uint8_t)); + for (int32_t i = 0; i < dest->size; i++) { + dest->containers[i] = + container_clone(source->containers[i], source->typecodes[i]); + if (dest->containers[i] == NULL) { + for (int32_t j = 0; j < i; j++) { + container_free(dest->containers[j], dest->typecodes[j]); + } + ra_clear_without_containers(dest); + return false; + } + } + } + return true; +} + +void ra_clear_containers(roaring_array_t *ra) { + for (int32_t i = 0; i < ra->size; ++i) { + container_free(ra->containers[i], ra->typecodes[i]); + } +} + +void ra_reset(roaring_array_t *ra) { + ra_clear_containers(ra); + ra->size = 0; + ra_shrink_to_fit(ra); +} + +void ra_clear_without_containers(roaring_array_t *ra) { + roaring_free(ra->containers); // keys and typecodes are allocated with containers + ra->size = 0; + ra->allocation_size = 0; + ra->containers = NULL; + ra->keys = NULL; + ra->typecodes = NULL; +} + +void ra_clear(roaring_array_t *ra) { + ra_clear_containers(ra); + ra_clear_without_containers(ra); +} + +bool extend_array(roaring_array_t *ra, int32_t k) { + int32_t desired_size = ra->size + k; + const int32_t max_containers = 65536; + assert(desired_size <= max_containers); + if (desired_size > ra->allocation_size) { + int32_t new_capacity = + (ra->size < 1024) ? 2 * desired_size : 5 * desired_size / 4; + if (new_capacity > max_containers) { + new_capacity = max_containers; + } + + return realloc_array(ra, new_capacity); + } + return true; +} + +void ra_append( + roaring_array_t *ra, uint16_t key, + container_t *c, uint8_t typecode +){ + extend_array(ra, 1); + const int32_t pos = ra->size; + + ra->keys[pos] = key; + ra->containers[pos] = c; + ra->typecodes[pos] = typecode; + ra->size++; +} + +void ra_append_copy(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t index, bool copy_on_write) { + extend_array(ra, 1); + const int32_t pos = ra->size; + + // old contents is junk not needing freeing + ra->keys[pos] = sa->keys[index]; + // the shared container will be in two bitmaps + if (copy_on_write) { + sa->containers[index] = get_copy_of_container( + sa->containers[index], &sa->typecodes[index], copy_on_write); + ra->containers[pos] = sa->containers[index]; + ra->typecodes[pos] = sa->typecodes[index]; + } else { + ra->containers[pos] = + container_clone(sa->containers[index], sa->typecodes[index]); + ra->typecodes[pos] = sa->typecodes[index]; + } + ra->size++; +} + +void ra_append_copies_until(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t stopping_key, bool copy_on_write) { + for (int32_t i = 0; i < sa->size; ++i) { + if (sa->keys[i] >= stopping_key) break; + ra_append_copy(ra, sa, (uint16_t)i, copy_on_write); + } +} + +void ra_append_copy_range(roaring_array_t *ra, const roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write) { + extend_array(ra, end_index - start_index); + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + ra->keys[pos] = sa->keys[i]; + if (copy_on_write) { + sa->containers[i] = get_copy_of_container( + sa->containers[i], &sa->typecodes[i], copy_on_write); + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + } else { + ra->containers[pos] = + container_clone(sa->containers[i], sa->typecodes[i]); + ra->typecodes[pos] = sa->typecodes[i]; + } + ra->size++; + } +} + +void ra_append_copies_after(roaring_array_t *ra, const roaring_array_t *sa, + uint16_t before_start, bool copy_on_write) { + int start_location = ra_get_index(sa, before_start); + if (start_location >= 0) + ++start_location; + else + start_location = -start_location - 1; + ra_append_copy_range(ra, sa, start_location, sa->size, copy_on_write); +} + +void ra_append_move_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index) { + extend_array(ra, end_index - start_index); + + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + + ra->keys[pos] = sa->keys[i]; + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + ra->size++; + } +} + +void ra_append_range(roaring_array_t *ra, roaring_array_t *sa, + int32_t start_index, int32_t end_index, + bool copy_on_write) { + extend_array(ra, end_index - start_index); + + for (int32_t i = start_index; i < end_index; ++i) { + const int32_t pos = ra->size; + ra->keys[pos] = sa->keys[i]; + if (copy_on_write) { + sa->containers[i] = get_copy_of_container( + sa->containers[i], &sa->typecodes[i], copy_on_write); + ra->containers[pos] = sa->containers[i]; + ra->typecodes[pos] = sa->typecodes[i]; + } else { + ra->containers[pos] = + container_clone(sa->containers[i], sa->typecodes[i]); + ra->typecodes[pos] = sa->typecodes[i]; + } + ra->size++; + } +} + +container_t *ra_get_container( + roaring_array_t *ra, uint16_t x, uint8_t *typecode +){ + int i = binarySearch(ra->keys, (int32_t)ra->size, x); + if (i < 0) return NULL; + *typecode = ra->typecodes[i]; + return ra->containers[i]; +} + +inline container_t *ra_get_container_at_index( + const roaring_array_t *ra, uint16_t i, + uint8_t *typecode); + +inline uint16_t ra_get_key_at_index(const roaring_array_t *ra, + uint16_t i); + +inline int32_t ra_get_index(const roaring_array_t *ra, uint16_t x); + +inline int32_t ra_advance_until(const roaring_array_t *ra, uint16_t x, + int32_t pos); + +// everything skipped over is freed +int32_t ra_advance_until_freeing(roaring_array_t *ra, uint16_t x, int32_t pos) { + while (pos < ra->size && ra->keys[pos] < x) { + container_free(ra->containers[pos], ra->typecodes[pos]); + ++pos; + } + return pos; +} + +void ra_insert_new_key_value_at( + roaring_array_t *ra, int32_t i, uint16_t key, + container_t *c, uint8_t typecode +){ + extend_array(ra, 1); + // May be an optimization opportunity with DIY memmove + memmove(&(ra->keys[i + 1]), &(ra->keys[i]), + sizeof(uint16_t) * (ra->size - i)); + memmove(&(ra->containers[i + 1]), &(ra->containers[i]), + sizeof(container_t *) * (ra->size - i)); + memmove(&(ra->typecodes[i + 1]), &(ra->typecodes[i]), + sizeof(uint8_t) * (ra->size - i)); + ra->keys[i] = key; + ra->containers[i] = c; + ra->typecodes[i] = typecode; + ra->size++; +} + +// note: Java routine set things to 0, enabling GC. +// Java called it "resize" but it was always used to downsize. +// Allowing upsize would break the conventions about +// valid containers below ra->size. + +void ra_downsize(roaring_array_t *ra, int32_t new_length) { + assert(new_length <= ra->size); + ra->size = new_length; +} + +void ra_remove_at_index(roaring_array_t *ra, int32_t i) { + memmove(&(ra->containers[i]), &(ra->containers[i + 1]), + sizeof(container_t *) * (ra->size - i - 1)); + memmove(&(ra->keys[i]), &(ra->keys[i + 1]), + sizeof(uint16_t) * (ra->size - i - 1)); + memmove(&(ra->typecodes[i]), &(ra->typecodes[i + 1]), + sizeof(uint8_t) * (ra->size - i - 1)); + ra->size--; +} + +void ra_remove_at_index_and_free(roaring_array_t *ra, int32_t i) { + container_free(ra->containers[i], ra->typecodes[i]); + ra_remove_at_index(ra, i); +} + +// used in inplace andNot only, to slide left the containers from +// the mutated RoaringBitmap that are after the largest container of +// the argument RoaringBitmap. In use it should be followed by a call to +// downsize. +// +void ra_copy_range(roaring_array_t *ra, uint32_t begin, uint32_t end, + uint32_t new_begin) { + assert(begin <= end); + assert(new_begin < begin); + + const int range = end - begin; + + // We ensure to previously have freed overwritten containers + // that are not copied elsewhere + + memmove(&(ra->containers[new_begin]), &(ra->containers[begin]), + sizeof(container_t *) * range); + memmove(&(ra->keys[new_begin]), &(ra->keys[begin]), + sizeof(uint16_t) * range); + memmove(&(ra->typecodes[new_begin]), &(ra->typecodes[begin]), + sizeof(uint8_t) * range); +} + +void ra_shift_tail(roaring_array_t *ra, int32_t count, int32_t distance) { + if (distance > 0) { + extend_array(ra, distance); + } + int32_t srcpos = ra->size - count; + int32_t dstpos = srcpos + distance; + memmove(&(ra->keys[dstpos]), &(ra->keys[srcpos]), + sizeof(uint16_t) * count); + memmove(&(ra->containers[dstpos]), &(ra->containers[srcpos]), + sizeof(container_t *) * count); + memmove(&(ra->typecodes[dstpos]), &(ra->typecodes[srcpos]), + sizeof(uint8_t) * count); + ra->size += distance; +} + + +void ra_to_uint32_array(const roaring_array_t *ra, uint32_t *ans) { + size_t ctr = 0; + for (int32_t i = 0; i < ra->size; ++i) { + int num_added = container_to_uint32_array( + ans + ctr, ra->containers[i], ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + ctr += num_added; + } +} + +bool ra_range_uint32_array(const roaring_array_t *ra, size_t offset, size_t limit, uint32_t *ans) { + size_t ctr = 0; + size_t dtr = 0; + + size_t t_limit = 0; + + bool first = false; + size_t first_skip = 0; + + uint32_t *t_ans = NULL; + size_t cur_len = 0; + + for (int i = 0; i < ra->size; ++i) { + + const container_t *c = container_unwrap_shared( + ra->containers[i], &ra->typecodes[i]); + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE: + t_limit = (const_CAST_bitset(c))->cardinality; + break; + case ARRAY_CONTAINER_TYPE: + t_limit = (const_CAST_array(c))->cardinality; + break; + case RUN_CONTAINER_TYPE: + t_limit = run_container_cardinality(const_CAST_run(c)); + break; + } + if (ctr + t_limit - 1 >= offset && ctr < offset + limit){ + if (!first){ + //first_skip = t_limit - (ctr + t_limit - offset); + first_skip = offset - ctr; + first = true; + t_ans = (uint32_t *)roaring_malloc(sizeof(*t_ans) * (first_skip + limit)); + if(t_ans == NULL) { + return false; + } + memset(t_ans, 0, sizeof(*t_ans) * (first_skip + limit)) ; + cur_len = first_skip + limit; + } + if (dtr + t_limit > cur_len){ + uint32_t * append_ans = (uint32_t *)roaring_malloc(sizeof(*append_ans) * (cur_len + t_limit)); + if(append_ans == NULL) { + if(t_ans != NULL) roaring_free(t_ans); + return false; + } + memset(append_ans, 0, sizeof(*append_ans) * (cur_len + t_limit)); + cur_len = cur_len + t_limit; + memcpy(append_ans, t_ans, dtr * sizeof(uint32_t)); + roaring_free(t_ans); + t_ans = append_ans; + } + switch (ra->typecodes[i]) { + case BITSET_CONTAINER_TYPE: + container_to_uint32_array( + t_ans + dtr, + const_CAST_bitset(c), ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + case ARRAY_CONTAINER_TYPE: + container_to_uint32_array( + t_ans + dtr, + const_CAST_array(c), ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + case RUN_CONTAINER_TYPE: + container_to_uint32_array( + t_ans + dtr, + const_CAST_run(c), ra->typecodes[i], + ((uint32_t)ra->keys[i]) << 16); + break; + } + dtr += t_limit; + } + ctr += t_limit; + if (dtr-first_skip >= limit) break; + } + if(t_ans != NULL) { + memcpy(ans, t_ans+first_skip, limit * sizeof(uint32_t)); + free(t_ans); + } + return true; +} + +bool ra_has_run_container(const roaring_array_t *ra) { + for (int32_t k = 0; k < ra->size; ++k) { + if (get_container_type(ra->containers[k], ra->typecodes[k]) == + RUN_CONTAINER_TYPE) + return true; + } + return false; +} + +uint32_t ra_portable_header_size(const roaring_array_t *ra) { + if (ra_has_run_container(ra)) { + if (ra->size < + NO_OFFSET_THRESHOLD) { // for small bitmaps, we omit the offsets + return 4 + (ra->size + 7) / 8 + 4 * ra->size; + } + return 4 + (ra->size + 7) / 8 + + 8 * ra->size; // - 4 because we pack the size with the cookie + } else { + return 4 + 4 + 8 * ra->size; + } +} + +size_t ra_portable_size_in_bytes(const roaring_array_t *ra) { + size_t count = ra_portable_header_size(ra); + + for (int32_t k = 0; k < ra->size; ++k) { + count += container_size_in_bytes(ra->containers[k], ra->typecodes[k]); + } + return count; +} + +// This function is endian-sensitive. +size_t ra_portable_serialize(const roaring_array_t *ra, char *buf) { + char *initbuf = buf; + uint32_t startOffset = 0; + bool hasrun = ra_has_run_container(ra); + if (hasrun) { + uint32_t cookie = SERIAL_COOKIE | ((ra->size - 1) << 16); + memcpy(buf, &cookie, sizeof(cookie)); + buf += sizeof(cookie); + uint32_t s = (ra->size + 7) / 8; + uint8_t *bitmapOfRunContainers = (uint8_t *)roaring_calloc(s, 1); + assert(bitmapOfRunContainers != NULL); // todo: handle + for (int32_t i = 0; i < ra->size; ++i) { + if (get_container_type(ra->containers[i], ra->typecodes[i]) == + RUN_CONTAINER_TYPE) { + bitmapOfRunContainers[i / 8] |= (1 << (i % 8)); + } + } + memcpy(buf, bitmapOfRunContainers, s); + buf += s; + roaring_free(bitmapOfRunContainers); + if (ra->size < NO_OFFSET_THRESHOLD) { + startOffset = 4 + 4 * ra->size + s; + } else { + startOffset = 4 + 8 * ra->size + s; + } + } else { // backwards compatibility + uint32_t cookie = SERIAL_COOKIE_NO_RUNCONTAINER; + + memcpy(buf, &cookie, sizeof(cookie)); + buf += sizeof(cookie); + memcpy(buf, &ra->size, sizeof(ra->size)); + buf += sizeof(ra->size); + + startOffset = 4 + 4 + 4 * ra->size + 4 * ra->size; + } + for (int32_t k = 0; k < ra->size; ++k) { + memcpy(buf, &ra->keys[k], sizeof(ra->keys[k])); + buf += sizeof(ra->keys[k]); + // get_cardinality returns a value in [1,1<<16], subtracting one + // we get [0,1<<16 - 1] which fits in 16 bits + uint16_t card = (uint16_t)( + container_get_cardinality(ra->containers[k], ra->typecodes[k]) - 1); + memcpy(buf, &card, sizeof(card)); + buf += sizeof(card); + } + if ((!hasrun) || (ra->size >= NO_OFFSET_THRESHOLD)) { + // writing the containers offsets + for (int32_t k = 0; k < ra->size; k++) { + memcpy(buf, &startOffset, sizeof(startOffset)); + buf += sizeof(startOffset); + startOffset = + startOffset + + container_size_in_bytes(ra->containers[k], ra->typecodes[k]); + } + } + for (int32_t k = 0; k < ra->size; ++k) { + buf += container_write(ra->containers[k], ra->typecodes[k], buf); + } + return buf - initbuf; +} + +// Quickly checks whether there is a serialized bitmap at the pointer, +// not exceeding size "maxbytes" in bytes. This function does not allocate +// memory dynamically. +// +// This function returns 0 if and only if no valid bitmap is found. +// Otherwise, it returns how many bytes are occupied. +// +size_t ra_portable_deserialize_size(const char *buf, const size_t maxbytes) { + size_t bytestotal = sizeof(int32_t);// for cookie + if(bytestotal > maxbytes) return 0; + uint32_t cookie; + memcpy(&cookie, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + if ((cookie & 0xFFFF) != SERIAL_COOKIE && + cookie != SERIAL_COOKIE_NO_RUNCONTAINER) { + return 0; + } + int32_t size; + + if ((cookie & 0xFFFF) == SERIAL_COOKIE) + size = (cookie >> 16) + 1; + else { + bytestotal += sizeof(int32_t); + if(bytestotal > maxbytes) return 0; + memcpy(&size, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + } + if (size > (1<<16)) { + return 0; + } + char *bitmapOfRunContainers = NULL; + bool hasrun = (cookie & 0xFFFF) == SERIAL_COOKIE; + if (hasrun) { + int32_t s = (size + 7) / 8; + bytestotal += s; + if(bytestotal > maxbytes) return 0; + bitmapOfRunContainers = (char *)buf; + buf += s; + } + bytestotal += size * 2 * sizeof(uint16_t); + if(bytestotal > maxbytes) return 0; + uint16_t *keyscards = (uint16_t *)buf; + buf += size * 2 * sizeof(uint16_t); + if ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) { + // skipping the offsets + bytestotal += size * 4; + if(bytestotal > maxbytes) return 0; + buf += size * 4; + } + // Reading the containers + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k+1, sizeof(tmp)); + uint32_t thiscard = tmp + 1; + bool isbitmap = (thiscard > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + if (isbitmap) { + size_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } else if (isrun) { + bytestotal += sizeof(uint16_t); + if(bytestotal > maxbytes) return 0; + uint16_t n_runs; + memcpy(&n_runs, buf, sizeof(uint16_t)); + buf += sizeof(uint16_t); + size_t containersize = n_runs * sizeof(rle16_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } else { + size_t containersize = thiscard * sizeof(uint16_t); + bytestotal += containersize; + if(bytestotal > maxbytes) return 0; + buf += containersize; + } + } + return bytestotal; +} + +// This function populates answer from the content of buf (reading up to maxbytes bytes). +// The function returns false if a properly serialized bitmap cannot be found. +// If it returns true, readbytes is populated by how many bytes were read, we have that *readbytes <= maxbytes. +// +// This function is endian-sensitive. +bool ra_portable_deserialize(roaring_array_t *answer, const char *buf, const size_t maxbytes, size_t * readbytes) { + *readbytes = sizeof(int32_t);// for cookie + if(*readbytes > maxbytes) { + // Ran out of bytes while reading first 4 bytes. + return false; + } + uint32_t cookie; + memcpy(&cookie, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + if ((cookie & 0xFFFF) != SERIAL_COOKIE && + cookie != SERIAL_COOKIE_NO_RUNCONTAINER) { + // "I failed to find one of the right cookies. + return false; + } + int32_t size; + + if ((cookie & 0xFFFF) == SERIAL_COOKIE) + size = (cookie >> 16) + 1; + else { + *readbytes += sizeof(int32_t); + if(*readbytes > maxbytes) { + // Ran out of bytes while reading second part of the cookie. + return false; + } + memcpy(&size, buf, sizeof(int32_t)); + buf += sizeof(uint32_t); + } + if (size < 0) { + // You cannot have a negative number of containers, the data must be corrupted. + return false; + } + if (size > (1<<16)) { + // You cannot have so many containers, the data must be corrupted. + return false; + } + const char *bitmapOfRunContainers = NULL; + bool hasrun = (cookie & 0xFFFF) == SERIAL_COOKIE; + if (hasrun) { + int32_t s = (size + 7) / 8; + *readbytes += s; + if(*readbytes > maxbytes) {// data is corrupted? + // Ran out of bytes while reading run bitmap. + return false; + } + bitmapOfRunContainers = buf; + buf += s; + } + uint16_t *keyscards = (uint16_t *)buf; + + *readbytes += size * 2 * sizeof(uint16_t); + if(*readbytes > maxbytes) { + // Ran out of bytes while reading key-cardinality array. + return false; + } + buf += size * 2 * sizeof(uint16_t); + + bool is_ok = ra_init_with_capacity(answer, size); + if (!is_ok) { + // Failed to allocate memory for roaring array. Bailing out. + return false; + } + + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k, sizeof(tmp)); + answer->keys[k] = tmp; + } + if ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) { + *readbytes += size * 4; + if(*readbytes > maxbytes) {// data is corrupted? + // Ran out of bytes while reading offsets. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + + // skipping the offsets + buf += size * 4; + } + // Reading the containers + for (int32_t k = 0; k < size; ++k) { + uint16_t tmp; + memcpy(&tmp, keyscards + 2*k+1, sizeof(tmp)); + uint32_t thiscard = tmp + 1; + bool isbitmap = (thiscard > DEFAULT_MAX_SIZE); + bool isrun = false; + if(hasrun) { + if((bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) { + isbitmap = false; + isrun = true; + } + } + if (isbitmap) { + // we check that the read is allowed + size_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t); + *readbytes += containersize; + if(*readbytes > maxbytes) { + // Running out of bytes while reading a bitset container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + bitset_container_t *c = bitset_container_create(); + if(c == NULL) {// memory allocation failure + // Failed to allocate memory for a bitset container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += bitset_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = BITSET_CONTAINER_TYPE; + } else if (isrun) { + // we check that the read is allowed + *readbytes += sizeof(uint16_t); + if(*readbytes > maxbytes) { + // Running out of bytes while reading a run container (header). + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + uint16_t n_runs; + memcpy(&n_runs, buf, sizeof(uint16_t)); + size_t containersize = n_runs * sizeof(rle16_t); + *readbytes += containersize; + if(*readbytes > maxbytes) {// data is corrupted? + // Running out of bytes while reading a run container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + + run_container_t *c = run_container_create(); + if(c == NULL) {// memory allocation failure + // Failed to allocate memory for a run container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += run_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = RUN_CONTAINER_TYPE; + } else { + // we check that the read is allowed + size_t containersize = thiscard * sizeof(uint16_t); + *readbytes += containersize; + if(*readbytes > maxbytes) {// data is corrupted? + // Running out of bytes while reading an array container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + // it is now safe to read + array_container_t *c = + array_container_create_given_capacity(thiscard); + if(c == NULL) {// memory allocation failure + // Failed to allocate memory for an array container. + ra_clear(answer);// we need to clear the containers already allocated, and the roaring array + return false; + } + answer->size++; + buf += array_container_read(thiscard, c, buf); + answer->containers[k] = c; + answer->typecodes[k] = ARRAY_CONTAINER_TYPE; + } + } + return true; +} + +}} +/* end file src/roaring_array.c */ +/* begin file src/roaring_priority_queue.c */ + + +namespace paimon::roaring { namespace internal { +struct roaring_pq_element_s { + uint64_t size; + bool is_temporary; + roaring_bitmap_t *bitmap; +}; + +typedef struct roaring_pq_element_s roaring_pq_element_t; + +struct roaring_pq_s { + roaring_pq_element_t *elements; + uint64_t size; +}; + +typedef struct roaring_pq_s roaring_pq_t; + +static inline bool compare(roaring_pq_element_t *t1, roaring_pq_element_t *t2) { + return t1->size < t2->size; +} + +static void pq_add(roaring_pq_t *pq, roaring_pq_element_t *t) { + uint64_t i = pq->size; + pq->elements[pq->size++] = *t; + while (i > 0) { + uint64_t p = (i - 1) >> 1; + roaring_pq_element_t ap = pq->elements[p]; + if (!compare(t, &ap)) break; + pq->elements[i] = ap; + i = p; + } + pq->elements[i] = *t; +} + +static void pq_free(roaring_pq_t *pq) { + roaring_free(pq); +} + +static void percolate_down(roaring_pq_t *pq, uint32_t i) { + uint32_t size = (uint32_t)pq->size; + uint32_t hsize = size >> 1; + roaring_pq_element_t ai = pq->elements[i]; + while (i < hsize) { + uint32_t l = (i << 1) + 1; + uint32_t r = l + 1; + roaring_pq_element_t bestc = pq->elements[l]; + if (r < size) { + if (compare(pq->elements + r, &bestc)) { + l = r; + bestc = pq->elements[r]; + } + } + if (!compare(&bestc, &ai)) { + break; + } + pq->elements[i] = bestc; + i = l; + } + pq->elements[i] = ai; +} + +static roaring_pq_t *create_pq(const roaring_bitmap_t **arr, uint32_t length) { + size_t alloc_size = sizeof(roaring_pq_t) + sizeof(roaring_pq_element_t) * length; + roaring_pq_t *answer = (roaring_pq_t *)roaring_malloc(alloc_size); + answer->elements = (roaring_pq_element_t *)(answer + 1); + answer->size = length; + for (uint32_t i = 0; i < length; i++) { + answer->elements[i].bitmap = (roaring_bitmap_t *)arr[i]; + answer->elements[i].is_temporary = false; + answer->elements[i].size = + roaring_bitmap_portable_size_in_bytes(arr[i]); + } + for (int32_t i = (length >> 1); i >= 0; i--) { + percolate_down(answer, i); + } + return answer; +} + +static roaring_pq_element_t pq_poll(roaring_pq_t *pq) { + roaring_pq_element_t ans = *pq->elements; + if (pq->size > 1) { + pq->elements[0] = pq->elements[--pq->size]; + percolate_down(pq, 0); + } else + --pq->size; + // memmove(pq->elements,pq->elements+1,(pq->size-1)*sizeof(roaring_pq_element_t));--pq->size; + return ans; +} + +// this function consumes and frees the inputs +static roaring_bitmap_t *lazy_or_from_lazy_inputs(roaring_bitmap_t *x1, + roaring_bitmap_t *x2) { + uint8_t result_type = 0; + const int length1 = ra_get_size(&x1->high_low_container), + length2 = ra_get_size(&x2->high_low_container); + if (0 == length1) { + roaring_bitmap_free(x1); + return x2; + } + if (0 == length2) { + roaring_bitmap_free(x2); + return x1; + } + uint32_t neededcap = length1 > length2 ? length2 : length1; + roaring_bitmap_t *answer = roaring_bitmap_create_with_capacity(neededcap); + int pos1 = 0, pos2 = 0; + uint8_t type1, type2; + uint16_t s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + uint16_t s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + while (true) { + if (s1 == s2) { + // todo: unsharing can be inefficient as it may create a clone where + // none + // is needed, but it has the benefit of being easy to reason about. + + ra_unshare_container_at_index(&x1->high_low_container, (uint16_t)pos1); + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + assert(type1 != SHARED_CONTAINER_TYPE); + + ra_unshare_container_at_index(&x2->high_low_container, (uint16_t)pos2); + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + assert(type2 != SHARED_CONTAINER_TYPE); + + container_t *c; + + if ((type2 == BITSET_CONTAINER_TYPE) && + (type1 != BITSET_CONTAINER_TYPE) + ){ + c = container_lazy_ior(c2, type2, c1, type1, &result_type); + container_free(c1, type1); + if (c != c2) { + container_free(c2, type2); + } + } else { + c = container_lazy_ior(c1, type1, c2, type2, &result_type); + container_free(c2, type2); + if (c != c1) { + container_free(c1, type1); + } + } + // since we assume that the initial containers are non-empty, the + // result here + // can only be non-empty + ra_append(&answer->high_low_container, s1, c, result_type); + ++pos1; + ++pos2; + if (pos1 == length1) break; + if (pos2 == length2) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + + } else if (s1 < s2) { // s1 < s2 + container_t *c1 = ra_get_container_at_index( + &x1->high_low_container, (uint16_t)pos1, &type1); + ra_append(&answer->high_low_container, s1, c1, type1); + pos1++; + if (pos1 == length1) break; + s1 = ra_get_key_at_index(&x1->high_low_container, (uint16_t)pos1); + + } else { // s1 > s2 + container_t *c2 = ra_get_container_at_index( + &x2->high_low_container, (uint16_t)pos2, &type2); + ra_append(&answer->high_low_container, s2, c2, type2); + pos2++; + if (pos2 == length2) break; + s2 = ra_get_key_at_index(&x2->high_low_container, (uint16_t)pos2); + } + } + if (pos1 == length1) { + ra_append_move_range(&answer->high_low_container, + &x2->high_low_container, pos2, length2); + } else if (pos2 == length2) { + ra_append_move_range(&answer->high_low_container, + &x1->high_low_container, pos1, length1); + } + ra_clear_without_containers(&x1->high_low_container); + ra_clear_without_containers(&x2->high_low_container); + roaring_free(x1); + roaring_free(x2); + return answer; +} + +/** + * Compute the union of 'number' bitmaps using a heap. This can + * sometimes be faster than roaring_bitmap_or_many which uses + * a naive algorithm. Caller is responsible for freeing the + * result. + */ +roaring_bitmap_t *roaring_bitmap_or_many_heap(uint32_t number, + const roaring_bitmap_t **x) { + if (number == 0) { + return roaring_bitmap_create(); + } + if (number == 1) { + return roaring_bitmap_copy(x[0]); + } + roaring_pq_t *pq = create_pq(x, number); + while (pq->size > 1) { + roaring_pq_element_t x1 = pq_poll(pq); + roaring_pq_element_t x2 = pq_poll(pq); + + if (x1.is_temporary && x2.is_temporary) { + roaring_bitmap_t *newb = + lazy_or_from_lazy_inputs(x1.bitmap, x2.bitmap); + // should normally return a fresh new bitmap *except* that + // it can return x1.bitmap or x2.bitmap in degenerate cases + bool temporary = !((newb == x1.bitmap) && (newb == x2.bitmap)); + uint64_t bsize = roaring_bitmap_portable_size_in_bytes(newb); + roaring_pq_element_t newelement = { + .size = bsize, .is_temporary = temporary, .bitmap = newb}; + pq_add(pq, &newelement); + } else if (x2.is_temporary) { + roaring_bitmap_lazy_or_inplace(x2.bitmap, x1.bitmap, false); + x2.size = roaring_bitmap_portable_size_in_bytes(x2.bitmap); + pq_add(pq, &x2); + } else if (x1.is_temporary) { + roaring_bitmap_lazy_or_inplace(x1.bitmap, x2.bitmap, false); + x1.size = roaring_bitmap_portable_size_in_bytes(x1.bitmap); + + pq_add(pq, &x1); + } else { + roaring_bitmap_t *newb = + roaring_bitmap_lazy_or(x1.bitmap, x2.bitmap, false); + uint64_t bsize = roaring_bitmap_portable_size_in_bytes(newb); + roaring_pq_element_t newelement = { + .size = bsize, .is_temporary = true, .bitmap = newb}; + + pq_add(pq, &newelement); + } + } + roaring_pq_element_t X = pq_poll(pq); + roaring_bitmap_t *answer = X.bitmap; + roaring_bitmap_repair_after_lazy(answer); + pq_free(pq); + return answer; +} + +}} +/* end file src/roaring_priority_queue.c */ diff --git a/third_party/roaring_bitmap/roaring.h b/third_party/roaring_bitmap/roaring.h new file mode 100644 index 0000000..2a9bf3b --- /dev/null +++ b/third_party/roaring_bitmap/roaring.h @@ -0,0 +1,1943 @@ +// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!! +// Created by amalgamation.sh on 2023-12-16T02:52:00Z + +/* + * The CRoaring project is under a dual license (Apache/MIT). + * Users of the library may choose one or the other license. + */ +/* + * Copyright 2016-2022 The CRoaring authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * MIT License + * + * Copyright 2016-2022 The CRoaring authors + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without + * limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +/* begin file include/roaring/roaring_version.h */ +// /include/roaring/roaring_version.h automatically generated by release.py, do not change by hand +#ifndef ROARING_INCLUDE_ROARING_VERSION +#define ROARING_INCLUDE_ROARING_VERSION +#define ROARING_VERSION "2.1.0" +enum { ROARING_VERSION_MAJOR = 2, ROARING_VERSION_MINOR = 1, ROARING_VERSION_REVISION = 0 }; +#endif // ROARING_INCLUDE_ROARING_VERSION +/* end file include/roaring/roaring_version.h */ +/* begin file include/roaring/roaring_types.h */ +/* + Typedefs used by various components +*/ + +#ifndef ROARING_TYPES_H +#define ROARING_TYPES_H + +#include +#include + +namespace paimon::roaring { +namespace internal { + +/** + * When building .c files as C++, there's added compile-time checking if the + * container types are derived from a `container_t` base class. So long as + * such a base class is empty, the struct will behave compatibly with C structs + * despite the derivation. This is due to the Empty Base Class Optimization: + * + * https://en.cppreference.com/w/cpp/language/ebo + * + * But since C isn't namespaced, taking `container_t` globally might collide + * with other projects. So roaring.h uses ROARING_CONTAINER_T, while internal + * code #undefs that after declaring `typedef ROARING_CONTAINER_T container_t;` + */ +#if defined(__cplusplus) +struct container_s {}; +#define ROARING_CONTAINER_T ::paimon::roaring::internal::container_s +#else +#define ROARING_CONTAINER_T void // no compile-time checking +#endif + +#define ROARING_FLAG_COW UINT8_C(0x1) +#define ROARING_FLAG_FROZEN UINT8_C(0x2) + +/** + * Roaring arrays are array-based key-value pairs having containers as values + * and 16-bit integer keys. A roaring bitmap might be implemented as such. + */ + +// parallel arrays. Element sizes quite different. +// Alternative is array +// of structs. Which would have better +// cache performance through binary searches? + +typedef struct roaring_array_s { + int32_t size; + int32_t allocation_size; + ROARING_CONTAINER_T **containers; // Use container_t in non-API files! + uint16_t *keys; + uint8_t *typecodes; + uint8_t flags; +} roaring_array_t; + +typedef bool (*roaring_iterator)(uint32_t value, void *param); +typedef bool (*roaring_iterator64)(uint64_t value, void *param); + +/** + * (For advanced users.) + * The roaring_statistics_t can be used to collect detailed statistics about + * the composition of a roaring bitmap. + */ +typedef struct roaring_statistics_s { + uint32_t n_containers; /* number of containers */ + + uint32_t n_array_containers; /* number of array containers */ + uint32_t n_run_containers; /* number of run containers */ + uint32_t n_bitset_containers; /* number of bitmap containers */ + + uint32_t n_values_array_containers; /* number of values in array containers */ + uint32_t n_values_run_containers; /* number of values in run containers */ + uint32_t n_values_bitset_containers; /* number of values in bitmap containers */ + + uint32_t n_bytes_array_containers; /* number of allocated bytes in array + containers */ + uint32_t n_bytes_run_containers; /* number of allocated bytes in run + containers */ + uint32_t n_bytes_bitset_containers; /* number of allocated bytes in bitmap + containers */ + + uint32_t max_value; /* the maximal value, undefined if cardinality is zero */ + uint32_t min_value; /* the minimal value, undefined if cardinality is zero */ + uint64_t sum_value; /* the sum of all values (could be used to compute + average) */ + + uint64_t cardinality; /* total number of values stored in the bitmap */ + + // and n_values_arrays, n_values_rle, n_values_bitmap +} roaring_statistics_t; + +} // namespace internal +} // namespace paimon::roaring + +#endif /* ROARING_TYPES_H */ +/* end file include/roaring/roaring_types.h */ +/* begin file include/roaring/portability.h */ +/* + * portability.h + * + */ + +/** + * All macros should be prefixed with either CROARING or ROARING. + * The library uses both ROARING_... + * as well as CROAIRING_ as prefixes. The ROARING_ prefix is for + * macros that are provided by the build system or that are closely + * related to the format. The header macros may also use ROARING_. + * The CROARING_ prefix is for internal macros that a user is unlikely + * to ever interact with. + */ + +#ifndef INCLUDE_PORTABILITY_H_ +#define INCLUDE_PORTABILITY_H_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif // _GNU_SOURCE +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS 1 +#endif // __STDC_FORMAT_MACROS + +#ifdef _MSC_VER +#define CROARING_VISUAL_STUDIO 1 +/** + * We want to differentiate carefully between + * clang under visual studio and regular visual + * studio. + */ +#ifdef __clang__ +// clang under visual studio +#define CROARING_CLANG_VISUAL_STUDIO 1 +#else +// just regular visual studio (best guess) +#define CROARING_REGULAR_VISUAL_STUDIO 1 +#endif // __clang__ +#endif // _MSC_VER +#ifndef CROARING_VISUAL_STUDIO +#define CROARING_VISUAL_STUDIO 0 +#endif +#ifndef CROARING_CLANG_VISUAL_STUDIO +#define CROARING_CLANG_VISUAL_STUDIO 0 +#endif +#ifndef CROARING_REGULAR_VISUAL_STUDIO +#define CROARING_REGULAR_VISUAL_STUDIO 0 +#endif + +#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L) +#undef _POSIX_C_SOURCE +#endif + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif // !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L) +#if !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700) +#define _XOPEN_SOURCE 700 +#endif // !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700) + +#ifdef __illumos__ +#define __EXTENSIONS__ +#endif + +#include +#include +#include // will provide posix_memalign with _POSIX_C_SOURCE as defined above +#ifdef __GLIBC__ +#include // this should never be needed but there are some reports that it is needed. +#endif + +#if defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ != 8 +#error This code assumes 64-bit long longs (by use of the GCC intrinsics). Your system is not currently supported. +#endif + +#if CROARING_REGULAR_VISUAL_STUDIO +#ifndef __restrict__ +#define __restrict__ __restrict +#endif // __restrict__ +#endif // CROARING_REGULAR_VISUAL_STUDIO + +#if defined(__x86_64__) || defined(_M_X64) +// we have an x64 processor +#define CROARING_IS_X64 1 + +#if defined(_MSC_VER) && (_MSC_VER < 1910) +// Old visual studio systems won't support AVX2 well. +#undef CROARING_IS_X64 +#endif + +#if defined(__clang_major__) && (__clang_major__ <= 8) && !defined(__AVX2__) +// Older versions of clang have a bug affecting us +// https://stackoverflow.com/questions/57228537/how-does-one-use-pragma-clang-attribute-push-with-c-namespaces +#undef CROARING_IS_X64 +#endif + +#ifdef ROARING_DISABLE_X64 +#undef CROARING_IS_X64 +#endif +// we include the intrinsic header +#if !CROARING_REGULAR_VISUAL_STUDIO +/* Non-Microsoft C/C++-compatible compiler */ +#include // on some recent GCC, this will declare posix_memalign + +#if CROARING_CLANG_VISUAL_STUDIO + +/** + * You are not supposed, normally, to include these + * headers directly. Instead you should either include intrin.h + * or x86intrin.h. However, when compiling with clang + * under Windows (i.e., when _MSC_VER is set), these headers + * only get included *if* the corresponding features are detected + * from macros: + * e.g., if __AVX2__ is set... in turn, we normally set these + * macros by compiling against the corresponding architecture + * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole + * software with these advanced instructions. These headers would + * normally guard against such usage, but we carefully included + * (or ) before, so the headers + * are fooled. + */ +#include +#include +#include // for _blsr_u64 +#include // for most things (AVX2, AVX512, _popcnt64) +#include // for __lzcnt64 +#include +#include +#include +#if _MSC_VER >= 1920 +// Important: we need the AVX-512 headers: +#include +#include +#include +#include +#include +#include +#include +#include +#endif // _MSC_VER >= 1920 +// unfortunately, we may not get _blsr_u64, but, thankfully, clang +// has it as a macro. +#ifndef _blsr_u64 +// we roll our own +#define _blsr_u64(n) ((n - 1) & n) +#endif // _blsr_u64 +#endif // SIMDJSON_CLANG_VISUAL_STUDIO + +#endif // CROARING_REGULAR_VISUAL_STUDIO +#endif // defined(__x86_64__) || defined(_M_X64) + +#if !defined(CROARING_USENEON) && !defined(DISABLENEON) && defined(__ARM_NEON) +#define CROARING_USENEON +#endif +#if defined(CROARING_USENEON) +#include +#endif + +#if !CROARING_REGULAR_VISUAL_STUDIO +/* Non-Microsoft C/C++-compatible compiler, assumes that it supports inline + * assembly */ +#define CROARING_INLINE_ASM 1 +#endif // _MSC_VER + +#if CROARING_REGULAR_VISUAL_STUDIO +/* Microsoft C/C++-compatible compiler */ +#include + +#ifndef __clang__ // if one compiles with MSVC *with* clang, then these + // intrinsics are defined!!! +#define CROARING_INTRINSICS 1 +// sadly there is no way to check whether we are missing these intrinsics +// specifically. + +/* wrappers for Visual Studio built-ins that look like gcc built-ins __builtin_ctzll */ +/* result might be undefined when input_num is zero */ +static inline int roaring_trailing_zeroes(unsigned long long input_num) { + unsigned long index; +#ifdef _WIN64 // highly recommended!!! + _BitScanForward64(&index, input_num); +#else // if we must support 32-bit Windows + if ((uint32_t)input_num != 0) { + _BitScanForward(&index, (uint32_t)input_num); + } else { + _BitScanForward(&index, (uint32_t)(input_num >> 32)); + index += 32; + } +#endif // _WIN64 + return index; +} + +/* wrappers for Visual Studio built-ins that look like gcc built-ins __builtin_clzll */ +/* result might be undefined when input_num is zero */ +static inline int roaring_leading_zeroes(unsigned long long input_num) { + unsigned long index; +#ifdef _WIN64 // highly recommended!!! + _BitScanReverse64(&index, input_num); +#else // if we must support 32-bit Windows + if (input_num > 0xFFFFFFFF) { + _BitScanReverse(&index, (uint32_t)(input_num >> 32)); + index += 32; + } else { + _BitScanReverse(&index, (uint32_t)(input_num)); + } +#endif // _WIN64 + return 63 - index; +} + +/* Use #define so this is effective even under /Ob0 (no inline) */ +#define roaring_unreachable __assume(0) +#endif // __clang__ + +#endif // CROARING_REGULAR_VISUAL_STUDIO + +#ifndef CROARING_INTRINSICS +#define CROARING_INTRINSICS 1 +#define roaring_unreachable __builtin_unreachable() +inline int roaring_trailing_zeroes(unsigned long long input_num) { + return __builtin_ctzll(input_num); +} +inline int roaring_leading_zeroes(unsigned long long input_num) { + return __builtin_clzll(input_num); +} +#endif + +#if CROARING_REGULAR_VISUAL_STUDIO +#define ALIGNED(x) __declspec(align(x)) +#elif defined(__GNUC__) || defined(__clang__) +#define ALIGNED(x) __attribute__((aligned(x))) +#else +#warning "Warning. Unrecognized compiler." +#define ALIGNED(x) +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define WARN_UNUSED __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED +#endif + +#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100) + +#ifdef CROARING_USENEON +// we can always compute the popcount fast. +#elif (defined(_M_ARM) || defined(_M_ARM64)) && \ + ((defined(_WIN64) || defined(_WIN32)) && defined(CROARING_REGULAR_VISUAL_STUDIO) && \ + CROARING_REGULAR_VISUAL_STUDIO) +// we will need this function: +static inline int roaring_hamming_backup(uint64_t x) { + uint64_t c1 = UINT64_C(0x5555555555555555); + uint64_t c2 = UINT64_C(0x3333333333333333); + uint64_t c4 = UINT64_C(0x0F0F0F0F0F0F0F0F); + x -= (x >> 1) & c1; + x = ((x >> 2) & c2) + (x & c2); + x = (x + (x >> 4)) & c4; + x *= UINT64_C(0x0101010101010101); + return x >> 56; +} +#endif + +static inline int roaring_hamming(uint64_t x) { +#if defined(_WIN64) && defined(CROARING_REGULAR_VISUAL_STUDIO) && CROARING_REGULAR_VISUAL_STUDIO +#ifdef CROARING_USENEON + return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); +#elif defined(_M_ARM64) + return roaring_hamming_backup(x); + // (int) _CountOneBits64(x); is unavailable +#else // _M_ARM64 + return (int)__popcnt64(x); +#endif // _M_ARM64 +#elif defined(_WIN32) && defined(CROARING_REGULAR_VISUAL_STUDIO) && CROARING_REGULAR_VISUAL_STUDIO +#ifdef _M_ARM + return roaring_hamming_backup(x); + // _CountOneBits is unavailable +#else // _M_ARM + return (int)__popcnt((unsigned int)x) + (int)__popcnt((unsigned int)(x >> 32)); +#endif // _M_ARM +#else + return __builtin_popcountll(x); +#endif +} + +#ifndef UINT64_C +#define UINT64_C(c) (c##ULL) +#endif // UINT64_C + +#ifndef UINT32_C +#define UINT32_C(c) (c##UL) +#endif // UINT32_C + +// this is almost standard? +#undef STRINGIFY_IMPLEMENTATION_ +#undef STRINGIFY +#define STRINGIFY_IMPLEMENTATION_(a) #a +#define STRINGIFY(a) STRINGIFY_IMPLEMENTATION_(a) + +// Our fast kernels require 64-bit systems. +// +// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions. +// Furthermore, the number of SIMD registers is reduced. +// +// On 32-bit ARM, we would have smaller registers. +// +// The library should still have the fallback kernel. It is +// slower, but it should run everywhere. + +// +// Enable valid runtime implementations, and select CROARING_BUILTIN_IMPLEMENTATION +// + +// We are going to use runtime dispatch. +#if CROARING_IS_X64 +#ifdef __clang__ +// clang does not have GCC push pop +// warning: clang attribute push can't be used within a namespace in clang up +// til 8.0 so CROARING_TARGET_REGION and CROARING_UNTARGET_REGION must be *outside* of a +// namespace. +#define CROARING_TARGET_REGION(T) \ + _Pragma(STRINGIFY(clang attribute push(__attribute__((target(T))), apply_to = function))) +#define CROARING_UNTARGET_REGION _Pragma("clang attribute pop") +#elif defined(__GNUC__) +// GCC is easier +#define CROARING_TARGET_REGION(T) _Pragma("GCC push_options") _Pragma(STRINGIFY(GCC target(T))) +#define CROARING_UNTARGET_REGION _Pragma("GCC pop_options") +#endif // clang then gcc + +#endif // CROARING_IS_X64 + +// Default target region macros don't do anything. +#ifndef CROARING_TARGET_REGION +#define CROARING_TARGET_REGION(T) +#define CROARING_UNTARGET_REGION +#endif + +#define CROARING_TARGET_AVX2 CROARING_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") +#define CROARING_TARGET_AVX512 \ + CROARING_TARGET_REGION( \ + "avx2,bmi,bmi2,pclmul,lzcnt,popcnt,avx512f,avx512dq,avx512bw,avx512vbmi2,avx512bitalg," \ + "avx512vpopcntdq") +#define CROARING_UNTARGET_AVX2 CROARING_UNTARGET_REGION +#define CROARING_UNTARGET_AVX512 CROARING_UNTARGET_REGION + +#ifdef __AVX2__ +// No need for runtime dispatching. +// It is unnecessary and harmful to old clang to tag regions. +#undef CROARING_TARGET_AVX2 +#define CROARING_TARGET_AVX2 +#undef CROARING_UNTARGET_AVX2 +#define CROARING_UNTARGET_AVX2 +#endif + +#if defined(__AVX512F__) && defined(__AVX512DQ__) && defined(__AVX512BW__) && \ + defined(__AVX512VBMI2__) && defined(__AVX512BITALG__) && defined(__AVX512VPOPCNTDQ__) +// No need for runtime dispatching. +// It is unnecessary and harmful to old clang to tag regions. +#undef CROARING_TARGET_AVX512 +#define CROARING_TARGET_AVX512 +#undef CROARING_UNTARGET_AVX512 +#define CROARING_UNTARGET_AVX512 +#endif + +// Allow unaligned memory access +#if defined(__GNUC__) || defined(__clang__) +#define ALLOW_UNALIGNED __attribute__((no_sanitize("alignment"))) +#else +#define ALLOW_UNALIGNED +#endif + +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) +#define CROARING_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined(_WIN32) +#define CROARING_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || \ + defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#include +#elif defined(sun) || defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__) +#include +#else // defined(__APPLE__) || defined(__FreeBSD__) + +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#endif //__has_include + +#endif // defined(__APPLE__) || defined(__FreeBSD__) + +#ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) +#define CROARING_IS_BIG_ENDIAN 0 +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define CROARING_IS_BIG_ENDIAN 0 +#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define CROARING_IS_BIG_ENDIAN 1 +#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#endif + +// Defines for the possible CROARING atomic implementations +#define CROARING_ATOMIC_IMPL_NONE 1 +#define CROARING_ATOMIC_IMPL_CPP 2 +#define CROARING_ATOMIC_IMPL_C 3 +#define CROARING_ATOMIC_IMPL_C_WINDOWS 4 + +// If the use has forced a specific implementation, use that, otherwise, +// figure out the best implementation we can use. +#if !defined(CROARING_ATOMIC_IMPL) +#if defined(__cplusplus) && __cplusplus >= 201103L +#ifdef __has_include +#if __has_include() +#define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_CPP +#endif //__has_include() +#else +// We lack __has_include to check: +#define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_CPP +#endif //__has_include +#elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) +#define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_C +#elif CROARING_REGULAR_VISUAL_STUDIO +// https://www.technetworkhub.com/c11-atomics-in-visual-studio-2022-version-17/ +#define CROARING_ATOMIC_IMPL CROARING_ATOMIC_IMPL_C_WINDOWS +#endif +#endif // !defined(CROARING_ATOMIC_IMPL) + +#if CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C +#include +typedef _Atomic(uint32_t) croaring_refcount_t; + +static inline void croaring_refcount_inc(croaring_refcount_t *val) { + // Increasing the reference counter can always be done with + // memory_order_relaxed: New references to an object can only be formed from + // an existing reference, and passing an existing reference from one thread to + // another must already provide any required synchronization. + atomic_fetch_add_explicit(val, 1, memory_order_relaxed); +} + +static inline bool croaring_refcount_dec(croaring_refcount_t *val) { + // It is important to enforce any possible access to the object in one thread + // (through an existing reference) to happen before deleting the object in a + // different thread. This is achieved by a "release" operation after dropping + // a reference (any access to the object through this reference must obviously + // happened before), and an "acquire" operation before deleting the object. + bool is_zero = atomic_fetch_sub_explicit(val, 1, memory_order_release) == 1; + if (is_zero) { + atomic_thread_fence(memory_order_acquire); + } + return is_zero; +} + +static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) { + return atomic_load_explicit(val, memory_order_relaxed); +} +#elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_CPP +#include +typedef std::atomic croaring_refcount_t; + +static inline void croaring_refcount_inc(croaring_refcount_t *val) { + val->fetch_add(1, std::memory_order_relaxed); +} + +static inline bool croaring_refcount_dec(croaring_refcount_t *val) { + // See above comments on the c11 atomic implementation for memory ordering + bool is_zero = val->fetch_sub(1, std::memory_order_release) == 1; + if (is_zero) { + std::atomic_thread_fence(std::memory_order_acquire); + } + return is_zero; +} + +static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) { + return val->load(std::memory_order_relaxed); +} +#elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_C_WINDOWS +#include +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedDecrement) + +// _InterlockedIncrement and _InterlockedDecrement take a (signed) long, and +// overflow is defined to wrap, so we can pretend it is a uint32_t for our case +typedef volatile long croaring_refcount_t; + +static inline void croaring_refcount_inc(croaring_refcount_t *val) { + _InterlockedIncrement(val); +} + +static inline bool croaring_refcount_dec(croaring_refcount_t *val) { + return _InterlockedDecrement(val) == 0; +} + +static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) { + // Per https://learn.microsoft.com/en-us/windows/win32/sync/interlocked-variable-access + // > Simple reads and writes to properly-aligned 32-bit variables are atomic + // > operations. In other words, you will not end up with only one portion + // > of the variable updated; all bits are updated in an atomic fashion. + return *val; +} +#elif CROARING_ATOMIC_IMPL == CROARING_ATOMIC_IMPL_NONE +#include +typedef uint32_t croaring_refcount_t; + +static inline void croaring_refcount_inc(croaring_refcount_t *val) { + *val += 1; +} + +static inline bool croaring_refcount_dec(croaring_refcount_t *val) { + assert(*val > 0); + *val -= 1; + return val == 0; +} + +static inline uint32_t croaring_refcount_get(const croaring_refcount_t *val) { + return *val; +} +#else +#error "Unknown atomic implementation" +#endif + +// We need portability.h to be included first, +// but we also always want isadetection.h to be +// included (right after). +// See https://github.com/RoaringBitmap/CRoaring/issues/394 +// There is no scenario where we want portability.h to +// be included, but not isadetection.h: the latter is a +// strict requirement. +#endif /* INCLUDE_PORTABILITY_H_ */ +/* end file include/roaring/portability.h */ +/* begin file include/roaring/bitset/bitset.h */ +#ifndef CBITSET_BITSET_H +#define CBITSET_BITSET_H + +// For compatibility with MSVC with the use of `restrict` +#if (__STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && defined(__STDC_VERSION__)) +#define CBITSET_RESTRICT restrict +#else +#define CBITSET_RESTRICT +#endif // (__STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && + // defined(__STDC_VERSION__ )) + +#include +#include +#include +#include +#include + +namespace paimon::roaring { +namespace internal { +struct bitset_s { + uint64_t *CBITSET_RESTRICT array; + /* For simplicity and performance, we prefer to have a size and a capacity that is a multiple of + * 64 bits. Thus we only track the size and the capacity in terms of 64-bit words allocated */ + size_t arraysize; + size_t capacity; +}; + +typedef struct bitset_s bitset_t; + +/* Create a new bitset. Return NULL in case of failure. */ +bitset_t *bitset_create(void); + +/* Create a new bitset able to contain size bits. Return NULL in case of + * failure. */ +bitset_t *bitset_create_with_capacity(size_t size); + +/* Free memory. */ +void bitset_free(bitset_t *bitset); + +/* Set all bits to zero. */ +void bitset_clear(bitset_t *bitset); + +/* Set all bits to one. */ +void bitset_fill(bitset_t *bitset); + +/* Create a copy */ +bitset_t *bitset_copy(const bitset_t *bitset); + +/* For advanced users: Resize the bitset so that it can support newarraysize * 64 bits. + * Return true in case of success, false for failure. Pad + * with zeroes new buffer areas if requested. */ +bool bitset_resize(bitset_t *bitset, size_t newarraysize, bool padwithzeroes); + +/* returns how many bytes of memory the backend buffer uses */ +inline size_t bitset_size_in_bytes(const bitset_t *bitset) { + return bitset->arraysize * sizeof(uint64_t); +} + +/* returns how many bits can be accessed */ +inline size_t bitset_size_in_bits(const bitset_t *bitset) { + return bitset->arraysize * 64; +} + +/* returns how many words (64-bit) of memory the backend buffer uses */ +inline size_t bitset_size_in_words(const bitset_t *bitset) { + return bitset->arraysize; +} + +/* For advanced users: Grow the bitset so that it can support newarraysize * 64 bits with padding. + * Return true in case of success, false for failure. */ +bool bitset_grow(bitset_t *bitset, size_t newarraysize); + +/* attempts to recover unused memory, return false in case of roaring_reallocation + * failure */ +bool bitset_trim(bitset_t *bitset); + +/* shifts all bits by 's' positions so that the bitset representing values + * 1,2,10 would represent values 1+s, 2+s, 10+s */ +void bitset_shift_left(bitset_t *bitset, size_t s); + +/* shifts all bits by 's' positions so that the bitset representing values + * 1,2,10 would represent values 1-s, 2-s, 10-s, negative values are deleted */ +void bitset_shift_right(bitset_t *bitset, size_t s); + +/* Set the ith bit. Attempts to resize the bitset if needed (may silently fail) + */ +inline void bitset_set(bitset_t *bitset, size_t i) { + size_t shiftedi = i / 64; + if (shiftedi >= bitset->arraysize) { + if (!bitset_grow(bitset, shiftedi + 1)) { + return; + } + } + bitset->array[shiftedi] |= ((uint64_t)1) << (i % 64); +} + +/* Set the ith bit to the specified value. Attempts to resize the bitset if + * needed (may silently fail) */ +inline void bitset_set_to_value(bitset_t *bitset, size_t i, bool flag) { + size_t shiftedi = i / 64; + uint64_t mask = ((uint64_t)1) << (i % 64); + uint64_t dynmask = ((uint64_t)flag) << (i % 64); + if (shiftedi >= bitset->arraysize) { + if (!bitset_grow(bitset, shiftedi + 1)) { + return; + } + } + uint64_t w = bitset->array[shiftedi]; + w &= ~mask; + w |= dynmask; + bitset->array[shiftedi] = w; +} + +/* Get the value of the ith bit. */ +inline bool bitset_get(const bitset_t *bitset, size_t i) { + size_t shiftedi = i / 64; + if (shiftedi >= bitset->arraysize) { + return false; + } + return (bitset->array[shiftedi] & (((uint64_t)1) << (i % 64))) != 0; +} + +/* Count number of bits set. */ +size_t bitset_count(const bitset_t *bitset); + +/* Find the index of the first bit set. Or zero if the bitset is empty. */ +size_t bitset_minimum(const bitset_t *bitset); + +/* Find the index of the last bit set. Or zero if the bitset is empty. */ +size_t bitset_maximum(const bitset_t *bitset); + +/* compute the union in-place (to b1), returns true if successful, to generate a + * new bitset first call bitset_copy */ +bool bitset_inplace_union(bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* report the size of the union (without materializing it) */ +size_t bitset_union_count(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* compute the intersection in-place (to b1), to generate a new bitset first + * call bitset_copy */ +void bitset_inplace_intersection(bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2); + +/* report the size of the intersection (without materializing it) */ +size_t bitset_intersection_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2); + +/* returns true if the bitsets contain no common elements */ +bool bitsets_disjoint(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* returns true if the bitsets contain any common elements */ +bool bitsets_intersect(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* returns true if b1 contains all of the set bits of b2 */ +bool bitset_contains_all(const bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* compute the difference in-place (to b1), to generate a new bitset first call + * bitset_copy */ +void bitset_inplace_difference(bitset_t *CBITSET_RESTRICT b1, const bitset_t *CBITSET_RESTRICT b2); + +/* compute the size of the difference */ +size_t bitset_difference_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2); + +/* compute the symmetric difference in-place (to b1), return true if successful, + * to generate a new bitset first call bitset_copy */ +bool bitset_inplace_symmetric_difference(bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2); + +/* compute the size of the symmetric difference */ +size_t bitset_symmetric_difference_count(const bitset_t *CBITSET_RESTRICT b1, + const bitset_t *CBITSET_RESTRICT b2); + +/* iterate over the set bits + like so : + for(size_t i = 0; bitset_next_set_bit(b,&i) ; i++) { + //..... + } + */ +inline bool bitset_next_set_bit(const bitset_t *bitset, size_t *i) { + size_t x = *i / 64; + if (x >= bitset->arraysize) { + return false; + } + uint64_t w = bitset->array[x]; + w >>= (*i & 63); + if (w != 0) { + *i += roaring_trailing_zeroes(w); + return true; + } + x++; + while (x < bitset->arraysize) { + w = bitset->array[x]; + if (w != 0) { + *i = x * 64 + roaring_trailing_zeroes(w); + return true; + } + x++; + } + return false; +} + +/* iterate over the set bits + like so : + size_t buffer[256]; + size_t howmany = 0; + for(size_t startfrom = 0; (howmany = bitset_next_set_bits(b,buffer,256, &startfrom)) > + 0 ; startfrom++) { + //..... + } + */ +inline size_t bitset_next_set_bits(const bitset_t *bitset, size_t *buffer, size_t capacity, + size_t *startfrom) { + if (capacity == 0) return 0; // sanity check + size_t x = *startfrom / 64; + if (x >= bitset->arraysize) { + return 0; // nothing more to iterate over + } + uint64_t w = bitset->array[x]; + w >>= (*startfrom & 63); + size_t howmany = 0; + size_t base = x << 6; + while (howmany < capacity) { + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + buffer[howmany++] = r + base; + if (howmany == capacity) goto end; + w ^= t; + } + x += 1; + if (x == bitset->arraysize) { + break; + } + base += 64; + w = bitset->array[x]; + } +end: + if (howmany > 0) { + *startfrom = buffer[howmany - 1]; + } + return howmany; +} + +typedef bool (*bitset_iterator)(size_t value, void *param); + +// return true if uninterrupted +inline bool bitset_for_each(const bitset_t *b, bitset_iterator iterator, void *ptr) { + size_t base = 0; + for (size_t i = 0; i < b->arraysize; ++i) { + uint64_t w = b->array[i]; + while (w != 0) { + uint64_t t = w & (~w + 1); + int r = roaring_trailing_zeroes(w); + if (!iterator(r + base, ptr)) return false; + w ^= t; + } + base += 64; + } + return true; +} + +inline void bitset_print(const bitset_t *b) { + printf("{"); + for (size_t i = 0; bitset_next_set_bit(b, &i); i++) { + printf("%zu, ", i); + } + printf("}"); +} + +} // namespace internal +} // namespace paimon::roaring + +#endif +/* end file include/roaring/bitset/bitset.h */ +/* begin file include/roaring/roaring.h */ +/* + * An implementation of Roaring Bitmaps in C. + */ + +#ifndef ROARING_H +#define ROARING_H + +#include +#include // for `size_t` +#include + +namespace paimon::roaring { +namespace internal { + +typedef struct roaring_bitmap_s { + roaring_array_t high_low_container; +} roaring_bitmap_t; + +/** + * Dynamically allocates a new bitmap (initially empty). + * Returns NULL if the allocation fails. + * Capacity is a performance hint for how many "containers" the data will need. + * Client is responsible for calling `roaring_bitmap_free()`. + */ +roaring_bitmap_t *roaring_bitmap_create_with_capacity(uint32_t cap); + +/** + * Dynamically allocates a new bitmap (initially empty). + * Returns NULL if the allocation fails. + * Client is responsible for calling `roaring_bitmap_free()`. + */ +inline roaring_bitmap_t *roaring_bitmap_create(void) { + return roaring_bitmap_create_with_capacity(0); +} + +/** + * Initialize a roaring bitmap structure in memory controlled by client. + * Capacity is a performance hint for how many "containers" the data will need. + * Can return false if auxiliary allocations fail when capacity greater than 0. + */ +bool roaring_bitmap_init_with_capacity(roaring_bitmap_t *r, uint32_t cap); + +/** + * Initialize a roaring bitmap structure in memory controlled by client. + * The bitmap will be in a "clear" state, with no auxiliary allocations. + * Since this performs no allocations, the function will not fail. + */ +inline void roaring_bitmap_init_cleared(roaring_bitmap_t *r) { + roaring_bitmap_init_with_capacity(r, 0); +} + +/** + * Add all the values between min (included) and max (excluded) that are at a + * distance k*step from min. + */ +roaring_bitmap_t *roaring_bitmap_from_range(uint64_t min, uint64_t max, uint32_t step); + +/** + * Creates a new bitmap from a pointer of uint32_t integers + */ +roaring_bitmap_t *roaring_bitmap_of_ptr(size_t n_args, const uint32_t *vals); + +/* + * Whether you want to use copy-on-write. + * Saves memory and avoids copies, but needs more care in a threaded context. + * Most users should ignore this flag. + * + * Note: If you do turn this flag to 'true', enabling COW, then ensure that you + * do so for all of your bitmaps, since interactions between bitmaps with and + * without COW is unsafe. + */ +inline bool roaring_bitmap_get_copy_on_write(const roaring_bitmap_t *r) { + return r->high_low_container.flags & ROARING_FLAG_COW; +} +inline void roaring_bitmap_set_copy_on_write(roaring_bitmap_t *r, bool cow) { + if (cow) { + r->high_low_container.flags |= ROARING_FLAG_COW; + } else { + r->high_low_container.flags &= ~ROARING_FLAG_COW; + } +} + +roaring_bitmap_t *roaring_bitmap_add_offset(const roaring_bitmap_t *bm, int64_t offset); +/** + * Describe the inner structure of the bitmap. + */ +void roaring_bitmap_printf_describe(const roaring_bitmap_t *r); + +/** + * Creates a new bitmap from a list of uint32_t integers + */ +roaring_bitmap_t *roaring_bitmap_of(size_t n, ...); + +/** + * Copies a bitmap (this does memory allocation). + * The caller is responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_copy(const roaring_bitmap_t *r); + +/** + * Copies a bitmap from src to dest. It is assumed that the pointer dest + * is to an already allocated bitmap. The content of the dest bitmap is + * freed/deleted. + * + * It might be preferable and simpler to call roaring_bitmap_copy except + * that roaring_bitmap_overwrite can save on memory allocations. + * + * Returns true if successful, or false if there was an error. On failure, + * the dest bitmap is left in a valid, empty state (even if it was not empty before). + */ +bool roaring_bitmap_overwrite(roaring_bitmap_t *dest, const roaring_bitmap_t *src); + +/** + * Print the content of the bitmap. + */ +void roaring_bitmap_printf(const roaring_bitmap_t *r); + +/** + * Computes the intersection between two bitmaps and returns new bitmap. The + * caller is responsible for memory management. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + * You may also rely on roaring_bitmap_and_inplace to avoid creating + * many temporary bitmaps. + */ +roaring_bitmap_t *roaring_bitmap_and(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Computes the size of the intersection between two bitmaps. + */ +uint64_t roaring_bitmap_and_cardinality(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Check whether two bitmaps intersect. + */ +bool roaring_bitmap_intersect(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Check whether a bitmap and a closed range intersect. + */ +bool roaring_bitmap_intersect_with_range(const roaring_bitmap_t *bm, uint64_t x, uint64_t y); + +/** + * Computes the Jaccard index between two bitmaps. (Also known as the Tanimoto + * distance, or the Jaccard similarity coefficient) + * + * The Jaccard index is undefined if both bitmaps are empty. + */ +double roaring_bitmap_jaccard_index(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Computes the size of the union between two bitmaps. + */ +uint64_t roaring_bitmap_or_cardinality(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Computes the size of the difference (andnot) between two bitmaps. + */ +uint64_t roaring_bitmap_andnot_cardinality(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Computes the size of the symmetric difference (xor) between two bitmaps. + */ +uint64_t roaring_bitmap_xor_cardinality(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Inplace version of `roaring_bitmap_and()`, modifies r1 + * r1 == r2 is allowed. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + */ +void roaring_bitmap_and_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Computes the union between two bitmaps and returns new bitmap. The caller is + * responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_or(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Inplace version of `roaring_bitmap_or(), modifies r1. + * TODO: decide whether r1 == r2 ok + */ +void roaring_bitmap_or_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Compute the union of 'number' bitmaps. + * Caller is responsible for freeing the result. + * See also `roaring_bitmap_or_many_heap()` + */ +roaring_bitmap_t *roaring_bitmap_or_many(size_t number, const roaring_bitmap_t **rs); + +/** + * Compute the union of 'number' bitmaps using a heap. This can sometimes be + * faster than `roaring_bitmap_or_many() which uses a naive algorithm. + * Caller is responsible for freeing the result. + */ +roaring_bitmap_t *roaring_bitmap_or_many_heap(uint32_t number, const roaring_bitmap_t **rs); + +/** + * Computes the symmetric difference (xor) between two bitmaps + * and returns new bitmap. The caller is responsible for memory management. + */ +roaring_bitmap_t *roaring_bitmap_xor(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Inplace version of roaring_bitmap_xor, modifies r1, r1 != r2. + */ +void roaring_bitmap_xor_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Compute the xor of 'number' bitmaps. + * Caller is responsible for freeing the result. + */ +roaring_bitmap_t *roaring_bitmap_xor_many(size_t number, const roaring_bitmap_t **rs); + +/** + * Computes the difference (andnot) between two bitmaps and returns new bitmap. + * Caller is responsible for freeing the result. + */ +roaring_bitmap_t *roaring_bitmap_andnot(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Inplace version of roaring_bitmap_andnot, modifies r1, r1 != r2. + */ +void roaring_bitmap_andnot_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * TODO: consider implementing: + * + * "Compute the xor of 'number' bitmaps using a heap. This can sometimes be + * faster than roaring_bitmap_xor_many which uses a naive algorithm. Caller is + * responsible for freeing the result."" + * + * roaring_bitmap_t *roaring_bitmap_xor_many_heap(uint32_t number, + * const roaring_bitmap_t **rs); + */ + +/** + * Frees the memory. + */ +void roaring_bitmap_free(const roaring_bitmap_t *r); + +/** + * A bit of context usable with `roaring_bitmap_*_bulk()` functions + * + * Should be initialized with `{0}` (or `memset()` to all zeros). + * Callers should treat it as an opaque type. + * + * A context may only be used with a single bitmap + * (unless re-initialized to zero), and any modification to a bitmap + * (other than modifications performed with `_bulk()` functions with the context + * passed) will invalidate any contexts associated with that bitmap. + */ +typedef struct roaring_bulk_context_s { + ROARING_CONTAINER_T *container; + int idx; + uint16_t key; + uint8_t typecode; +} roaring_bulk_context_t; + +/** + * Add an item, using context from a previous insert for speed optimization. + * + * `context` will be used to store information between calls to make bulk + * operations faster. `*context` should be zero-initialized before the first + * call to this function. + * + * Modifying the bitmap in any way (other than `-bulk` suffixed functions) + * will invalidate the stored context, calling this function with a non-zero + * context after doing any modification invokes undefined behavior. + * + * In order to exploit this optimization, the caller should call this function + * with values with the same "key" (high 16 bits of the value) consecutively. + */ +void roaring_bitmap_add_bulk(roaring_bitmap_t *r, roaring_bulk_context_t *context, uint32_t val); + +/** + * Add value n_args from pointer vals, faster than repeatedly calling + * `roaring_bitmap_add()` + * + * In order to exploit this optimization, the caller should attempt to keep + * values with the same "key" (high 16 bits of the value) as consecutive + * elements in `vals` + */ +void roaring_bitmap_add_many(roaring_bitmap_t *r, size_t n_args, const uint32_t *vals); + +/** + * Add value x + */ +void roaring_bitmap_add(roaring_bitmap_t *r, uint32_t x); + +/** + * Add value x + * Returns true if a new value was added, false if the value already existed. + */ +bool roaring_bitmap_add_checked(roaring_bitmap_t *r, uint32_t x); + +/** + * Add all values in range [min, max] + */ +void roaring_bitmap_add_range_closed(roaring_bitmap_t *r, uint32_t min, uint32_t max); + +/** + * Add all values in range [min, max) + */ +inline void roaring_bitmap_add_range(roaring_bitmap_t *r, uint64_t min, uint64_t max) { + if (max <= min) return; + roaring_bitmap_add_range_closed(r, (uint32_t)min, (uint32_t)(max - 1)); +} + +/** + * Remove value x + */ +void roaring_bitmap_remove(roaring_bitmap_t *r, uint32_t x); + +/** + * Remove all values in range [min, max] + */ +void roaring_bitmap_remove_range_closed(roaring_bitmap_t *r, uint32_t min, uint32_t max); + +/** + * Remove all values in range [min, max) + */ +inline void roaring_bitmap_remove_range(roaring_bitmap_t *r, uint64_t min, uint64_t max) { + if (max <= min) return; + roaring_bitmap_remove_range_closed(r, (uint32_t)min, (uint32_t)(max - 1)); +} + +/** + * Remove multiple values + */ +void roaring_bitmap_remove_many(roaring_bitmap_t *r, size_t n_args, const uint32_t *vals); + +/** + * Remove value x + * Returns true if a new value was removed, false if the value was not existing. + */ +bool roaring_bitmap_remove_checked(roaring_bitmap_t *r, uint32_t x); + +/** + * Check if value is present + */ +bool roaring_bitmap_contains(const roaring_bitmap_t *r, uint32_t val); + +/** + * Check whether a range of values from range_start (included) + * to range_end (excluded) is present + */ +bool roaring_bitmap_contains_range(const roaring_bitmap_t *r, uint64_t range_start, + uint64_t range_end); + +/** + * Check if an items is present, using context from a previous insert or search + * for speed optimization. + * + * `context` will be used to store information between calls to make bulk + * operations faster. `*context` should be zero-initialized before the first + * call to this function. + * + * Modifying the bitmap in any way (other than `-bulk` suffixed functions) + * will invalidate the stored context, calling this function with a non-zero + * context after doing any modification invokes undefined behavior. + * + * In order to exploit this optimization, the caller should call this function + * with values with the same "key" (high 16 bits of the value) consecutively. + */ +bool roaring_bitmap_contains_bulk(const roaring_bitmap_t *r, roaring_bulk_context_t *context, + uint32_t val); + +/** + * Get the cardinality of the bitmap (number of elements). + */ +uint64_t roaring_bitmap_get_cardinality(const roaring_bitmap_t *r); + +/** + * Returns the number of elements in the range [range_start, range_end). + */ +uint64_t roaring_bitmap_range_cardinality(const roaring_bitmap_t *r, uint64_t range_start, + uint64_t range_end); + +/** + * Returns true if the bitmap is empty (cardinality is zero). + */ +bool roaring_bitmap_is_empty(const roaring_bitmap_t *r); + +/** + * Empties the bitmap. It will have no auxiliary allocations (so if the bitmap + * was initialized in client memory via roaring_bitmap_init(), then a call to + * roaring_bitmap_clear() would be enough to "free" it) + */ +void roaring_bitmap_clear(roaring_bitmap_t *r); + +/** + * Convert the bitmap to a sorted array, output in `ans`. + * + * Caller is responsible to ensure that there is enough memory allocated, e.g. + * + * ans = malloc(roaring_bitmap_get_cardinality(bitmap) * sizeof(uint32_t)); + */ +void roaring_bitmap_to_uint32_array(const roaring_bitmap_t *r, uint32_t *ans); + +/** + * Store the bitmap to a bitset. This can be useful for people + * who need the performance and simplicity of a standard bitset. + * We assume that the input bitset is originally empty (does not + * have any set bit). + * + * bitset_t * out = bitset_create(); + * // if the bitset has content in it, call "bitset_clear(out)" + * bool success = roaring_bitmap_to_bitset(mybitmap, out); + * // on failure, success will be false. + * // You can then query the bitset: + * bool is_present = bitset_get(out, 10011 ); + * // you must free the memory: + * bitset_free(out); + * + */ +bool roaring_bitmap_to_bitset(const roaring_bitmap_t *r, bitset_t *bitset); + +/** + * Convert the bitmap to a sorted array from `offset` by `limit`, output in `ans`. + * + * Caller is responsible to ensure that there is enough memory allocated, e.g. + * + * ans = malloc(roaring_bitmap_get_cardinality(limit) * sizeof(uint32_t)); + * + * Return false in case of failure (e.g., insufficient memory) + */ +bool roaring_bitmap_range_uint32_array(const roaring_bitmap_t *r, size_t offset, size_t limit, + uint32_t *ans); + +/** + * Remove run-length encoding even when it is more space efficient. + * Return whether a change was applied. + */ +bool roaring_bitmap_remove_run_compression(roaring_bitmap_t *r); + +/** + * Convert array and bitmap containers to run containers when it is more + * efficient; also convert from run containers when more space efficient. + * + * Returns true if the result has at least one run container. + * Additional savings might be possible by calling `shrinkToFit()`. + */ +bool roaring_bitmap_run_optimize(roaring_bitmap_t *r); + +/** + * If needed, reallocate memory to shrink the memory usage. + * Returns the number of bytes saved. + */ +size_t roaring_bitmap_shrink_to_fit(roaring_bitmap_t *r); + +/** + * Write the bitmap to an output pointer, this output buffer should refer to + * at least `roaring_bitmap_size_in_bytes(r)` allocated bytes. + * + * See `roaring_bitmap_portable_serialize()` if you want a format that's + * compatible with Java and Go implementations. This format can sometimes be + * more space efficient than the portable form, e.g. when the data is sparse. + * + * Returns how many bytes written, should be `roaring_bitmap_size_in_bytes(r)`. + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +size_t roaring_bitmap_serialize(const roaring_bitmap_t *r, char *buf); + +/** + * Use with `roaring_bitmap_serialize()`. + * + * (See `roaring_bitmap_portable_deserialize()` if you want a format that's + * compatible with Java and Go implementations). + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf); + +/** + * Use with `roaring_bitmap_serialize()`. + * + * (See `roaring_bitmap_portable_deserialize_safe()` if you want a format that's + * compatible with Java and Go implementations). + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + * + * The difference with `roaring_bitmap_deserialize()` is that this function checks that the input + * buffer is a valid bitmap. If the buffer is too small, NULL is returned. + */ +roaring_bitmap_t *roaring_bitmap_deserialize_safe(const void *buf, size_t maxbytes); + +/** + * How many bytes are required to serialize this bitmap (NOT compatible + * with Java and Go versions) + */ +size_t roaring_bitmap_size_in_bytes(const roaring_bitmap_t *r); + +/** + * Read bitmap from a serialized buffer. + * In case of failure, NULL is returned. + * + * This function is unsafe in the sense that if there is no valid serialized + * bitmap at the pointer, then many bytes could be read, possibly causing a + * buffer overflow. See also roaring_bitmap_portable_deserialize_safe(). + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf); + +/** + * Read bitmap from a serialized buffer safely (reading up to maxbytes). + * In case of failure, NULL is returned. + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + * + * The function itself is safe in the sense that it will not cause buffer overflows. + * However, for correct operations, it is assumed that the bitmap read was once + * serialized from a valid bitmap (i.e., it follows the format specification). + * If you provided an incorrect input (garbage), then the bitmap read may not be in + * a valid state and following operations may not lead to sensible results. + * In particular, the serialized array containers need to be in sorted order, and the + * run containers should be in sorted non-overlapping order. This is is guaranteed to + * happen when serializing an existing bitmap, but not for random inputs. + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes); + +/** + * Read bitmap from a serialized buffer. + * In case of failure, NULL is returned. + * + * Bitmap returned by this function can be used in all readonly contexts. + * Bitmap must be freed as usual, by calling roaring_bitmap_free(). + * Underlying buffer must not be freed or modified while it backs any bitmaps. + * + * The function is unsafe in the following ways: + * 1) It may execute unaligned memory accesses. + * 2) A buffer overflow may occur if buf does not point to a valid serialized + * bitmap. + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +roaring_bitmap_t *roaring_bitmap_portable_deserialize_frozen(const char *buf); + +/** + * Check how many bytes would be read (up to maxbytes) at this pointer if there + * is a bitmap, returns zero if there is no valid bitmap. + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + */ +size_t roaring_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes); + +/** + * How many bytes are required to serialize this bitmap. + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + */ +size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *r); + +/** + * Write a bitmap to a char buffer. The output buffer should refer to at least + * `roaring_bitmap_portable_size_in_bytes(r)` bytes of allocated memory. + * + * Returns how many bytes were written which should match + * `roaring_bitmap_portable_size_in_bytes(r)`. + * + * This is meant to be compatible with the Java and Go versions: + * https://github.com/RoaringBitmap/RoaringFormatSpec + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *r, char *buf); + +/* + * "Frozen" serialization format imitates memory layout of roaring_bitmap_t. + * Deserialized bitmap is a constant view of the underlying buffer. + * This significantly reduces amount of allocations and copying required during + * deserialization. + * It can be used with memory mapped files. + * Example can be found in benchmarks/frozen_benchmark.c + * + * [#####] const roaring_bitmap_t * + * | | | + * +----+ | +-+ + * | | | + * [#####################################] underlying buffer + * + * Note that because frozen serialization format imitates C memory layout + * of roaring_bitmap_t, it is not fixed. It is different on big/little endian + * platforms and can be changed in future. + */ + +/** + * Returns number of bytes required to serialize bitmap using frozen format. + */ +size_t roaring_bitmap_frozen_size_in_bytes(const roaring_bitmap_t *r); + +/** + * Serializes bitmap using frozen format. + * Buffer size must be at least roaring_bitmap_frozen_size_in_bytes(). + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +void roaring_bitmap_frozen_serialize(const roaring_bitmap_t *r, char *buf); + +/** + * Creates constant bitmap that is a view of a given buffer. + * Buffer data should have been written by `roaring_bitmap_frozen_serialize()` + * Its beginning must also be aligned by 32 bytes. + * Length must be equal exactly to `roaring_bitmap_frozen_size_in_bytes()`. + * In case of failure, NULL is returned. + * + * Bitmap returned by this function can be used in all readonly contexts. + * Bitmap must be freed as usual, by calling roaring_bitmap_free(). + * Underlying buffer must not be freed or modified while it backs any bitmaps. + * + * This function is endian-sensitive. If you have a big-endian system (e.g., a mainframe IBM s390x), + * the data format is going to be big-endian and not compatible with little-endian systems. + */ +const roaring_bitmap_t *roaring_bitmap_frozen_view(const char *buf, size_t length); + +/** + * Iterate over the bitmap elements. The function iterator is called once for + * all the values with ptr (can be NULL) as the second parameter of each call. + * + * `roaring_iterator` is simply a pointer to a function that returns bool + * (true means that the iteration should continue while false means that it + * should stop), and takes (uint32_t,void*) as inputs. + * + * Returns true if the roaring_iterator returned true throughout (so that all + * data points were necessarily visited). + * + * Iteration is ordered: from the smallest to the largest elements. + */ +bool roaring_iterate(const roaring_bitmap_t *r, roaring_iterator iterator, void *ptr); + +bool roaring_iterate64(const roaring_bitmap_t *r, roaring_iterator64 iterator, uint64_t high_bits, + void *ptr); + +/** + * Return true if the two bitmaps contain the same elements. + */ +bool roaring_bitmap_equals(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Return true if all the elements of r1 are also in r2. + */ +bool roaring_bitmap_is_subset(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Return true if all the elements of r1 are also in r2, and r2 is strictly + * greater than r1. + */ +bool roaring_bitmap_is_strict_subset(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * (For expert users who seek high performance.) + * + * Computes the union between two bitmaps and returns new bitmap. The caller is + * responsible for memory management. + * + * The lazy version defers some computations such as the maintenance of the + * cardinality counts. Thus you must call `roaring_bitmap_repair_after_lazy()` + * after executing "lazy" computations. + * + * It is safe to repeatedly call roaring_bitmap_lazy_or_inplace on the result. + * + * `bitsetconversion` is a flag which determines whether container-container + * operations force a bitset conversion. + */ +roaring_bitmap_t *roaring_bitmap_lazy_or(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2, + const bool bitsetconversion); + +/** + * (For expert users who seek high performance.) + * + * Inplace version of roaring_bitmap_lazy_or, modifies r1. + * + * `bitsetconversion` is a flag which determines whether container-container + * operations force a bitset conversion. + */ +void roaring_bitmap_lazy_or_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2, + const bool bitsetconversion); + +/** + * (For expert users who seek high performance.) + * + * Execute maintenance on a bitmap created from `roaring_bitmap_lazy_or()` + * or modified with `roaring_bitmap_lazy_or_inplace()`. + */ +void roaring_bitmap_repair_after_lazy(roaring_bitmap_t *r1); + +/** + * Computes the symmetric difference between two bitmaps and returns new bitmap. + * The caller is responsible for memory management. + * + * The lazy version defers some computations such as the maintenance of the + * cardinality counts. Thus you must call `roaring_bitmap_repair_after_lazy()` + * after executing "lazy" computations. + * + * It is safe to repeatedly call `roaring_bitmap_lazy_xor_inplace()` on + * the result. + */ +roaring_bitmap_t *roaring_bitmap_lazy_xor(const roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * (For expert users who seek high performance.) + * + * Inplace version of roaring_bitmap_lazy_xor, modifies r1. r1 != r2 + */ +void roaring_bitmap_lazy_xor_inplace(roaring_bitmap_t *r1, const roaring_bitmap_t *r2); + +/** + * Compute the negation of the bitmap in the interval [range_start, range_end). + * The number of negated values is range_end - range_start. + * Areas outside the range are passed through unchanged. + */ +roaring_bitmap_t *roaring_bitmap_flip(const roaring_bitmap_t *r1, uint64_t range_start, + uint64_t range_end); + +/** + * compute (in place) the negation of the roaring bitmap within a specified + * interval: [range_start, range_end). The number of negated values is + * range_end - range_start. + * Areas outside the range are passed through unchanged. + */ +void roaring_bitmap_flip_inplace(roaring_bitmap_t *r1, uint64_t range_start, uint64_t range_end); + +/** + * Selects the element at index 'rank' where the smallest element is at index 0. + * If the size of the roaring bitmap is strictly greater than rank, then this + * function returns true and sets element to the element of given rank. + * Otherwise, it returns false. + */ +bool roaring_bitmap_select(const roaring_bitmap_t *r, uint32_t rank, uint32_t *element); + +/** + * roaring_bitmap_rank returns the number of integers that are smaller or equal + * to x. Thus if x is the first element, this function will return 1. If + * x is smaller than the smallest element, this function will return 0. + * + * The indexing convention differs between roaring_bitmap_select and + * roaring_bitmap_rank: roaring_bitmap_select refers to the smallest value + * as having index 0, whereas roaring_bitmap_rank returns 1 when ranking + * the smallest value. + */ +uint64_t roaring_bitmap_rank(const roaring_bitmap_t *r, uint32_t x); + +/** + * roaring_bitmap_rank_many is an `Bulk` version of `roaring_bitmap_rank` + * it puts rank value of each element in `[begin .. end)` to `ans[]` + * + * the values in `[begin .. end)` must be sorted in Ascending order; + * Caller is responsible to ensure that there is enough memory allocated, e.g. + * + * ans = malloc((end-begin) * sizeof(uint64_t)); + */ +void roaring_bitmap_rank_many(const roaring_bitmap_t *r, const uint32_t *begin, const uint32_t *end, + uint64_t *ans); + +/** + * Returns the index of x in the given roaring bitmap. + * If the roaring bitmap doesn't contain x , this function will return -1. + * The difference with rank function is that this function will return -1 when x + * is not the element of roaring bitmap, but the rank function will return a + * non-negative number. + */ +int64_t roaring_bitmap_get_index(const roaring_bitmap_t *r, uint32_t x); + +/** + * Returns the smallest value in the set, or UINT32_MAX if the set is empty. + */ +uint32_t roaring_bitmap_minimum(const roaring_bitmap_t *r); + +/** + * Returns the greatest value in the set, or 0 if the set is empty. + */ +uint32_t roaring_bitmap_maximum(const roaring_bitmap_t *r); + +/** + * (For advanced users.) + * + * Collect statistics about the bitmap, see roaring_types.h for + * a description of roaring_statistics_t + */ +void roaring_bitmap_statistics(const roaring_bitmap_t *r, roaring_statistics_t *stat); + +/** + * Perform internal consistency checks. Returns true if the bitmap is consistent. + * + * Note that some operations intentionally leave bitmaps in an inconsistent state temporarily, + * for example, `roaring_bitmap_lazy_*` functions, until `roaring_bitmap_repair_after_lazy` is + * called. + * + * If reason is non-null, it will be set to a string describing the first inconsistency found if + * any. + */ +bool roaring_bitmap_internal_validate(const roaring_bitmap_t *r, const char **reason); + +/********************* +* What follows is code use to iterate through values in a roaring bitmap + +roaring_bitmap_t *r =... +roaring_uint32_iterator_t i; +roaring_create_iterator(r, &i); +while(i.has_value) { + printf("value = %d\n", i.current_value); + roaring_advance_uint32_iterator(&i); +} + +Obviously, if you modify the underlying bitmap, the iterator +becomes invalid. So don't. +*/ + +typedef struct roaring_uint32_iterator_s { + const roaring_bitmap_t *parent; // owner + int32_t container_index; // point to the current container index + int32_t in_container_index; // for bitset and array container, this is out + // index + int32_t run_index; // for run container, this points at the run + + uint32_t current_value; + bool has_value; + + const ROARING_CONTAINER_T + *container; // should be: + // parent->high_low_container.containers[container_index]; + uint8_t typecode; // should be: + // parent->high_low_container.typecodes[container_index]; + uint32_t highbits; // should be: + // parent->high_low_container.keys[container_index]) << + // 16; + +} roaring_uint32_iterator_t; + +/** + * Initialize an iterator object that can be used to iterate through the + * values. If there is a value, then this iterator points to the first value + * and `it->has_value` is true. The value is in `it->current_value`. + */ +void roaring_init_iterator(const roaring_bitmap_t *r, roaring_uint32_iterator_t *newit); + +/** + * Initialize an iterator object that can be used to iterate through the + * values. If there is a value, then this iterator points to the last value + * and `it->has_value` is true. The value is in `it->current_value`. + */ +void roaring_init_iterator_last(const roaring_bitmap_t *r, roaring_uint32_iterator_t *newit); + +/** + * Create an iterator object that can be used to iterate through the values. + * Caller is responsible for calling `roaring_free_iterator()`. + * + * The iterator is initialized (this function calls `roaring_init_iterator()`) + * If there is a value, then this iterator points to the first value and + * `it->has_value` is true. The value is in `it->current_value`. + */ +roaring_uint32_iterator_t *roaring_create_iterator(const roaring_bitmap_t *r); + +/** + * Advance the iterator. If there is a new value, then `it->has_value` is true. + * The new value is in `it->current_value`. Values are traversed in increasing + * orders. For convenience, returns `it->has_value`. + */ +bool roaring_advance_uint32_iterator(roaring_uint32_iterator_t *it); + +/** + * Decrement the iterator. If there's a new value, then `it->has_value` is true. + * The new value is in `it->current_value`. Values are traversed in decreasing + * order. For convenience, returns `it->has_value`. + */ +bool roaring_previous_uint32_iterator(roaring_uint32_iterator_t *it); + +/** + * Move the iterator to the first value >= `val`. If there is a such a value, + * then `it->has_value` is true. The new value is in `it->current_value`. + * For convenience, returns `it->has_value`. + */ +bool roaring_move_uint32_iterator_equalorlarger(roaring_uint32_iterator_t *it, uint32_t val); + +/** + * Creates a copy of an iterator. + * Caller must free it. + */ +roaring_uint32_iterator_t *roaring_copy_uint32_iterator(const roaring_uint32_iterator_t *it); + +/** + * Free memory following `roaring_create_iterator()` + */ +void roaring_free_uint32_iterator(roaring_uint32_iterator_t *it); + +/* + * Reads next ${count} values from iterator into user-supplied ${buf}. + * Returns the number of read elements. + * This number can be smaller than ${count}, which means that iterator is drained. + * + * This function satisfies semantics of iteration and can be used together with + * other iterator functions. + * - first value is copied from ${it}->current_value + * - after function returns, iterator is positioned at the next element + */ +uint32_t roaring_read_uint32_iterator(roaring_uint32_iterator_t *it, uint32_t *buf, uint32_t count); + +} // namespace internal +} // namespace paimon::roaring + +#endif /* ROARING_H */ + +#ifdef __cplusplus +/** + * Best practices for C++ headers is to avoid polluting global scope. + * But for C compatibility when just `roaring.h` is included building as + * C++, default to global access for the C public API. + * + * BUT when `roaring.hh` is included instead, it sets this flag. That way + * explicit namespacing must be used to get the C functions. + * + * This is outside the include guard so that if you include BOTH headers, + * the order won't matter; you still get the global definitions. + */ +#if !defined(ROARING_API_NOT_IN_GLOBAL_NAMESPACE) +using namespace ::paimon::roaring::internal; +#endif +#endif +/* end file include/roaring/roaring.h */ +/* begin file include/roaring/memory.h */ +#ifndef INCLUDE_ROARING_MEMORY_H_ +#define INCLUDE_ROARING_MEMORY_H_ + +#include // for size_t + +typedef void *(*roaring_malloc_p)(size_t); +typedef void *(*roaring_realloc_p)(void *, size_t); +typedef void *(*roaring_calloc_p)(size_t, size_t); +typedef void (*roaring_free_p)(void *); +typedef void *(*roaring_aligned_malloc_p)(size_t, size_t); +typedef void (*roaring_aligned_free_p)(void *); + +typedef struct roaring_memory_s { + roaring_malloc_p malloc; + roaring_realloc_p realloc; + roaring_calloc_p calloc; + roaring_free_p free; + roaring_aligned_malloc_p aligned_malloc; + roaring_aligned_free_p aligned_free; +} roaring_memory_t; + +void roaring_init_memory_hook(roaring_memory_t memory_hook); + +void *roaring_malloc(size_t); +void *roaring_realloc(void *, size_t); +void *roaring_calloc(size_t, size_t); +void roaring_free(void *); +void *roaring_aligned_malloc(size_t, size_t); +void roaring_aligned_free(void *); + +#endif // INCLUDE_ROARING_MEMORY_H_ +/* end file include/roaring/memory.h */ diff --git a/third_party/roaring_bitmap/roaring.hh b/third_party/roaring_bitmap/roaring.hh new file mode 100644 index 0000000..4e829cd --- /dev/null +++ b/third_party/roaring_bitmap/roaring.hh @@ -0,0 +1,2893 @@ +// !!! DO NOT EDIT - THIS IS AN AUTO-GENERATED FILE !!! +// Created by amalgamation.sh on 2023-12-16T02:52:00Z + +/* + * The CRoaring project is under a dual license (Apache/MIT). + * Users of the library may choose one or the other license. + */ +/* + * Copyright 2016-2022 The CRoaring authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* + * MIT License + * + * Copyright 2016-2022 The CRoaring authors + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without + * limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +#define ROARING_API_NOT_IN_GLOBAL_NAMESPACE // see remarks in roaring.h +#include "roaring.h" +#undef ROARING_API_NOT_IN_GLOBAL_NAMESPACE +/* begin file cpp/roaring.hh */ +/* +A C++ header for Roaring Bitmaps. +*/ +#ifndef INCLUDE_ROARING_HH_ +#define INCLUDE_ROARING_HH_ + +#include + +#include +#include +#include +#include +#include + +#if !defined(ROARING_EXCEPTIONS) +// __cpp_exceptions is required by C++98 and we require C++11 or better. +#ifndef __cpp_exceptions +#error "__cpp_exceptions should be defined" +#endif +# if __cpp_exceptions +# define ROARING_EXCEPTIONS 1 +# else +# define ROARING_EXCEPTIONS 0 +# endif +#endif + +#ifndef ROARING_TERMINATE +# if ROARING_EXCEPTIONS +# define ROARING_TERMINATE(_s) throw std::runtime_error(_s) +# else +# define ROARING_TERMINATE(_s) std::terminate() +# endif +#endif + +#define ROARING_API_NOT_IN_GLOBAL_NAMESPACE // see remarks in roaring.h +#undef ROARING_API_NOT_IN_GLOBAL_NAMESPACE + + +namespace paimon::roaring { + +class RoaringSetBitForwardIterator; + +/** + * A bit of context usable with `*Bulk()` functions. + * + * A context may only be used with a single bitmap, and any modification to a bitmap + * (other than modifications performed with `Bulk()` functions with the context + * passed) will invalidate any contexts associated with that bitmap. + */ +class BulkContext { + public: + friend class Roaring; + using roaring_bitmap_bulk_context_t = internal::roaring_bulk_context_t; + BulkContext() : context_{nullptr, 0, 0, 0} {} + + BulkContext(const BulkContext&) = delete; + BulkContext& operator=(const BulkContext&) = delete; + BulkContext(BulkContext&&) noexcept = default; + BulkContext& operator=(BulkContext&&) noexcept = default; + + private: + roaring_bitmap_bulk_context_t context_; +}; + +class Roaring { + typedef internal::roaring_bitmap_t roaring_bitmap_t; // class-local name alias + +public: + /** + * Create an empty bitmap in the existing memory for the class. + * The bitmap will be in the "clear" state with no auxiliary allocations. + */ + Roaring() : roaring{} { + // The empty constructor roaring{} silences warnings from pedantic static analyzers. + internal::roaring_bitmap_init_cleared(&roaring); + } + + /** + * Construct a bitmap from a list of 32-bit integer values. + */ + Roaring(size_t n, const uint32_t *data) : Roaring() { + internal::roaring_bitmap_add_many(&roaring, n, data); + } + + /** + * Construct a bitmap from an initializer list. + */ + Roaring(std::initializer_list l) : Roaring() { + addMany(l.size(), l.begin()); + } + + /** + * Copy constructor. + * It may throw std::runtime_error if there is insufficient memory. + */ + Roaring(const Roaring &r) : Roaring() { + if (!internal::roaring_bitmap_overwrite(&roaring, &r.roaring)) { + ROARING_TERMINATE("failed roaring_bitmap_overwrite in constructor"); + } + internal::roaring_bitmap_set_copy_on_write( + &roaring, + internal::roaring_bitmap_get_copy_on_write(&r.roaring)); + } + + /** + * Move constructor. The moved-from object remains valid but empty, i.e. + * it behaves as though it was just freshly constructed. + */ + Roaring(Roaring &&r) noexcept : roaring(r.roaring) { + // + // !!! This clones the bits of the roaring structure to a new location + // and then overwrites the old bits...assuming that this will still + // work. There are scenarios where this could break; e.g. if some of + // those bits were pointers into the structure memory itself. If such + // things were possible, a roaring_bitmap_move() API would be needed. + // + internal::roaring_bitmap_init_cleared(&r.roaring); + } + + /** + * Construct a roaring object by taking control of a malloc()'d C struct. + * + * Passing a NULL pointer is unsafe. + * The pointer to the C struct will be invalid after the call. + */ + explicit Roaring(roaring_bitmap_t *s) noexcept : roaring (*s) { + roaring_free(s); // deallocate the passed-in pointer + } + + /** + * Construct a bitmap from a list of uint32_t values. + */ + static Roaring bitmapOf(size_t n, ...) { + Roaring ans; + va_list vl; + va_start(vl, n); + for (size_t i = 0; i < n; i++) { + ans.add(va_arg(vl, uint32_t)); + } + va_end(vl); + return ans; + } + + /** + * Construct a bitmap from a list of uint32_t values. + * E.g., bitmapOfList({1,2,3}). + */ + static Roaring bitmapOfList(std::initializer_list l) { + Roaring ans; + ans.addMany(l.size(), l.begin()); + return ans; + } + + /** + * Add value x + */ + void add(uint32_t x) noexcept { internal::roaring_bitmap_add(&roaring, x); } + + /** + * Add value x + * Returns true if a new value was added, false if the value was already + * existing. + */ + bool addChecked(uint32_t x) noexcept { + return internal::roaring_bitmap_add_checked(&roaring, x); + } + + /** + * Add all values in range [min, max) + */ + void addRange(const uint64_t min, const uint64_t max) noexcept { + return internal::roaring_bitmap_add_range(&roaring, min, max); + } + + /** + * Add all values in range [min, max] + */ + void addRangeClosed(const uint32_t min, const uint32_t max) noexcept { + return internal::roaring_bitmap_add_range_closed(&roaring, min, max); + } + + /** + * Add value n_args from pointer vals + */ + void addMany(size_t n_args, const uint32_t *vals) noexcept { + internal::roaring_bitmap_add_many(&roaring, n_args, vals); + } + + /** + * Add value val, using context from a previous insert for speed + * optimization. + * + * `context` will be used to store information between calls to make bulk + * operations faster. `context` should be default-initialized before the + * first call to this function. + */ + void addBulk(BulkContext &context, uint32_t x) noexcept { + internal::roaring_bitmap_add_bulk(&roaring, &context.context_, x); + } + + /** + * Check if item x is present, using context from a previous insert or search + * for speed optimization. + * + * `context` will be used to store information between calls to make bulk + * operations faster. `context` should be default-initialized before the + * first call to this function. + */ + bool containsBulk(BulkContext& context, uint32_t x) const noexcept { + return internal::roaring_bitmap_contains_bulk(&roaring, &context.context_, x); + } + + /** + * Remove value x + */ + void remove(uint32_t x) noexcept { internal::roaring_bitmap_remove(&roaring, x); } + + /** + * Remove value x + * Returns true if a new value was removed, false if the value was not + * existing. + */ + bool removeChecked(uint32_t x) noexcept { + return internal::roaring_bitmap_remove_checked(&roaring, x); + } + + /** + * Remove all values in range [min, max) + */ + void removeRange(uint64_t min, uint64_t max) noexcept { + return internal::roaring_bitmap_remove_range(&roaring, min, max); + } + + /** + * Remove all values in range [min, max] + */ + void removeRangeClosed(uint32_t min, uint32_t max) noexcept { + return internal::roaring_bitmap_remove_range_closed(&roaring, min, max); + } + + /** + * Return the largest value (if not empty) + */ + uint32_t maximum() const noexcept { return internal::roaring_bitmap_maximum(&roaring); } + + /** + * Return the smallest value (if not empty) + */ + uint32_t minimum() const noexcept { return internal::roaring_bitmap_minimum(&roaring); } + + /** + * Check if value x is present + */ + bool contains(uint32_t x) const noexcept { + return internal::roaring_bitmap_contains(&roaring, x); + } + + /** + * Check if all values from x (included) to y (excluded) are present + */ + bool containsRange(const uint64_t x, const uint64_t y) const noexcept { + return internal::roaring_bitmap_contains_range(&roaring, x, y); + } + + /** + * Destructor. By contract, calling roaring_bitmap_clear() is enough to + * release all auxiliary memory used by the structure. + */ + ~Roaring() { + if (!(roaring.high_low_container.flags & ROARING_FLAG_FROZEN)) { + internal::roaring_bitmap_clear(&roaring); + } else { + // The roaring member variable copies the `roaring_bitmap_t` and + // nested `roaring_array_t` structures by value and is freed in the + // constructor, however the underlying memory arena used for the + // container data is not freed with it. Here we derive the arena + // pointer from the second arena allocation in + // `roaring_bitmap_frozen_view` and free it as well. + roaring_bitmap_free( + (roaring_bitmap_t *)((char *) + roaring.high_low_container.containers - + sizeof(roaring_bitmap_t))); + } + } + + /** + * Copies the content of the provided bitmap, and + * discard the current content. + * It may throw std::runtime_error if there is insufficient memory. + */ + Roaring &operator=(const Roaring &r) { + if (!internal::roaring_bitmap_overwrite(&roaring, &r.roaring)) { + ROARING_TERMINATE("failed memory alloc in assignment"); + } + internal::roaring_bitmap_set_copy_on_write( + &roaring, + internal::roaring_bitmap_get_copy_on_write(&r.roaring)); + return *this; + } + + /** + * Moves the content of the provided bitmap, and + * discard the current content. + */ + Roaring &operator=(Roaring &&r) noexcept { + internal::roaring_bitmap_clear(&roaring); // free this class's allocations + + // !!! See notes in the Move Constructor regarding roaring_bitmap_move() + // + roaring = r.roaring; + internal::roaring_bitmap_init_cleared(&r.roaring); + + return *this; + } + + /** + * Assignment from an initializer list. + */ + Roaring &operator=(std::initializer_list l) { + // Delegate to move assignment operator + *this = Roaring(l); + return *this; + } + + /** + * Compute the intersection between the current bitmap and the provided + * bitmap, writing the result in the current bitmap. The provided bitmap + * is not modified. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + */ + Roaring &operator&=(const Roaring &r) noexcept { + internal::roaring_bitmap_and_inplace(&roaring, &r.roaring); + return *this; + } + + /** + * Compute the difference between the current bitmap and the provided + * bitmap, writing the result in the current bitmap. The provided bitmap + * is not modified. + */ + Roaring &operator-=(const Roaring &r) noexcept { + internal::roaring_bitmap_andnot_inplace(&roaring, &r.roaring); + return *this; + } + + /** + * Compute the union between the current bitmap and the provided bitmap, + * writing the result in the current bitmap. The provided bitmap is not + * modified. + * + * See also the fastunion function to aggregate many bitmaps more quickly. + */ + Roaring &operator|=(const Roaring &r) noexcept { + internal::roaring_bitmap_or_inplace(&roaring, &r.roaring); + return *this; + } + + /** + * Compute the symmetric union between the current bitmap and the provided + * bitmap, writing the result in the current bitmap. The provided bitmap + * is not modified. + */ + Roaring &operator^=(const Roaring &r) noexcept { + internal::roaring_bitmap_xor_inplace(&roaring, &r.roaring); + return *this; + } + + /** + * Exchange the content of this bitmap with another. + */ + void swap(Roaring &r) noexcept { std::swap(r.roaring, roaring); } + + /** + * Get the cardinality of the bitmap (number of elements). + */ + uint64_t cardinality() const noexcept { + return internal::roaring_bitmap_get_cardinality(&roaring); + } + + /** + * Returns true if the bitmap is empty (cardinality is zero). + */ + bool isEmpty() const noexcept { return internal::roaring_bitmap_is_empty(&roaring); } + + /** + * Returns true if the bitmap is subset of the other. + */ + bool isSubset(const Roaring &r) const noexcept { + return internal::roaring_bitmap_is_subset(&roaring, &r.roaring); + } + + /** + * Returns true if the bitmap is strict subset of the other. + */ + bool isStrictSubset(const Roaring &r) const noexcept { + return internal::roaring_bitmap_is_strict_subset(&roaring, &r.roaring); + } + + /** + * Convert the bitmap to an array. Write the output to "ans", caller is + * responsible to ensure that there is enough memory allocated + * (e.g., ans = new uint32[mybitmap.cardinality()];) + */ + void toUint32Array(uint32_t *ans) const noexcept { + internal::roaring_bitmap_to_uint32_array(&roaring, ans); + } + /** + * To int array with pagination + */ + void rangeUint32Array(uint32_t *ans, size_t offset, size_t limit) const noexcept { + internal::roaring_bitmap_range_uint32_array(&roaring, offset, limit, ans); + } + + /** + * Return true if the two bitmaps contain the same elements. + */ + bool operator==(const Roaring &r) const noexcept { + return internal::roaring_bitmap_equals(&roaring, &r.roaring); + } + + /** + * Compute the negation of the roaring bitmap within the half-open interval + * [range_start, range_end). Areas outside the interval are unchanged. + */ + void flip(uint64_t range_start, uint64_t range_end) noexcept { + internal::roaring_bitmap_flip_inplace(&roaring, range_start, range_end); + } + + /** + * Compute the negation of the roaring bitmap within the closed interval + * [range_start, range_end]. Areas outside the interval are unchanged. + */ + void flipClosed(uint32_t range_start, uint32_t range_end) noexcept { + internal::roaring_bitmap_flip_inplace( + &roaring, range_start, uint64_t(range_end) + 1); + } + + /** + * Remove run-length encoding even when it is more space efficient. + * Return whether a change was applied. + */ + bool removeRunCompression() noexcept { + return internal::roaring_bitmap_remove_run_compression(&roaring); + } + + /** + * Convert array and bitmap containers to run containers when it is more + * efficient; also convert from run containers when more space efficient. + * Returns true if the result has at least one run container. Additional + * savings might be possible by calling shrinkToFit(). + */ + bool runOptimize() noexcept { return internal::roaring_bitmap_run_optimize(&roaring); } + + /** + * If needed, reallocate memory to shrink the memory usage. Returns + * the number of bytes saved. + */ + size_t shrinkToFit() noexcept { return internal::roaring_bitmap_shrink_to_fit(&roaring); } + + /** + * Iterate over the bitmap elements. The function iterator is called once + * for all the values with ptr (can be NULL) as the second parameter of + * each call. + * + * roaring_iterator is simply a pointer to a function that returns bool + * (true means that the iteration should continue while false means that it + * should stop), and takes (uint32_t,void*) as inputs. + */ + void iterate(internal::roaring_iterator iterator, void *ptr) const { + internal::roaring_iterate(&roaring, iterator, ptr); + } + + /** + * Selects the value at index rnk in the bitmap, where the smallest value + * is at index 0. + * + * If the size of the roaring bitmap is strictly greater than rank, then + * this function returns true and sets element to the element of given rank. + * Otherwise, it returns false. + */ + bool select(uint32_t rnk, uint32_t *element) const noexcept { + return internal::roaring_bitmap_select(&roaring, rnk, element); + } + + /** + * Computes the size of the intersection between two bitmaps. + */ + uint64_t and_cardinality(const Roaring &r) const noexcept { + return internal::roaring_bitmap_and_cardinality(&roaring, &r.roaring); + } + + /** + * Check whether the two bitmaps intersect. + */ + bool intersect(const Roaring &r) const noexcept { + return internal::roaring_bitmap_intersect(&roaring, &r.roaring); + } + + /** + * Computes the Jaccard index between two bitmaps. (Also known as the + * Tanimoto distance, + * or the Jaccard similarity coefficient) + * + * The Jaccard index is undefined if both bitmaps are empty. + */ + double jaccard_index(const Roaring &r) const noexcept { + return internal::roaring_bitmap_jaccard_index(&roaring, &r.roaring); + } + + /** + * Computes the size of the union between two bitmaps. + */ + uint64_t or_cardinality(const Roaring &r) const noexcept { + return internal::roaring_bitmap_or_cardinality(&roaring, &r.roaring); + } + + /** + * Computes the size of the difference (andnot) between two bitmaps. + */ + uint64_t andnot_cardinality(const Roaring &r) const noexcept { + return internal::roaring_bitmap_andnot_cardinality(&roaring, &r.roaring); + } + + /** + * Computes the size of the symmetric difference (andnot) between two + * bitmaps. + */ + uint64_t xor_cardinality(const Roaring &r) const noexcept { + return internal::roaring_bitmap_xor_cardinality(&roaring, &r.roaring); + } + + /** + * Returns the number of integers that are smaller or equal to x. + * Thus the rank of the smallest element is one. If + * x is smaller than the smallest element, this function will return 0. + * The rank and select functions differ in convention: this function returns + * 1 when ranking the smallest value, but the select function returns the + * smallest value when using index 0. + */ + uint64_t rank(uint32_t x) const noexcept { + return internal::roaring_bitmap_rank(&roaring, x); + } + + /** + * Get `rank()` values in bulk. The values in `[begin .. end)` must be in Ascending order. + * possible implementation: for(auto* iter = begin; iter != end; ++iter) *(ans++) = rank(*iter); + */ + void rank_many(const uint32_t* begin, const uint32_t* end, uint64_t* ans) const noexcept { + return internal::roaring_bitmap_rank_many(&roaring, begin, end, ans); + } + + /** + * Returns the index of x in the set, index start from 0. + * If the set doesn't contain x , this function will return -1. + * The difference with rank function is that this function will return -1 + * when x isn't in the set, but the rank function will return a + * non-negative number. + */ + int64_t getIndex(uint32_t x) const noexcept { + return internal::roaring_bitmap_get_index(&roaring, x); + } + + /** + * Write a bitmap to a char buffer. This is meant to be compatible with + * the Java and Go versions. Returns how many bytes were written which + * should be getSizeInBytes(). + * + * Setting the portable flag to false enable a custom format that + * can save space compared to the portable format (e.g., for very + * sparse bitmaps). + * + * Boost users can serialize bitmaps in this manner: + * + * BOOST_SERIALIZATION_SPLIT_FREE(Roaring) + * namespace boost { + * namespace serialization { + * + * template + * void save(Archive& ar, const Roaring& bitmask, + * const unsigned int version) { + * std::size_t expected_size_in_bytes = bitmask.getSizeInBytes(); + * std::vector buffer(expected_size_in_bytes); + * std::size_t size_in_bytes = bitmask.write(buffer.data()); + * + * ar& size_in_bytes; + * ar& boost::serialization::make_binary_object(buffer.data(), + * size_in_bytes); + * } + * template + * void load(Archive& ar, Roaring& bitmask, + * const unsigned int version) { + * std::size_t size_in_bytes = 0; + * ar& size_in_bytes; + * std::vector buffer(size_in_bytes); + * ar& boost::serialization::make_binary_object(buffer.data(), + * size_in_bytes); + * bitmask = Roaring::readSafe(buffer.data(), size_in_bytes); + * } + * } // namespace serialization + * } // namespace boost + */ + size_t write(char *buf, bool portable = true) const noexcept { + if (portable) { + return internal::roaring_bitmap_portable_serialize(&roaring, buf); + } else { + return internal::roaring_bitmap_serialize(&roaring, buf); + } + } + + /** + * Read a bitmap from a serialized version. This is meant to be compatible + * with the Java and Go versions. + * + * Setting the portable flag to false enable a custom format that + * can save space compared to the portable format (e.g., for very + * sparse bitmaps). + * + * This function is unsafe in the sense that if you provide bad data, + * many, many bytes could be read. See also readSafe. + * + * The function may throw std::runtime_error if a bitmap could not be read. Not that even + * if it does not throw, the bitmap could still be unusable if the loaded + * data does not match the portable Roaring specification: you should + * ensure that the data you load come from a serialized bitmap. + */ + static Roaring read(const char *buf, bool portable = true) { + roaring_bitmap_t * r = portable + ? internal::roaring_bitmap_portable_deserialize(buf) + : internal::roaring_bitmap_deserialize(buf); + if (r == NULL) { + ROARING_TERMINATE("failed alloc while reading"); + } + return Roaring(r); + } + + /** + * Read a bitmap from a serialized version, reading no more than maxbytes + * bytes. This is meant to be compatible with the Java and Go versions. + * The function itself is safe in the sense that it will not cause buffer overflows. + * However, for correct operations, it is assumed that the bitmap read was once + * serialized from a valid bitmap. If you provided an incorrect input (garbage), then the + * bitmap read may not be in a valid state and following operations may not lead + * to sensible results. It is your responsability to ensure that the input bytes + * follow the format specification if you want a usable bitmap: + * https://github.com/RoaringBitmap/RoaringFormatSpec + * In particular, the serialized array containers need to be in sorted order, and the + * run containers should be in sorted non-overlapping order. This is is guaranteed to + * happen when serializing an existing bitmap, but not for random inputs. + * Note that this function assumes that your bitmap was serialized in *portable* mode + * (which is the default with the 'write' method). + * + * The function may throw std::runtime_error if a bitmap could not be read. Not that even + * if it does not throw, the bitmap could still be unusable if the loaded + * data does not match the portable Roaring specification: you should + * ensure that the data you load come from a serialized bitmap. + */ + static Roaring readSafe(const char *buf, size_t maxbytes) { + roaring_bitmap_t * r = + internal::roaring_bitmap_portable_deserialize_safe(buf,maxbytes); + if (r == NULL) { + ROARING_TERMINATE("failed alloc while reading"); + } + return Roaring(r); + } + + /** + * How many bytes are required to serialize this bitmap (meant to be + * compatible with Java and Go versions) + * + * Setting the portable flag to false enable a custom format that + * can save space compared to the portable format (e.g., for very + * sparse bitmaps). + */ + size_t getSizeInBytes(bool portable = true) const noexcept { + if (portable) { + return internal::roaring_bitmap_portable_size_in_bytes(&roaring); + } else { + return internal::roaring_bitmap_size_in_bytes(&roaring); + } + } + + /** + * For advanced users. + * This function may throw std::runtime_error. + */ + static const Roaring frozenView(const char *buf, size_t length) { + const roaring_bitmap_t *s = + internal::roaring_bitmap_frozen_view(buf, length); + if (s == NULL) { + ROARING_TERMINATE("failed to read frozen bitmap"); + } + Roaring r; + r.roaring = *s; + return r; + } + + /** + * For advanced users. + */ + void writeFrozen(char *buf) const noexcept { + roaring_bitmap_frozen_serialize(&roaring, buf); + } + + /** + * For advanced users. + */ + size_t getFrozenSizeInBytes() const noexcept { + return roaring_bitmap_frozen_size_in_bytes(&roaring); + } + + /** + * Computes the intersection between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + * Consider also using the operator &= to avoid needlessly creating + * many temporary bitmaps. + * This function may throw std::runtime_error. + */ + Roaring operator&(const Roaring &o) const { + roaring_bitmap_t *r = internal::roaring_bitmap_and(&roaring, &o.roaring); + if (r == NULL) { + ROARING_TERMINATE("failed materalization in and"); + } + return Roaring(r); + } + + /** + * Computes the difference between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + * This function may throw std::runtime_error. + */ + Roaring operator-(const Roaring &o) const { + roaring_bitmap_t *r = internal::roaring_bitmap_andnot(&roaring, &o.roaring); + if (r == NULL) { + ROARING_TERMINATE("failed materalization in andnot"); + } + return Roaring(r); + } + + /** + * Computes the union between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + * This function may throw std::runtime_error. + */ + Roaring operator|(const Roaring &o) const { + roaring_bitmap_t *r = internal::roaring_bitmap_or(&roaring, &o.roaring); + if (r == NULL) { + ROARING_TERMINATE("failed materalization in or"); + } + return Roaring(r); + } + + /** + * Computes the symmetric union between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + * This function may throw std::runtime_error. + */ + Roaring operator^(const Roaring &o) const { + roaring_bitmap_t *r = internal::roaring_bitmap_xor(&roaring, &o.roaring); + if (r == NULL) { + ROARING_TERMINATE("failed materalization in xor"); + } + return Roaring(r); + } + + /** + * Whether or not we apply copy and write. + */ + void setCopyOnWrite(bool val) noexcept { + internal::roaring_bitmap_set_copy_on_write(&roaring, val); + } + + /** + * Print the content of the bitmap + */ + void printf() const noexcept { internal::roaring_bitmap_printf(&roaring); } + + /** + * Print the content of the bitmap into a string + */ + std::string toString() const noexcept { + struct iter_data { + std::string str{}; // The empty constructor silences warnings from pedantic static analyzers. + char first_char = '{'; + } outer_iter_data; + if (!isEmpty()) { + iterate( + [](uint32_t value, void *inner_iter_data) -> bool { + ((iter_data *)inner_iter_data)->str += + ((iter_data *)inner_iter_data)->first_char; + ((iter_data *)inner_iter_data)->str += + std::to_string(value); + ((iter_data *)inner_iter_data)->first_char = ','; + return true; + }, + (void *)&outer_iter_data); + } else + outer_iter_data.str = '{'; + outer_iter_data.str += '}'; + return outer_iter_data.str; + } + + /** + * Whether or not copy and write is active. + */ + bool getCopyOnWrite() const noexcept { + return internal::roaring_bitmap_get_copy_on_write(&roaring); + } + + /** + * Computes the logical or (union) between "n" bitmaps (referenced by a + * pointer). + * This function may throw std::runtime_error. + */ + static Roaring fastunion(size_t n, const Roaring **inputs) { + const roaring_bitmap_t **x = + (const roaring_bitmap_t **)roaring_malloc(n * sizeof(roaring_bitmap_t *)); + if (x == NULL) { + ROARING_TERMINATE("failed memory alloc in fastunion"); + } + for (size_t k = 0; k < n; ++k) x[k] = &inputs[k]->roaring; + + roaring_bitmap_t *c_ans = internal::roaring_bitmap_or_many(n, x); + if (c_ans == NULL) { + roaring_free(x); + ROARING_TERMINATE("failed memory alloc in fastunion"); + } + Roaring ans(c_ans); + roaring_free(x); + return ans; + } + + typedef RoaringSetBitForwardIterator const_iterator; + + /** + * Returns an iterator that can be used to access the position of the set + * bits. The running time complexity of a full scan is proportional to the + * number of set bits: be aware that if you have long strings of 1s, this + * can be very inefficient. + * + * It can be much faster to use the toArray method if you want to retrieve + * the set bits. + */ + const_iterator begin() const; + + /** + * A bogus iterator that can be used together with begin() + * for constructions such as for (auto i = b.begin(); * i!=b.end(); ++i) {} + */ + const_iterator &end() const; + + roaring_bitmap_t roaring; +}; + +/** + * Used to go through the set bits. Not optimally fast, but convenient. + */ +class RoaringSetBitForwardIterator final { +public: + typedef std::forward_iterator_tag iterator_category; + typedef uint32_t *pointer; + typedef uint32_t &reference_type; + typedef uint32_t value_type; + typedef int32_t difference_type; + typedef RoaringSetBitForwardIterator type_of_iterator; + + /** + * Provides the location of the set bit. + */ + value_type operator*() const { return i.current_value; } + + bool operator<(const type_of_iterator &o) const { + if (!i.has_value) return false; + if (!o.i.has_value) return true; + return i.current_value < *o; + } + + bool operator<=(const type_of_iterator &o) const { + if (!o.i.has_value) return true; + if (!i.has_value) return false; + return i.current_value <= *o; + } + + bool operator>(const type_of_iterator &o) const { + if (!o.i.has_value) return false; + if (!i.has_value) return true; + return i.current_value > *o; + } + + bool operator>=(const type_of_iterator &o) const { + if (!i.has_value) return true; + if (!o.i.has_value) return false; + return i.current_value >= *o; + } + + /** + * Move the iterator to the first value >= val. + */ + void equalorlarger(uint32_t val) { + internal::roaring_move_uint32_iterator_equalorlarger(&i,val); + } + + type_of_iterator &operator++() { // ++i, must returned inc. value + internal::roaring_advance_uint32_iterator(&i); + return *this; + } + + type_of_iterator operator++(int) { // i++, must return orig. value + RoaringSetBitForwardIterator orig(*this); + internal::roaring_advance_uint32_iterator(&i); + return orig; + } + + type_of_iterator& operator--() { // prefix -- + internal::roaring_previous_uint32_iterator(&i); + return *this; + } + + type_of_iterator operator--(int) { // postfix -- + RoaringSetBitForwardIterator orig(*this); + internal::roaring_previous_uint32_iterator(&i); + return orig; + } + + bool operator==(const RoaringSetBitForwardIterator &o) const { + return i.current_value == *o && i.has_value == o.i.has_value; + } + + bool operator!=(const RoaringSetBitForwardIterator &o) const { + return i.current_value != *o || i.has_value != o.i.has_value; + } + + explicit RoaringSetBitForwardIterator(const Roaring &parent, + bool exhausted = false) { + if (exhausted) { + i.parent = &parent.roaring; + i.container_index = INT32_MAX; + i.has_value = false; + i.current_value = UINT32_MAX; + } else { + internal::roaring_init_iterator(&parent.roaring, &i); + } + } + + internal::roaring_uint32_iterator_t i{}; // The empty constructor silences warnings from pedantic static analyzers. +}; + +inline RoaringSetBitForwardIterator Roaring::begin() const { + return RoaringSetBitForwardIterator(*this); +} + +inline RoaringSetBitForwardIterator &Roaring::end() const { + static RoaringSetBitForwardIterator e(*this, true); + return e; +} + +} // namespace paimon::roaring + +#endif /* INCLUDE_ROARING_HH_ */ +/* end file cpp/roaring.hh */ +/* begin file cpp/roaring64map.hh */ +/** + * A C++ header for 64-bit Roaring Bitmaps, + * implemented by way of a map of many + * 32-bit Roaring Bitmaps. + * + * Reference (format specification) : + * https://github.com/RoaringBitmap/RoaringFormatSpec#extention-for-64-bit-implementations +*/ +#ifndef INCLUDE_ROARING_64_MAP_HH_ +#define INCLUDE_ROARING_64_MAP_HH_ + +#include +#include // PRIu64 macro +#include // for va_list handling in bitmapOf() +#include // for std::printf() in the printf() method +#include // for std::memcpy() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace paimon::roaring { + +using roaring::Roaring; + +class Roaring64MapSetBitForwardIterator; +class Roaring64MapSetBitBiDirectionalIterator; + +class Roaring64Map { + typedef internal::roaring_bitmap_t roaring_bitmap_t; + +public: + /** + * Create an empty bitmap + */ + Roaring64Map() = default; + + /** + * Construct a bitmap from a list of 32-bit integer values. + */ + Roaring64Map(size_t n, const uint32_t *data) { addMany(n, data); } + + /** + * Construct a bitmap from a list of 64-bit integer values. + */ + Roaring64Map(size_t n, const uint64_t *data) { addMany(n, data); } + + /** + * Construct a bitmap from an initializer list. + */ + Roaring64Map(std::initializer_list l) { + addMany(l.size(), l.begin()); + } + + /** + * Construct a 64-bit map from a 32-bit one + */ + explicit Roaring64Map(const Roaring &r) { emplaceOrInsert(0, r); } + + /** + * Construct a 64-bit map from a 32-bit rvalue + */ + explicit Roaring64Map(Roaring &&r) { emplaceOrInsert(0, std::move(r)); } + + /** + * Construct a roaring object from the C struct. + * + * Passing a NULL point is unsafe. + */ + explicit Roaring64Map(roaring_bitmap_t *s) { + emplaceOrInsert(0, Roaring(s)); + } + + Roaring64Map(const Roaring64Map& r) = default; + + Roaring64Map(Roaring64Map&& r) noexcept = default; + + /** + * Copy assignment operator. + */ + Roaring64Map &operator=(const Roaring64Map &r) = default; + + /** + * Move assignment operator. + */ + Roaring64Map &operator=(Roaring64Map &&r) noexcept = default; + + /** + * Assignment from an initializer list. + */ + Roaring64Map &operator=(std::initializer_list l) { + // Delegate to move assignment operator + *this = Roaring64Map(l); + return *this; + } + + /** + * Construct a bitmap from a list of uint64_t values. + */ + static Roaring64Map bitmapOf(size_t n...) { + Roaring64Map ans; + va_list vl; + va_start(vl, n); + for (size_t i = 0; i < n; i++) { + ans.add(va_arg(vl, uint64_t)); + } + va_end(vl); + return ans; + } + + /** + * Construct a bitmap from a list of uint64_t values. + * E.g., bitmapOfList({1,2,3}). + */ + static Roaring64Map bitmapOfList(std::initializer_list l) { + Roaring64Map ans; + ans.addMany(l.size(), l.begin()); + return ans; + } + + /** + * Adds value x. + */ + void add(uint32_t x) { + lookupOrCreateInner(0).add(x); + } + + /** + * Adds value x. + */ + void add(uint64_t x) { + lookupOrCreateInner(highBytes(x)).add(lowBytes(x)); + } + + /** + * Adds value x. + * Returns true if a new value was added, false if the value was already + * present. + */ + bool addChecked(uint32_t x) { + return lookupOrCreateInner(0).addChecked(x); + } + + /** + * Adds value x. + * Returns true if a new value was added, false if the value was already + * present. + */ + bool addChecked(uint64_t x) { + return lookupOrCreateInner(highBytes(x)).addChecked(lowBytes(x)); + } + + /** + * Adds all values in the half-open interval [min, max). + */ + void addRange(uint64_t min, uint64_t max) { + if (min >= max) { + return; + } + addRangeClosed(min, max - 1); + } + + /** + * Adds all values in the closed interval [min, max]. + */ + void addRangeClosed(uint32_t min, uint32_t max) { + lookupOrCreateInner(0).addRangeClosed(min, max); + } + + /** + * Adds all values in the closed interval [min, max] + */ + void addRangeClosed(uint64_t min, uint64_t max) { + if (min > max) { + return; + } + uint32_t start_high = highBytes(min); + uint32_t start_low = lowBytes(min); + uint32_t end_high = highBytes(max); + uint32_t end_low = lowBytes(max); + + // We put std::numeric_limits<>::max in parentheses to avoid a + // clash with the Windows.h header under Windows. + const uint32_t uint32_max = (std::numeric_limits::max)(); + + // Fill in any nonexistent slots with empty Roarings. This simplifies + // the logic below, allowing it to simply iterate over the map between + // 'start_high' and 'end_high' in a linear fashion. + auto current_iter = ensureRangePopulated(start_high, end_high); + + // If start and end land on the same inner bitmap, then we can do the + // whole operation in one call. + if (start_high == end_high) { + auto &bitmap = current_iter->second; + bitmap.addRangeClosed(start_low, end_low); + return; + } + + // Because start and end don't land on the same inner bitmap, + // we need to do this in multiple steps: + // 1. Partially fill the first bitmap with values from the closed + // interval [start_low, uint32_max] + // 2. Fill intermediate bitmaps completely: [0, uint32_max] + // 3. Partially fill the last bitmap with values from the closed + // interval [0, end_low] + auto num_intermediate_bitmaps = end_high - start_high - 1; + + // Step 1: Partially fill the first bitmap. + { + auto &bitmap = current_iter->second; + bitmap.addRangeClosed(start_low, uint32_max); + ++current_iter; + } + + // Step 2. Fill intermediate bitmaps completely. + if (num_intermediate_bitmaps != 0) { + auto &first_intermediate = current_iter->second; + first_intermediate.addRangeClosed(0, uint32_max); + ++current_iter; + + // Now make (num_intermediate_bitmaps - 1) copies of this. + for (uint32_t i = 1; i != num_intermediate_bitmaps; ++i) { + auto &next_intermediate = current_iter->second; + next_intermediate = first_intermediate; + ++current_iter; + } + } + + // Step 3: Partially fill the last bitmap. + auto &bitmap = current_iter->second; + bitmap.addRangeClosed(0, end_low); + } + + /** + * Adds 'n_args' values from the contiguous memory range starting at 'vals'. + */ + void addMany(size_t n_args, const uint32_t *vals) { + lookupOrCreateInner(0).addMany(n_args, vals); + } + + /** + * Adds 'n_args' values from the contiguous memory range starting at 'vals'. + */ + void addMany(size_t n_args, const uint64_t *vals) { + // Potentially reduce outer map lookups by optimistically + // assuming that adjacent values will belong to the same inner bitmap. + Roaring *last_inner_bitmap = nullptr; + uint32_t last_value_high = 0; + for (size_t lcv = 0; lcv < n_args; lcv++) { + auto value = vals[lcv]; + auto value_high = highBytes(value); + auto value_low = lowBytes(value); + if (last_inner_bitmap == nullptr || value_high != last_value_high) { + last_inner_bitmap = &lookupOrCreateInner(value_high); + last_value_high = value_high; + } + last_inner_bitmap->add(value_low); + } + } + + /** + * Removes value x. + */ + void remove(uint32_t x) { + auto iter = roarings.begin(); + // Since x is a uint32_t, highbytes(x) == 0. The inner bitmap we are + // looking for, if it exists, will be at the first slot of 'roarings'. + if (iter == roarings.end() || iter->first != 0) { + return; + } + auto &bitmap = iter->second; + bitmap.remove(x); + eraseIfEmpty(iter); + } + + /** + * Removes value x. + */ + void remove(uint64_t x) { + auto iter = roarings.find(highBytes(x)); + if (iter == roarings.end()) { + return; + } + auto &bitmap = iter->second; + bitmap.remove(lowBytes(x)); + eraseIfEmpty(iter); + } + + /** + * Removes value x + * Returns true if a new value was removed, false if the value was not + * present. + */ + bool removeChecked(uint32_t x) { + auto iter = roarings.begin(); + // Since x is a uint32_t, highbytes(x) == 0. The inner bitmap we are + // looking for, if it exists, will be at the first slot of 'roarings'. + if (iter == roarings.end() || iter->first != 0) { + return false; + } + auto &bitmap = iter->second; + if (!bitmap.removeChecked(x)) { + return false; + } + eraseIfEmpty(iter); + return true; + } + + /** + * Remove value x + * Returns true if a new value was removed, false if the value was not + * present. + */ + bool removeChecked(uint64_t x) { + auto iter = roarings.find(highBytes(x)); + if (iter == roarings.end()) { + return false; + } + auto &bitmap = iter->second; + if (!bitmap.removeChecked(lowBytes(x))) { + return false; + } + eraseIfEmpty(iter); + return true; + } + + /** + * Removes all values in the half-open interval [min, max). + */ + void removeRange(uint64_t min, uint64_t max) { + if (min >= max) { + return; + } + return removeRangeClosed(min, max - 1); + } + + /** + * Removes all values in the closed interval [min, max]. + */ + void removeRangeClosed(uint32_t min, uint32_t max) { + auto iter = roarings.begin(); + // Since min and max are uint32_t, highbytes(min or max) == 0. The inner + // bitmap we are looking for, if it exists, will be at the first slot of + // 'roarings'. + if (iter == roarings.end() || iter->first != 0) { + return; + } + auto &bitmap = iter->second; + bitmap.removeRangeClosed(min, max); + eraseIfEmpty(iter); + } + + /** + * Removes all values in the closed interval [min, max]. + */ + void removeRangeClosed(uint64_t min, uint64_t max) { + if (min > max) { + return; + } + uint32_t start_high = highBytes(min); + uint32_t start_low = lowBytes(min); + uint32_t end_high = highBytes(max); + uint32_t end_low = lowBytes(max); + + // We put std::numeric_limits<>::max in parentheses to avoid a + // clash with the Windows.h header under Windows. + const uint32_t uint32_max = (std::numeric_limits::max)(); + + // If the outer map is empty, end_high is less than the first key, + // or start_high is greater than the last key, then exit now because + // there is no work to do. + if (roarings.empty() || end_high < roarings.cbegin()->first || + start_high > (roarings.crbegin())->first) { + return; + } + + // If we get here, start_iter points to the first entry in the outer map + // with key >= start_high. Such an entry is known to exist (i.e. the + // iterator will not be equal to end()) because start_high <= the last + // key in the map (thanks to the above if statement). + auto start_iter = roarings.lower_bound(start_high); + // end_iter points to the first entry in the outer map with + // key >= end_high, if such a key exists. Otherwise, it equals end(). + auto end_iter = roarings.lower_bound(end_high); + + // Note that the 'lower_bound' method will find the start and end slots, + // if they exist; otherwise it will find the next-higher slots. + // In the case where 'start' landed on an existing slot, we need to do a + // partial erase of that slot, and likewise for 'end'. But all the slots + // in between can be fully erased. More precisely: + // + // 1. If the start point falls on an existing entry, there are two + // subcases: + // a. if the end point falls on that same entry, remove the closed + // interval [start_low, end_low] from that entry and we are done. + // b. Otherwise, remove the closed interval [start_low, uint32_max] + // from that entry, advance start_iter, and fall through to step 2. + // 2. Completely erase all slots in the half-open interval + // [start_iter, end_iter) + // 3. If the end point falls on an existing entry, remove the closed + // interval [0, end_high] from it. + + // Step 1. If the start point falls on an existing entry... + if (start_iter->first == start_high) { + auto &start_inner = start_iter->second; + // 1a. if the end point falls on that same entry... + if (start_iter == end_iter) { + start_inner.removeRangeClosed(start_low, end_low); + eraseIfEmpty(start_iter); + return; + } + + // 1b. Otherwise, remove the closed range [start_low, uint32_max]... + start_inner.removeRangeClosed(start_low, uint32_max); + // Advance start_iter, but keep the old value so we can check the + // bitmap we just modified for emptiness and erase if it necessary. + auto temp = start_iter++; + eraseIfEmpty(temp); + } + + // 2. Completely erase all slots in the half-open interval... + roarings.erase(start_iter, end_iter); + + // 3. If the end point falls on an existing entry... + if (end_iter != roarings.end() && end_iter->first == end_high) { + auto &end_inner = end_iter->second; + end_inner.removeRangeClosed(0, end_low); + eraseIfEmpty(end_iter); + } + } + + /** + * Clears the bitmap. + */ + void clear() { + roarings.clear(); + } + + /** + * Return the largest value (if not empty) + */ + uint64_t maximum() const { + for (auto roaring_iter = roarings.crbegin(); + roaring_iter != roarings.crend(); ++roaring_iter) { + if (!roaring_iter->second.isEmpty()) { + return uniteBytes(roaring_iter->first, + roaring_iter->second.maximum()); + } + } + // we put std::numeric_limits<>::max/min in parentheses + // to avoid a clash with the Windows.h header under Windows + return (std::numeric_limits::min)(); + } + + /** + * Return the smallest value (if not empty) + */ + uint64_t minimum() const { + for (auto roaring_iter = roarings.cbegin(); + roaring_iter != roarings.cend(); ++roaring_iter) { + if (!roaring_iter->second.isEmpty()) { + return uniteBytes(roaring_iter->first, + roaring_iter->second.minimum()); + } + } + // we put std::numeric_limits<>::max/min in parentheses + // to avoid a clash with the Windows.h header under Windows + return (std::numeric_limits::max)(); + } + + /** + * Check if value x is present + */ + bool contains(uint32_t x) const { + return roarings.count(0) == 0 ? false : roarings.at(0).contains(x); + } + bool contains(uint64_t x) const { + return roarings.count(highBytes(x)) == 0 + ? false + : roarings.at(highBytes(x)).contains(lowBytes(x)); + } + + /** + * Compute the intersection of the current bitmap and the provided bitmap, + * writing the result in the current bitmap. The provided bitmap is not + * modified. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + */ + Roaring64Map &operator&=(const Roaring64Map &other) { + if (this == &other) { + // ANDing *this with itself is a no-op. + return *this; + } + + // Logic table summarizing what to do when a given outer key is + // present vs. absent from self and other. + // + // self other (self & other) work to do + // -------------------------------------------- + // absent absent empty None + // absent present empty None + // present absent empty Erase self + // present present empty or not Intersect self with other, but + // erase self if result is empty. + // + // Because there is only work to do when a key is present in 'self', the + // main for loop iterates over entries in 'self'. + + decltype(roarings.begin()) self_next; + for (auto self_iter = roarings.begin(); self_iter != roarings.end(); + self_iter = self_next) { + // Do the 'next' operation now, so we don't have to worry about + // invalidation of self_iter down below with the 'erase' operation. + self_next = std::next(self_iter); + + auto self_key = self_iter->first; + auto &self_bitmap = self_iter->second; + + auto other_iter = other.roarings.find(self_key); + if (other_iter == other.roarings.end()) { + // 'other' doesn't have self_key. In the logic table above, + // this reflects the case (self.present & other.absent). + // So, erase self. + roarings.erase(self_iter); + continue; + } + + // Both sides have self_key. In the logic table above, this reflects + // the case (self.present & other.present). So, intersect self with + // other. + const auto &other_bitmap = other_iter->second; + self_bitmap &= other_bitmap; + if (self_bitmap.isEmpty()) { + // ...but if intersection is empty, remove it altogether. + roarings.erase(self_iter); + } + } + return *this; + } + + /** + * Compute the difference between the current bitmap and the provided + * bitmap, writing the result in the current bitmap. The provided bitmap + * is not modified. + */ + Roaring64Map &operator-=(const Roaring64Map &other) { + if (this == &other) { + // Subtracting *this from itself results in the empty map. + roarings.clear(); + return *this; + } + + // Logic table summarizing what to do when a given outer key is + // present vs. absent from self and other. + // + // self other (self - other) work to do + // -------------------------------------------- + // absent absent empty None + // absent present empty None + // present absent unchanged None + // present present empty or not Subtract other from self, but + // erase self if result is empty + // + // Because there is only work to do when a key is present in both 'self' + // and 'other', the main while loop ping-pongs back and forth until it + // finds the next key that is the same on both sides. + + auto self_iter = roarings.begin(); + auto other_iter = other.roarings.cbegin(); + + while (self_iter != roarings.end() && + other_iter != other.roarings.cend()) { + auto self_key = self_iter->first; + auto other_key = other_iter->first; + if (self_key < other_key) { + // Because self_key is < other_key, advance self_iter to the + // first point where self_key >= other_key (or end). + self_iter = roarings.lower_bound(other_key); + continue; + } + + if (self_key > other_key) { + // Because self_key is > other_key, advance other_iter to the + // first point where other_key >= self_key (or end). + other_iter = other.roarings.lower_bound(self_key); + continue; + } + + // Both sides have self_key. In the logic table above, this reflects + // the case (self.present & other.present). So subtract other from + // self. + auto &self_bitmap = self_iter->second; + const auto &other_bitmap = other_iter->second; + self_bitmap -= other_bitmap; + + if (self_bitmap.isEmpty()) { + // ...but if subtraction is empty, remove it altogether. + self_iter = roarings.erase(self_iter); + } else { + ++self_iter; + } + ++other_iter; + } + return *this; + } + + /** + * Compute the union of the current bitmap and the provided bitmap, + * writing the result in the current bitmap. The provided bitmap is not + * modified. + * + * See also the fastunion function to aggregate many bitmaps more quickly. + */ + Roaring64Map &operator|=(const Roaring64Map &other) { + if (this == &other) { + // ORing *this with itself is a no-op. + return *this; + } + + // Logic table summarizing what to do when a given outer key is + // present vs. absent from self and other. + // + // self other (self | other) work to do + // -------------------------------------------- + // absent absent empty None + // absent present not empty Copy other to self and set flags + // present absent unchanged None + // present present not empty self |= other + // + // Because there is only work to do when a key is present in 'other', + // the main for loop iterates over entries in 'other'. + + for (const auto &other_entry : other.roarings) { + const auto &other_bitmap = other_entry.second; + + // Try to insert other_bitmap into self at other_key. We take + // advantage of the fact that std::map::insert will not overwrite an + // existing entry. + auto insert_result = roarings.insert(other_entry); + auto self_iter = insert_result.first; + auto insert_happened = insert_result.second; + auto &self_bitmap = self_iter->second; + + if (insert_happened) { + // Key was not present in self, so insert was performed above. + // In the logic table above, this reflects the case + // (self.absent | other.present). Because the copy has already + // happened, thanks to the 'insert' operation above, we just + // need to set the copyOnWrite flag. + self_bitmap.setCopyOnWrite(copyOnWrite); + continue; + } + + // Both sides have self_key, and the insert was not performed. In + // the logic table above, this reflects the case + // (self.present & other.present). So OR other into self. + self_bitmap |= other_bitmap; + } + return *this; + } + + /** + * Compute the XOR of the current bitmap and the provided bitmap, writing + * the result in the current bitmap. The provided bitmap is not modified. + */ + Roaring64Map &operator^=(const Roaring64Map &other) { + if (this == &other) { + // XORing *this with itself results in the empty map. + roarings.clear(); + return *this; + } + + // Logic table summarizing what to do when a given outer key is + // present vs. absent from self and other. + // + // self other (self ^ other) work to do + // -------------------------------------------- + // absent absent empty None + // absent present non-empty Copy other to self and set flags + // present absent unchanged None + // present present empty or not XOR other into self, but erase self + // if result is empty. + // + // Because there is only work to do when a key is present in 'other', + // the main for loop iterates over entries in 'other'. + + for (const auto &other_entry : other.roarings) { + const auto &other_bitmap = other_entry.second; + + // Try to insert other_bitmap into self at other_key. We take + // advantage of the fact that std::map::insert will not overwrite an + // existing entry. + auto insert_result = roarings.insert(other_entry); + auto self_iter = insert_result.first; + auto insert_happened = insert_result.second; + auto &self_bitmap = self_iter->second; + + if (insert_happened) { + // Key was not present in self, so insert was performed above. + // In the logic table above, this reflects the case + // (self.absent ^ other.present). Because the copy has already + // happened, thanks to the 'insert' operation above, we just + // need to set the copyOnWrite flag. + self_bitmap.setCopyOnWrite(copyOnWrite); + continue; + } + + // Both sides have self_key, and the insert was not performed. In + // the logic table above, this reflects the case + // (self.present ^ other.present). So XOR other into self. + self_bitmap ^= other_bitmap; + + if (self_bitmap.isEmpty()) { + // ...but if intersection is empty, remove it altogether. + roarings.erase(self_iter); + } + } + return *this; + } + + /** + * Exchange the content of this bitmap with another. + */ + void swap(Roaring64Map &r) { roarings.swap(r.roarings); } + + /** + * Get the cardinality of the bitmap (number of elements). + * Throws std::length_error in the special case where the bitmap is full + * (cardinality() == 2^64). Check isFull() before calling to avoid + * exception. + */ + uint64_t cardinality() const { + if (isFull()) { +#if ROARING_EXCEPTIONS + throw std::length_error("bitmap is full, cardinality is 2^64, " + "unable to represent in a 64-bit integer"); +#else + ROARING_TERMINATE("bitmap is full, cardinality is 2^64, " + "unable to represent in a 64-bit integer"); +#endif + } + return std::accumulate( + roarings.cbegin(), roarings.cend(), (uint64_t)0, + [](uint64_t previous, + const std::pair &map_entry) { + return previous + map_entry.second.cardinality(); + }); + } + + /** + * Returns true if the bitmap is empty (cardinality is zero). + */ + bool isEmpty() const { + return std::all_of(roarings.cbegin(), roarings.cend(), + [](const std::pair &map_entry) { + return map_entry.second.isEmpty(); + }); + } + + /** + * Returns true if the bitmap is full (cardinality is max uint64_t + 1). + */ + bool isFull() const { + // only bother to check if map is fully saturated + // + // we put std::numeric_limits<>::max/min in parentheses + // to avoid a clash with the Windows.h header under Windows + return roarings.size() == + ((uint64_t)(std::numeric_limits::max)()) + 1 + ? std::all_of( + roarings.cbegin(), roarings.cend(), + [](const std::pair &roaring_map_entry) { + // roarings within map are saturated if cardinality + // is uint32_t max + 1 + return roaring_map_entry.second.cardinality() == + ((uint64_t) + (std::numeric_limits::max)()) + + 1; + }) + : false; + } + + /** + * Returns true if the bitmap is subset of the other. + */ + bool isSubset(const Roaring64Map &r) const { + for (const auto &map_entry : roarings) { + if (map_entry.second.isEmpty()) { + continue; + } + auto roaring_iter = r.roarings.find(map_entry.first); + if (roaring_iter == r.roarings.cend()) + return false; + else if (!map_entry.second.isSubset(roaring_iter->second)) + return false; + } + return true; + } + + /** + * Returns true if the bitmap is strict subset of the other. + * Throws std::length_error in the special case where the bitmap is full + * (cardinality() == 2^64). Check isFull() before calling to avoid exception. + */ + bool isStrictSubset(const Roaring64Map &r) const { + return isSubset(r) && cardinality() != r.cardinality(); + } + + /** + * Convert the bitmap to an array. Write the output to "ans", + * caller is responsible to ensure that there is enough memory + * allocated + * (e.g., ans = new uint32[mybitmap.cardinality()];) + */ + void toUint64Array(uint64_t *ans) const { + // Annoyingly, VS 2017 marks std::accumulate() as [[nodiscard]] + (void)std::accumulate(roarings.cbegin(), roarings.cend(), ans, + [](uint64_t *previous, + const std::pair &map_entry) { + for (uint32_t low_bits : map_entry.second) + *previous++ = + uniteBytes(map_entry.first, low_bits); + return previous; + }); + } + + /** + * Return true if the two bitmaps contain the same elements. + */ + bool operator==(const Roaring64Map &r) const { + // we cannot use operator == on the map because either side may contain + // empty Roaring Bitmaps + auto lhs_iter = roarings.cbegin(); + auto lhs_cend = roarings.cend(); + auto rhs_iter = r.roarings.cbegin(); + auto rhs_cend = r.roarings.cend(); + while (lhs_iter != lhs_cend && rhs_iter != rhs_cend) { + auto lhs_key = lhs_iter->first, rhs_key = rhs_iter->first; + const auto &lhs_map = lhs_iter->second, &rhs_map = rhs_iter->second; + if (lhs_map.isEmpty()) { + ++lhs_iter; + continue; + } + if (rhs_map.isEmpty()) { + ++rhs_iter; + continue; + } + if (!(lhs_key == rhs_key)) { + return false; + } + if (!(lhs_map == rhs_map)) { + return false; + } + ++lhs_iter; + ++rhs_iter; + } + while (lhs_iter != lhs_cend) { + if (!lhs_iter->second.isEmpty()) { + return false; + } + ++lhs_iter; + } + while (rhs_iter != rhs_cend) { + if (!rhs_iter->second.isEmpty()) { + return false; + } + ++rhs_iter; + } + return true; + } + + /** + * Computes the negation of the roaring bitmap within the half-open interval + * [min, max). Areas outside the interval are unchanged. + */ + void flip(uint64_t min, uint64_t max) { + if (min >= max) { + return; + } + flipClosed(min, max - 1); + } + + /** + * Computes the negation of the roaring bitmap within the closed interval + * [min, max]. Areas outside the interval are unchanged. + */ + void flipClosed(uint32_t min, uint32_t max) { + auto iter = roarings.begin(); + // Since min and max are uint32_t, highbytes(min or max) == 0. The inner + // bitmap we are looking for, if it exists, will be at the first slot of + // 'roarings'. If it does not exist, we have to create it. + if (iter == roarings.end() || iter->first != 0) { + iter = roarings.emplace_hint(iter, std::piecewise_construct, + std::forward_as_tuple(0), + std::forward_as_tuple()); + auto &bitmap = iter->second; + bitmap.setCopyOnWrite(copyOnWrite); + } + auto &bitmap = iter->second; + bitmap.flipClosed(min, max); + eraseIfEmpty(iter); + } + + /** + * Computes the negation of the roaring bitmap within the closed interval + * [min, max]. Areas outside the interval are unchanged. + */ + void flipClosed(uint64_t min, uint64_t max) { + if (min > max) { + return; + } + uint32_t start_high = highBytes(min); + uint32_t start_low = lowBytes(min); + uint32_t end_high = highBytes(max); + uint32_t end_low = lowBytes(max); + + // We put std::numeric_limits<>::max in parentheses to avoid a + // clash with the Windows.h header under Windows. + const uint32_t uint32_max = (std::numeric_limits::max)(); + + // Fill in any nonexistent slots with empty Roarings. This simplifies + // the logic below, allowing it to simply iterate over the map between + // 'start_high' and 'end_high' in a linear fashion. + auto current_iter = ensureRangePopulated(start_high, end_high); + + // If start and end land on the same inner bitmap, then we can do the + // whole operation in one call. + if (start_high == end_high) { + auto &bitmap = current_iter->second; + bitmap.flipClosed(start_low, end_low); + eraseIfEmpty(current_iter); + return; + } + + // Because start and end don't land on the same inner bitmap, + // we need to do this in multiple steps: + // 1. Partially flip the first bitmap in the closed interval + // [start_low, uint32_max] + // 2. Flip intermediate bitmaps completely: [0, uint32_max] + // 3. Partially flip the last bitmap in the closed interval + // [0, end_low] + + auto num_intermediate_bitmaps = end_high - start_high - 1; + + // 1. Partially flip the first bitmap. + { + auto &bitmap = current_iter->second; + bitmap.flipClosed(start_low, uint32_max); + auto temp = current_iter++; + eraseIfEmpty(temp); + } + + // 2. Flip intermediate bitmaps completely. + for (uint32_t i = 0; i != num_intermediate_bitmaps; ++i) { + auto &bitmap = current_iter->second; + bitmap.flipClosed(0, uint32_max); + auto temp = current_iter++; + eraseIfEmpty(temp); + } + + // 3. Partially flip the last bitmap. + auto &bitmap = current_iter->second; + bitmap.flipClosed(0, end_low); + eraseIfEmpty(current_iter); + } + + /** + * Remove run-length encoding even when it is more space efficient + * return whether a change was applied + */ + bool removeRunCompression() { + return std::accumulate( + roarings.begin(), roarings.end(), true, + [](bool previous, std::pair &map_entry) { + return map_entry.second.removeRunCompression() && previous; + }); + } + + /** + * Convert array and bitmap containers to run containers when it is more + * efficient; also convert from run containers when more space efficient. + * Returns true if the result has at least one run container. + * Additional savings might be possible by calling shrinkToFit(). + */ + bool runOptimize() { + return std::accumulate( + roarings.begin(), roarings.end(), true, + [](bool previous, std::pair &map_entry) { + return map_entry.second.runOptimize() && previous; + }); + } + + /** + * If needed, reallocate memory to shrink the memory usage. + * Returns the number of bytes saved. + */ + size_t shrinkToFit() { + size_t savedBytes = 0; + auto iter = roarings.begin(); + while (iter != roarings.cend()) { + if (iter->second.isEmpty()) { + // empty Roarings are 84 bytes + savedBytes += 88; + roarings.erase(iter++); + } else { + savedBytes += iter->second.shrinkToFit(); + iter++; + } + } + return savedBytes; + } + + /** + * Iterate over the bitmap elements in order(start from the smallest one) + * and call iterator once for every element until the iterator function + * returns false. To iterate over all values, the iterator function should + * always return true. + * + * The roaring_iterator64 parameter is a pointer to a function that + * returns bool (true means that the iteration should continue while false + * means that it should stop), and takes (uint64_t element, void* ptr) as + * inputs. + */ + void iterate(internal::roaring_iterator64 iterator, void *ptr) const { + for (const auto &map_entry : roarings) { + bool should_continue = + roaring_iterate64(&map_entry.second.roaring, iterator, + uint64_t(map_entry.first) << 32, ptr); + if (!should_continue) { + break; + } + } + } + + /** + * Selects the value at index 'rank' in the bitmap, where the smallest value + * is at index 0. If 'rank' < cardinality(), returns true with *element set + * to the element of the specified rank. Otherwise, returns false and the + * contents of *element are unspecified. + */ + bool select(uint64_t rank, uint64_t *element) const { + for (const auto &map_entry : roarings) { + auto key = map_entry.first; + const auto &bitmap = map_entry.second; + + uint64_t sub_cardinality = bitmap.cardinality(); + if (rank < sub_cardinality) { + uint32_t low_bytes; + // Casting rank to uint32_t is safe because + // rank < sub_cardinality and sub_cardinality <= 2^32. + if (!bitmap.select((uint32_t)rank, &low_bytes)) { + ROARING_TERMINATE("Logic error: bitmap.select() " + "returned false despite rank < cardinality()"); + } + *element = uniteBytes(key, low_bytes); + return true; + } + rank -= sub_cardinality; + } + return false; + } + + /** + * Returns the number of integers that are smaller or equal to x. + */ + uint64_t rank(uint64_t x) const { + uint64_t result = 0; + // Find the first bitmap >= x's bucket. If that is the bucket x would be in, find it's rank in that bucket. + // Either way, we're left with a range of all buckets strictly smaller than x's bucket, add all their + // cardinalities together. + auto end = roarings.lower_bound(highBytes(x)); + if (end != roarings.cend() && end->first == highBytes(x)) { + result += end->second.rank(lowBytes(x)); + } + for (auto iter = roarings.cbegin(); iter != end; ++iter) { + result += iter->second.cardinality(); + } + return result; + } + + /** + * Returns the index of x in the set, index start from 0. + * If the set doesn't contain x , this function will return -1. + * The difference with rank function is that this function will return -1 + * when x isn't in the set, but the rank function will return a + * non-negative number. + */ + int64_t getIndex(uint64_t x) const { + int64_t index = 0; + auto roaring_destination = roarings.find(highBytes(x)); + if (roaring_destination != roarings.cend()) { + for (auto roaring_iter = roarings.cbegin(); + roaring_iter != roaring_destination; ++roaring_iter) { + index += roaring_iter->second.cardinality(); + } + auto low_idx = roaring_destination->second.getIndex(lowBytes(x)); + if (low_idx < 0) return -1; + index += low_idx; + return index; + } + return -1; + } + + /** + * Write a bitmap to a char buffer. This is meant to be compatible with + * the Java and Go versions. Returns how many bytes were written which + * should be getSizeInBytes(). + * + * Setting the portable flag to false enables a custom format that + * can save space compared to the portable format (e.g., for very + * sparse bitmaps). + */ + size_t write(char *buf, bool portable = true) const { + const char *orig = buf; + // push map size + uint64_t map_size = roarings.size(); + std::memcpy(buf, &map_size, sizeof(uint64_t)); + buf += sizeof(uint64_t); + std::for_each( + roarings.cbegin(), roarings.cend(), + [&buf, portable](const std::pair &map_entry) { + // push map key + std::memcpy(buf, &map_entry.first, sizeof(uint32_t)); + // ^-- Note: `*((uint32_t*)buf) = map_entry.first;` is undefined + + buf += sizeof(uint32_t); + // push map value Roaring + buf += map_entry.second.write(buf, portable); + }); + return buf - orig; + } + + /** + * Read a bitmap from a serialized version. This is meant to be compatible + * with the Java and Go versions. + * + * Setting the portable flag to false enable a custom format that + * can save space compared to the portable format (e.g., for very + * sparse bitmaps). + * + * This function is unsafe in the sense that if you provide bad data, many + * bytes could be read, possibly causing a buffer overflow. See also + * readSafe. + */ + static Roaring64Map read(const char *buf, bool portable = true) { + Roaring64Map result; + // get map size + uint64_t map_size; + std::memcpy(&map_size, buf, sizeof(uint64_t)); + buf += sizeof(uint64_t); + for (uint64_t lcv = 0; lcv < map_size; lcv++) { + // get map key + uint32_t key; + std::memcpy(&key, buf, sizeof(uint32_t)); + // ^-- Note: `uint32_t key = *((uint32_t*)buf);` is undefined + + buf += sizeof(uint32_t); + // read map value Roaring + Roaring read_var = Roaring::read(buf, portable); + // forward buffer past the last Roaring Bitmap + buf += read_var.getSizeInBytes(portable); + result.emplaceOrInsert(key, std::move(read_var)); + } + return result; + } + + /** + * Read a bitmap from a serialized version, reading no more than maxbytes + * bytes. This is meant to be compatible with the Java and Go versions. + * + * Setting the portable flag to false enable a custom format that can save + * space compared to the portable format (e.g., for very sparse bitmaps). + */ + static Roaring64Map readSafe(const char *buf, size_t maxbytes) { + if (maxbytes < sizeof(uint64_t)) { + ROARING_TERMINATE("ran out of bytes"); + } + Roaring64Map result; + uint64_t map_size; + std::memcpy(&map_size, buf, sizeof(uint64_t)); + buf += sizeof(uint64_t); + maxbytes -= sizeof(uint64_t); + for (uint64_t lcv = 0; lcv < map_size; lcv++) { + if(maxbytes < sizeof(uint32_t)) { + ROARING_TERMINATE("ran out of bytes"); + } + uint32_t key; + std::memcpy(&key, buf, sizeof(uint32_t)); + // ^-- Note: `uint32_t key = *((uint32_t*)buf);` is undefined + + buf += sizeof(uint32_t); + maxbytes -= sizeof(uint32_t); + // read map value Roaring + Roaring read_var = Roaring::readSafe(buf, maxbytes); + // forward buffer past the last Roaring Bitmap + size_t tz = read_var.getSizeInBytes(true); + buf += tz; + maxbytes -= tz; + result.emplaceOrInsert(key, std::move(read_var)); + } + return result; + } + + /** + * Return the number of bytes required to serialize this bitmap (meant to + * be compatible with Java and Go versions) + * + * Setting the portable flag to false enable a custom format that can save + * space compared to the portable format (e.g., for very sparse bitmaps). + */ + size_t getSizeInBytes(bool portable = true) const { + // start with, respectively, map size and size of keys for each map + // entry + return std::accumulate( + roarings.cbegin(), roarings.cend(), + sizeof(uint64_t) + roarings.size() * sizeof(uint32_t), + [=](size_t previous, + const std::pair &map_entry) { + // add in bytes used by each Roaring + return previous + map_entry.second.getSizeInBytes(portable); + }); + } + + static const Roaring64Map frozenView(const char *buf) { + // size of bitmap buffer and key + const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t); + + Roaring64Map result; + + // get map size + uint64_t map_size; + memcpy(&map_size, buf, sizeof(uint64_t)); + buf += sizeof(uint64_t); + + for (uint64_t lcv = 0; lcv < map_size; lcv++) { + // pad to 32 bytes minus the metadata size + while (((uintptr_t)buf + metadata_size) % 32 != 0) buf++; + + // get bitmap size + size_t len; + memcpy(&len, buf, sizeof(size_t)); + buf += sizeof(size_t); + + // get map key + uint32_t key; + memcpy(&key, buf, sizeof(uint32_t)); + buf += sizeof(uint32_t); + + // read map value Roaring + const Roaring read = Roaring::frozenView(buf, len); + result.emplaceOrInsert(key, read); + + // forward buffer past the last Roaring Bitmap + buf += len; + } + return result; + } + + // As with serialized 64-bit bitmaps, 64-bit frozen bitmaps are serialized + // by concatenating one or more Roaring::write output buffers with the + // preceeding map key. Unlike standard bitmap serialization, frozen bitmaps + // must be 32-byte aligned and requires a buffer length to parse. As a + // result, each concatenated output of Roaring::writeFrozen is preceeded by + // padding, the buffer size (size_t), and the map key (uint32_t). The + // padding is used to ensure 32-byte alignment, but since it is followed by + // the buffer size and map key, it actually pads to `(x - sizeof(size_t) + + // sizeof(uint32_t)) mod 32` to leave room for the metadata. + void writeFrozen(char *buf) const { + // size of bitmap buffer and key + const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t); + + // push map size + uint64_t map_size = roarings.size(); + memcpy(buf, &map_size, sizeof(uint64_t)); + buf += sizeof(uint64_t); + + for (auto &map_entry : roarings) { + size_t frozenSizeInBytes = map_entry.second.getFrozenSizeInBytes(); + + // pad to 32 bytes minus the metadata size + while (((uintptr_t)buf + metadata_size) % 32 != 0) buf++; + + // push bitmap size + memcpy(buf, &frozenSizeInBytes, sizeof(size_t)); + buf += sizeof(size_t); + + // push map key + memcpy(buf, &map_entry.first, sizeof(uint32_t)); + buf += sizeof(uint32_t); + + // push map value Roaring + map_entry.second.writeFrozen(buf); + buf += map_entry.second.getFrozenSizeInBytes(); + } + } + + size_t getFrozenSizeInBytes() const { + // size of bitmap size and map key + const size_t metadata_size = sizeof(size_t) + sizeof(uint32_t); + size_t ret = 0; + + // map size + ret += sizeof(uint64_t); + + for (auto &map_entry : roarings) { + // pad to 32 bytes minus the metadata size + while ((ret + metadata_size) % 32 != 0) ret++; + ret += metadata_size; + + // frozen bitmaps must be 32-byte aligned + ret += map_entry.second.getFrozenSizeInBytes(); + } + return ret; + } + + /** + * Computes the intersection between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + * + * Performance hint: if you are computing the intersection between several + * bitmaps, two-by-two, it is best to start with the smallest bitmap. + * Consider also using the operator &= to avoid needlessly creating + * many temporary bitmaps. + */ + Roaring64Map operator&(const Roaring64Map &o) const { + return Roaring64Map(*this) &= o; + } + + /** + * Computes the difference between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + */ + Roaring64Map operator-(const Roaring64Map &o) const { + return Roaring64Map(*this) -= o; + } + + /** + * Computes the union between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + */ + Roaring64Map operator|(const Roaring64Map &o) const { + return Roaring64Map(*this) |= o; + } + + /** + * Computes the symmetric union between two bitmaps and returns new bitmap. + * The current bitmap and the provided bitmap are unchanged. + */ + Roaring64Map operator^(const Roaring64Map &o) const { + return Roaring64Map(*this) ^= o; + } + + /** + * Whether or not we apply copy and write. + */ + void setCopyOnWrite(bool val) { + if (copyOnWrite == val) return; + copyOnWrite = val; + std::for_each(roarings.begin(), roarings.end(), + [=](std::pair &map_entry) { + map_entry.second.setCopyOnWrite(val); + }); + } + + /** + * Print the contents of the bitmap to stdout. + * Note: this method adds a final newline, but toString() does not. + */ + void printf() const { + auto sink = [](const std::string &s) { + fputs(s.c_str(), stdout); + }; + printToSink(sink); + sink("\n"); + } + + /** + * Print the contents of the bitmap into a string. + */ + std::string toString() const { + std::string result; + auto sink = [&result](const std::string &s) { + result += s; + }; + printToSink(sink); + return result; + } + + /** + * Whether or not copy and write is active. + */ + bool getCopyOnWrite() const { return copyOnWrite; } + + /** + * Computes the logical or (union) between "n" bitmaps (referenced by a + * pointer). + */ + static Roaring64Map fastunion(size_t n, const Roaring64Map **inputs) { + // The strategy here is to basically do a "group by" operation. + // We group the input roarings by key, do a 32-bit + // roaring_bitmap_or_many on each group, and collect the results. + // We accomplish the "group by" operation using a priority queue, which + // tracks the next key for each of our input maps. At each step, our + // algorithm takes the next subset of maps that share the same next key, + // runs roaring_bitmap_or_many on those bitmaps, and then advances the + // current_iter on all the affected entries and then repeats. + + // There is an entry in our priority queue for each of the 'n' inputs. + // For a given Roaring64Map, we look at its underlying 'roarings' + // std::map, and take its begin() and end(). This forms our half-open + // interval [current_iter, end_iter), which we keep in the priority + // queue as a pq_entry. These entries are updated (removed and then + // reinserted with the pq_entry.iterator field advanced by one step) as + // our algorithm progresses. But when a given interval becomes empty + // (i.e. pq_entry.iterator == pq_entry.end) it is not returned to the + // priority queue. + struct pq_entry { + roarings_t::const_iterator iterator; + roarings_t::const_iterator end; + }; + + // Custom comparator for the priority queue. + auto pq_comp = [](const pq_entry &lhs, const pq_entry &rhs) { + auto left_key = lhs.iterator->first; + auto right_key = rhs.iterator->first; + + // We compare in the opposite direction than normal because priority + // queues normally order from largest to smallest, but we want + // smallest to largest. + return left_key > right_key; + }; + + // Create and populate the priority queue. + std::priority_queue, decltype(pq_comp)> pq(pq_comp); + for (size_t i = 0; i < n; ++i) { + const auto &roarings = inputs[i]->roarings; + if (roarings.begin() != roarings.end()) { + pq.push({roarings.begin(), roarings.end()}); + } + } + + // A reusable vector that holds the pointers to the inner bitmaps that + // we pass to the underlying 32-bit fastunion operation. + std::vector group_bitmaps; + + // Summary of the algorithm: + // 1. While the priority queue is not empty: + // A. Get its lowest key. Call this group_key + // B. While the lowest entry in the priority queue has a key equal to + // group_key: + // 1. Remove this entry (the pair {current_iter, end_iter}) from + // the priority queue. + // 2. Add the bitmap pointed to by current_iter to a list of + // 32-bit bitmaps to process. + // 3. Advance current_iter. Now it will point to a bitmap entry + // with some key greater than group_key (or it will point to + // end()). + // 4. If current_iter != end_iter, reinsert the pair into the + // priority queue. + // C. Invoke the 32-bit roaring_bitmap_or_many() and add to result + Roaring64Map result; + while (!pq.empty()) { + // Find the next key (the lowest key) in the priority queue. + auto group_key = pq.top().iterator->first; + + // The purpose of the inner loop is to gather all the inner bitmaps + // that share "group_key" into "group_bitmaps" so that they can be + // fed to roaring_bitmap_or_many(). While we are doing this, we + // advance those iterators to their next value and reinsert them + // into the priority queue (unless they reach their end). + group_bitmaps.clear(); + while (!pq.empty()) { + auto candidate_current_iter = pq.top().iterator; + auto candidate_end_iter = pq.top().end; + + auto candidate_key = candidate_current_iter->first; + const auto &candidate_bitmap = candidate_current_iter->second; + + // This element will either be in the group (having + // key == group_key) or it will not be in the group (having + // key > group_key). (Note it cannot have key < group_key + // because of the ordered nature of the priority queue itself + // and the ordered nature of all the underlying roaring maps). + if (candidate_key != group_key) { + // This entry, and (thanks to the nature of the priority + // queue) all other entries as well, are all greater than + // group_key, so we're done collecting elements for the + // current group. Because of the way this loop was written, + // the group will will always contain at least one element. + break; + } + + group_bitmaps.push_back(&candidate_bitmap.roaring); + // Remove this entry from the priority queue. Note this + // invalidates pq.top() so make sure you don't have any dangling + // references to it. + pq.pop(); + + // Advance 'candidate_current_iter' and insert a new entry + // {candidate_current_iter, candidate_end_iter} into the + // priority queue (unless it has reached its end). + ++candidate_current_iter; + if (candidate_current_iter != candidate_end_iter) { + pq.push({candidate_current_iter, candidate_end_iter}); + } + } + + // Use the fast inner union to combine these. + auto *inner_result = roaring_bitmap_or_many(group_bitmaps.size(), + group_bitmaps.data()); + // Insert the 32-bit result at end of the 'roarings' map of the + // result we are building. + result.roarings.insert(result.roarings.end(), + std::make_pair(group_key, Roaring(inner_result))); + } + return result; + } + + friend class Roaring64MapSetBitForwardIterator; + friend class Roaring64MapSetBitBiDirectionalIterator; + + Roaring &getOrCreateInner(uint32_t high) { + return lookupOrCreateInner(high); + } + typedef Roaring64MapSetBitForwardIterator const_iterator; + typedef Roaring64MapSetBitBiDirectionalIterator const_bidirectional_iterator; + + /** + * Returns an iterator that can be used to access the position of the set + * bits. The running time complexity of a full scan is proportional to the + * number of set bits: be aware that if you have long strings of 1s, this + * can be very inefficient. + * + * It can be much faster to use the toArray method if you want to + * retrieve the set bits. + */ + const_iterator begin() const; + + /** + * A bogus iterator that can be used together with begin() + * for constructions such as: for (auto i = b.begin(); * i!=b.end(); ++i) {} + */ + const_iterator end() const; + +private: + typedef std::map roarings_t; + roarings_t roarings{}; // The empty constructor silences warnings from pedantic static analyzers. + bool copyOnWrite{false}; + static constexpr uint32_t highBytes(const uint64_t in) { return uint32_t(in >> 32); } + static constexpr uint32_t lowBytes(const uint64_t in) { return uint32_t(in); } + static constexpr uint64_t uniteBytes(const uint32_t highBytes, + const uint32_t lowBytes) { + return (uint64_t(highBytes) << 32) | uint64_t(lowBytes); + } + // this is needed to tolerate gcc's C++11 libstdc++ lacking emplace + // prior to version 4.8 + void emplaceOrInsert(const uint32_t key, const Roaring &value) { +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20130322 + roarings.insert(std::make_pair(key, value)); +#else + roarings.emplace(std::make_pair(key, value)); +#endif + } + + void emplaceOrInsert(const uint32_t key, Roaring &&value) { +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20130322 + roarings.insert(std::make_pair(key, std::move(value))); +#else + roarings.emplace(key, std::move(value)); +#endif + } + + /* + * Look up 'key' in the 'roarings' map. If it does not exist, create it. + * Also, set its copyOnWrite flag to 'copyOnWrite'. Then return a reference + * to the (already existing or newly created) inner bitmap. + */ + Roaring &lookupOrCreateInner(uint32_t key) { + auto &bitmap = roarings[key]; + bitmap.setCopyOnWrite(copyOnWrite); + return bitmap; + } + + /** + * Prints the contents of the bitmap to a caller-provided sink function. + */ + void printToSink(const std::function &sink) const { + sink("{"); + + // Storage for snprintf. Big enough to store the decimal representation + // of the largest uint64_t value and trailing \0. + char buffer[32]; + const char *separator = ""; + // Reusable, and therefore avoids many repeated heap allocations. + std::string callback_string; + for (const auto &entry : roarings) { + auto high_bits = entry.first; + const auto &bitmap = entry.second; + for (const auto low_bits : bitmap) { + auto value = uniteBytes(high_bits, low_bits); + snprintf(buffer, sizeof(buffer), "%" PRIu64, value); + callback_string = separator; + callback_string.append(buffer); + sink(callback_string); + separator = ","; + } + } + sink("}"); + } + + /** + * Ensures that every key in the closed interval [start_high, end_high] + * refers to a Roaring bitmap rather being an empty slot. Inserts empty + * Roaring bitmaps if necessary. The interval must be valid and non-empty. + * Returns an iterator to the bitmap at start_high. + */ + roarings_t::iterator ensureRangePopulated(uint32_t start_high, + uint32_t end_high) { + if (start_high > end_high) { + ROARING_TERMINATE("Logic error: start_high > end_high"); + } + // next_populated_iter points to the first entry in the outer map with + // key >= start_high, or end(). + auto next_populated_iter = roarings.lower_bound(start_high); + + // Use uint64_t to avoid an infinite loop when end_high == uint32_max. + roarings_t::iterator start_iter{}; // Definitely assigned in loop. + for (uint64_t slot = start_high; slot <= end_high; ++slot) { + roarings_t::iterator slot_iter; + if (next_populated_iter != roarings.end() && + next_populated_iter->first == slot) { + // 'slot' index has caught up to next_populated_iter. + // Note it here and advance next_populated_iter. + slot_iter = next_populated_iter++; + } else { + // 'slot' index has not yet caught up to next_populated_iter. + // Make a fresh entry {key = 'slot', value = Roaring()}, insert + // it just prior to next_populated_iter, and set its copy + // on write flag. We take pains to use emplace_hint and + // piecewise_construct to minimize effort. + slot_iter = roarings.emplace_hint( + next_populated_iter, std::piecewise_construct, + std::forward_as_tuple(uint32_t(slot)), + std::forward_as_tuple()); + auto &bitmap = slot_iter->second; + bitmap.setCopyOnWrite(copyOnWrite); + } + + // Make a note of the iterator of the starting slot. It will be + // needed for the return value. + if (slot == start_high) { + start_iter = slot_iter; + } + } + return start_iter; + } + + /** + * Erases the entry pointed to by 'iter' from the 'roarings' map. Warning: + * this invalidates 'iter'. + */ + void eraseIfEmpty(roarings_t::iterator iter) { + const auto &bitmap = iter->second; + if (bitmap.isEmpty()) { + roarings.erase(iter); + } + } +}; + +/** + * Used to go through the set bits. Not optimally fast, but convenient. + */ +class Roaring64MapSetBitForwardIterator { +public: + typedef std::forward_iterator_tag iterator_category; + typedef uint64_t *pointer; + typedef uint64_t &reference; + typedef uint64_t value_type; + typedef int64_t difference_type; + typedef Roaring64MapSetBitForwardIterator type_of_iterator; + + /** + * Provides the location of the set bit. + */ + value_type operator*() const { + return Roaring64Map::uniteBytes(map_iter->first, i.current_value); + } + + bool operator<(const type_of_iterator &o) const { + if (map_iter == map_end) return false; + if (o.map_iter == o.map_end) return true; + return **this < *o; + } + + bool operator<=(const type_of_iterator &o) const { + if (o.map_iter == o.map_end) return true; + if (map_iter == map_end) return false; + return **this <= *o; + } + + bool operator>(const type_of_iterator &o) const { + if (o.map_iter == o.map_end) return false; + if (map_iter == map_end) return true; + return **this > *o; + } + + bool operator>=(const type_of_iterator &o) const { + if (map_iter == map_end) return true; + if (o.map_iter == o.map_end) return false; + return **this >= *o; + } + + type_of_iterator &operator++() { // ++i, must returned inc. value + if (i.has_value == true) roaring_advance_uint32_iterator(&i); + while (!i.has_value) { + map_iter++; + if (map_iter == map_end) return *this; + roaring_init_iterator(&map_iter->second.roaring, &i); + } + return *this; + } + + type_of_iterator operator++(int) { // i++, must return orig. value + Roaring64MapSetBitForwardIterator orig(*this); + roaring_advance_uint32_iterator(&i); + while (!i.has_value) { + map_iter++; + if (map_iter == map_end) return orig; + roaring_init_iterator(&map_iter->second.roaring, &i); + } + return orig; + } + + bool move(const value_type& x) { + map_iter = p.lower_bound(Roaring64Map::highBytes(x)); + if (map_iter != p.cend()) { + roaring_init_iterator(&map_iter->second.roaring, &i); + if (map_iter->first == Roaring64Map::highBytes(x)) { + if (roaring_move_uint32_iterator_equalorlarger(&i, Roaring64Map::lowBytes(x))) + return true; + map_iter++; + if (map_iter == map_end) return false; + roaring_init_iterator(&map_iter->second.roaring, &i); + } + return true; + } + return false; + } + + bool operator==(const Roaring64MapSetBitForwardIterator &o) const { + if (map_iter == map_end && o.map_iter == o.map_end) return true; + if (o.map_iter == o.map_end) return false; + return **this == *o; + } + + bool operator!=(const Roaring64MapSetBitForwardIterator &o) const { + if (map_iter == map_end && o.map_iter == o.map_end) return false; + if (o.map_iter == o.map_end) return true; + return **this != *o; + } + + Roaring64MapSetBitForwardIterator &operator=(const Roaring64MapSetBitForwardIterator& r) { + map_iter = r.map_iter; + map_end = r.map_end; + i = r.i; + return *this; + } + + Roaring64MapSetBitForwardIterator(const Roaring64MapSetBitForwardIterator& r) + : p(r.p), + map_iter(r.map_iter), + map_end(r.map_end), + i(r.i) + {} + + Roaring64MapSetBitForwardIterator(const Roaring64Map &parent, + bool exhausted = false) + : p(parent.roarings), map_end(parent.roarings.cend()) { + if (exhausted || parent.roarings.empty()) { + map_iter = parent.roarings.cend(); + } else { + map_iter = parent.roarings.cbegin(); + roaring_init_iterator(&map_iter->second.roaring, &i); + while (!i.has_value) { + map_iter++; + if (map_iter == map_end) return; + roaring_init_iterator(&map_iter->second.roaring, &i); + } + } + } + +protected: + const std::map& p; + std::map::const_iterator map_iter{}; // The empty constructor silences warnings from pedantic static analyzers. + std::map::const_iterator map_end{}; // The empty constructor silences warnings from pedantic static analyzers. + internal::roaring_uint32_iterator_t i{}; // The empty constructor silences warnings from pedantic static analyzers. +}; + +class Roaring64MapSetBitBiDirectionalIterator final :public Roaring64MapSetBitForwardIterator { +public: + explicit Roaring64MapSetBitBiDirectionalIterator(const Roaring64Map &parent, + bool exhausted = false) + : Roaring64MapSetBitForwardIterator(parent, exhausted), map_begin(parent.roarings.cbegin()) + {} + + Roaring64MapSetBitBiDirectionalIterator &operator=(const Roaring64MapSetBitForwardIterator& r) { + *(Roaring64MapSetBitForwardIterator*)this = r; + return *this; + } + + Roaring64MapSetBitBiDirectionalIterator& operator--() { // --i, must return dec.value + if (map_iter == map_end) { + --map_iter; + roaring_init_iterator_last(&map_iter->second.roaring, &i); + if (i.has_value) return *this; + } + + roaring_previous_uint32_iterator(&i); + while (!i.has_value) { + if (map_iter == map_begin) return *this; + map_iter--; + roaring_init_iterator_last(&map_iter->second.roaring, &i); + } + return *this; + } + + Roaring64MapSetBitBiDirectionalIterator operator--(int) { // i--, must return orig. value + Roaring64MapSetBitBiDirectionalIterator orig(*this); + if (map_iter == map_end) { + --map_iter; + roaring_init_iterator_last(&map_iter->second.roaring, &i); + return orig; + } + + roaring_previous_uint32_iterator(&i); + while (!i.has_value) { + if (map_iter == map_begin) return orig; + map_iter--; + roaring_init_iterator_last(&map_iter->second.roaring, &i); + } + return orig; + } + +protected: + std::map::const_iterator map_begin; +}; + +inline Roaring64MapSetBitForwardIterator Roaring64Map::begin() const { + return Roaring64MapSetBitForwardIterator(*this); +} + +inline Roaring64MapSetBitForwardIterator Roaring64Map::end() const { + return Roaring64MapSetBitForwardIterator(*this, true); +} + +} // namespace paimon::roaring + +#endif /* INCLUDE_ROARING_64_MAP_HH_ */ +/* end file cpp/roaring64map.hh */ diff --git a/third_party/versions.txt b/third_party/versions.txt new file mode 100644 index 0000000..6029b85 --- /dev/null +++ b/third_party/versions.txt @@ -0,0 +1,135 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Adapted from Apache Arrow +# https://github.com/apache/arrow/blob/main/cpp/thirdparty/versions.txt + +# Toolchain library versions +# +# This file is used by `download_dependencies.sh` and cmake to figure out which +# version of a dependency to fetch. In order to add a new dependency, add a +# version variable, e.g. MY_DEP_VERSION and append an entry in the +# `DEPENDENCIES` array (see the comment on top of the declaration for the +# format). + +THIRDPARTY_MIRROR_URL= + +PAIMON_ZLIB_BUILD_VERSION=1.3.1 +PAIMON_ZLIB_BUILD_SHA256_CHECKSUM=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23 +PAIMON_ZLIB_PKG_NAME=zlib-${PAIMON_ZLIB_BUILD_VERSION}.tar.gz + +PAIMON_ZSTD_BUILD_VERSION=1.5.7 +PAIMON_ZSTD_BUILD_SHA256_CHECKSUM=eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 +PAIMON_ZSTD_PKG_NAME=zstd-${PAIMON_ZSTD_BUILD_VERSION}.tar.gz + +PAIMON_SNAPPY_BUILD_VERSION=1.1.10 +PAIMON_SNAPPY_BUILD_SHA256_CHECKSUM=49d831bffcc5f3d01482340fe5af59852ca2fe76c3e05df0e67203ebbe0f1d90 +PAIMON_SNAPPY_PKG_NAME=snappy-${PAIMON_SNAPPY_BUILD_VERSION}.tar.gz + +PAIMON_LZ4_BUILD_VERSION=v1.9.4 +PAIMON_LZ4_BUILD_SHA256_CHECKSUM=0b0e3aa07c8c063ddf40b082bdf7e37a1562bda40a0ff5272957f3e987e0e54b +PAIMON_LZ4_PKG_NAME=lz4-${PAIMON_LZ4_BUILD_VERSION}.tar.gz + +PAIMON_PROTOBUF_BUILD_VERSION=3.8.0 +PAIMON_PROTOBUF_BUILD_SHA256_CHECKSUM=b7220b41481011305bf9100847cf294393973e869973a9661046601959b2960b +PAIMON_PROTOBUF_PKG_NAME=protobuf-${PAIMON_PROTOBUF_BUILD_VERSION}.tar.gz + +PAIMON_TBB_BUILD_VERSION=v2021.13.0 +PAIMON_TBB_BUILD_SHA256_CHECKSUM=3ad5dd08954b39d113dc5b3f8a8dc6dc1fd5250032b7c491eb07aed5c94133e1 +PAIMON_TBB_PKG_NAME=tbb-${PAIMON_TBB_BUILD_VERSION}.tar.gz + +PAIMON_ORC_BUILD_VERSION=v2.1.1 +PAIMON_ORC_BUILD_SHA256_CHECKSUM=1f8eef537814fdcd003de13e49c6edb35427b45eb40bafd3355f775d99a0ff99 +PAIMON_ORC_PKG_NAME=orc-${PAIMON_ORC_BUILD_VERSION}.tar.gz + +PAIMON_GTEST_BUILD_VERSION=1.11.0 +PAIMON_GTEST_BUILD_SHA256_CHECKSUM=b4870bf121ff7795ba20d20bcdd8627b8e088f2d1dab299a031c1034eddc93d5 +PAIMON_GTEST_PKG_NAME=gtest-${PAIMON_GTEST_BUILD_VERSION}.tar.gz + +PAIMON_ARROW_BUILD_VERSION=17.0.0 +PAIMON_ARROW_BUILD_SHA256_CHECKSUM=9d280d8042e7cf526f8c28d170d93bfab65e50f94569f6a790982a878d8d898d +PAIMON_ARROW_PKG_NAME=apache-arrow-${PAIMON_ARROW_BUILD_VERSION}.tar.gz + +PAIMON_AVRO_BUILD_VERSION=c499eefb48aa2db906c7bca14a047223806f36db +PAIMON_AVRO_BUILD_SHA256_CHECKSUM=9771f1dcfe3c01aff7ff670e873e66d3406362f71941821d482de65f3d32d780 +PAIMON_AVRO_PKG_NAME=avro-${PAIMON_AVRO_BUILD_VERSION}.tar.gz + +PAIMON_FMT_BUILD_VERSION=11.2.0 +PAIMON_FMT_BUILD_SHA256_CHECKSUM=bc23066d87ab3168f27cef3e97d545fa63314f5c79df5ea444d41d56f962c6af +PAIMON_FMT_PKG_NAME=fmt-${PAIMON_FMT_BUILD_VERSION}.tar.gz + +PAIMON_GLOG_BUILD_VERSION=v0.7.1 +PAIMON_GLOG_BUILD_SHA256_CHECKSUM=00e4a87e87b7e7612f519a41e491f16623b12423620006f59f5688bfd8d13b08 +PAIMON_GLOG_PKG_NAME=glog-${PAIMON_GLOG_BUILD_VERSION}.tar.gz + +PAIMON_RE2_BUILD_VERSION=2022-06-01 +PAIMON_RE2_BUILD_SHA256_CHECKSUM=f89c61410a072e5cbcf8c27e3a778da7d6fd2f2b5b1445cd4f4508bee946ab0f +PAIMON_RE2_PKG_NAME=re2-${PAIMON_RE2_BUILD_VERSION}.tar.gz + +PAIMON_RAPIDJSON_BUILD_VERSION=232389d4f1012dddec4ef84861face2d2ba85709 +PAIMON_RAPIDJSON_BUILD_SHA256_CHECKSUM=b9290a9a6d444c8e049bd589ab804e0ccf2b05dc5984a19ed5ae75d090064806 +PAIMON_RAPIDJSON_PKG_NAME=rapidjson-${PAIMON_RAPIDJSON_BUILD_VERSION}.tar.gz + +PAIMON_JINDOSDK_C_BUILD_VERSION=6.10.2 +PAIMON_JINDOSDK_C_LINUX_X86_64_BUILD_SHA256_CHECKSUM=23e61c9815fab1cd88c369445bdbe1eab02cc09bafed3bb5118ecaf5b2fbc518 +PAIMON_JINDOSDK_C_LINUX_X86_64_PKG_NAME=jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-linux.tar.gz +PAIMON_JINDOSDK_C_LINUX_AARCH64_BUILD_SHA256_CHECKSUM=b4afbf6abaa9bad2e3c6bd6a02af2acd81f4dc418b313ef959e1c55f9fb13ebd +PAIMON_JINDOSDK_C_LINUX_AARCH64_PKG_NAME=jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-linux-el7-aarch64.tar.gz +PAIMON_JINDOSDK_C_MACOS_X86_64_BUILD_SHA256_CHECKSUM=3c79e2be018a486423fa63dd5ffe5a9a08477f9cf51ffdae81c0320e2190b489 +PAIMON_JINDOSDK_C_MACOS_X86_64_PKG_NAME=jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-macos-11_0-x86_64.tar.gz +PAIMON_JINDOSDK_C_MACOS_AARCH64_BUILD_SHA256_CHECKSUM=7db9e47b7311f6ffcaa661a4d73c989640f9bbbbd2ad48e35788aa99d44759db +PAIMON_JINDOSDK_C_MACOS_AARCH64_PKG_NAME=jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-macos-11_0-aarch64.tar.gz + +PAIMON_LUCENE_BUILD_VERSION=3.0.9 +PAIMON_LUCENE_BUILD_SHA256_CHECKSUM=4e69e29d5d79a976498ef71eab70c9c88c7014708be4450a9fda7780fe93584e +PAIMON_LUCENE_PKG_NAME=lucene-${PAIMON_LUCENE_BUILD_VERSION}.tar.gz + +PAIMON_LIMONP_BUILD_VERSION=1.0.1 +PAIMON_LIMONP_BUILD_SHA256_CHECKSUM=c7b18794f020dbaa1006229b49a39217a463da0cb3586aee83eb7471f4ae71df +PAIMON_LIMONP_PKG_NAME=limonp-${PAIMON_LIMONP_BUILD_VERSION}.tar.gz + +PAIMON_JIEBA_BUILD_VERSION=v5.6.0 +PAIMON_JIEBA_BUILD_SHA256_CHECKSUM=e6e517b778e0f4a99cbed1ee3eaa041616b74bc685e03a6ca08887ad9cedfe49 +PAIMON_JIEBA_PKG_NAME=jieba-${PAIMON_JIEBA_BUILD_VERSION}.tar.gz + +# The first field is the name of the environment variable expected by cmake. +# This _must_ match what is defined. The second field is the name of the +# generated archive file. The third field is the url of the project for the +# given version. +DEPENDENCIES=( + "PAIMON_ZLIB_URL ${PAIMON_ZLIB_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/madler/zlib/releases/download/v${PAIMON_ZLIB_BUILD_VERSION}/zlib-${PAIMON_ZLIB_BUILD_VERSION}.tar.gz" + "PAIMON_ZSTD_URL ${PAIMON_ZSTD_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/facebook/zstd/releases/download/v${PAIMON_ZSTD_BUILD_VERSION}/zstd-${PAIMON_ZSTD_BUILD_VERSION}.tar.gz" + "PAIMON_SNAPPY_URL ${PAIMON_SNAPPY_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/google/snappy/archive/${PAIMON_SNAPPY_BUILD_VERSION}.tar.gz" + "PAIMON_LZ4_URL ${PAIMON_LZ4_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/lz4/lz4/archive/${PAIMON_LZ4_BUILD_VERSION}.tar.gz" + "PAIMON_PROTOBUF_URL ${PAIMON_PROTOBUF_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/protocolbuffers/protobuf/releases/download/v${PAIMON_PROTOBUF_BUILD_VERSION}/protobuf-all-${PAIMON_PROTOBUF_BUILD_VERSION}.tar.gz" + "PAIMON_TBB_URL ${PAIMON_TBB_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/uxlfoundation/oneTBB/archive/refs/tags/${PAIMON_TBB_BUILD_VERSION}.tar.gz" + "PAIMON_ORC_URL ${PAIMON_ORC_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/apache/orc/archive/refs/tags/${PAIMON_ORC_BUILD_VERSION}.tar.gz" + "PAIMON_GTEST_URL ${PAIMON_GTEST_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/google/googletest/archive/release-${PAIMON_GTEST_BUILD_VERSION}.tar.gz" + "PAIMON_ARROW_URL ${PAIMON_ARROW_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/apache/arrow/releases/download/apache-arrow-${PAIMON_ARROW_BUILD_VERSION}/apache-arrow-${PAIMON_ARROW_BUILD_VERSION}.tar.gz" + "PAIMON_AVRO_URL ${PAIMON_AVRO_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/apache/avro/archive/${PAIMON_AVRO_BUILD_VERSION}.tar.gz" + "PAIMON_FMT_URL ${PAIMON_FMT_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/fmtlib/fmt/archive/refs/tags/${PAIMON_FMT_BUILD_VERSION}.tar.gz" + "PAIMON_GLOG_URL ${PAIMON_GLOG_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/google/glog/archive/${PAIMON_GLOG_BUILD_VERSION}.tar.gz" + "PAIMON_RAPIDJSON_URL ${PAIMON_RAPIDJSON_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/miloyip/rapidjson/archive/${PAIMON_RAPIDJSON_BUILD_VERSION}.tar.gz" + "PAIMON_RE2_URL ${PAIMON_RE2_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/google/re2/archive/${PAIMON_RE2_BUILD_VERSION}.tar.gz" + "PAIMON_JINDOSDK_C_LINUX_X86_64_URL ${PAIMON_JINDOSDK_C_LINUX_X86_64_PKG_NAME} https://jindodata-binary.oss-cn-shanghai.aliyuncs.com/release/${PAIMON_JINDOSDK_C_BUILD_VERSION}/jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-linux.tar.gz" + "PAIMON_JINDOSDK_C_LINUX_AARCH64_URL ${PAIMON_JINDOSDK_C_LINUX_AARCH64_PKG_NAME} https://jindodata-binary.oss-cn-shanghai.aliyuncs.com/release/${PAIMON_JINDOSDK_C_BUILD_VERSION}/jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-linux-el7-aarch64.tar.gz" + "PAIMON_JINDOSDK_C_MACOS_X86_64_URL ${PAIMON_JINDOSDK_C_MACOS_X86_64_PKG_NAME} https://jindodata-binary.oss-cn-shanghai.aliyuncs.com/release/${PAIMON_JINDOSDK_C_BUILD_VERSION}/jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-macos-11_0-x86_64.tar.gz" + "PAIMON_JINDOSDK_C_MACOS_AARCH64_URL ${PAIMON_JINDOSDK_C_MACOS_AARCH64_PKG_NAME} https://jindodata-binary.oss-cn-shanghai.aliyuncs.com/release/${PAIMON_JINDOSDK_C_BUILD_VERSION}/jindosdk-${PAIMON_JINDOSDK_C_BUILD_VERSION}-macos-11_0-aarch64.tar.gz" + "PAIMON_LUCENE_URL ${PAIMON_LUCENE_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/luceneplusplus/LucenePlusPlus/archive/refs/tags/rel_${PAIMON_LUCENE_BUILD_VERSION}.tar.gz" + "PAIMON_LIMONP_URL ${PAIMON_LIMONP_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/yanyiwu/limonp/archive/refs/tags/v${PAIMON_LIMONP_BUILD_VERSION}.tar.gz" + "PAIMON_JIEBA_URL ${PAIMON_JIEBA_PKG_NAME} ${THIRDPARTY_MIRROR_URL}https://github.com/yanyiwu/cppjieba/archive/refs/tags/${PAIMON_JIEBA_BUILD_VERSION}.tar.gz" +) diff --git a/third_party/xxhash/CMakeLists.txt b/third_party/xxhash/CMakeLists.txt new file mode 100644 index 0000000..e7c67f3 --- /dev/null +++ b/third_party/xxhash/CMakeLists.txt @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +cmake_minimum_required(VERSION 3.10) + +add_library(xxhash STATIC xxhash.c) +target_include_directories(xxhash PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/third_party/xxhash/xxhash.c b/third_party/xxhash/xxhash.c new file mode 100644 index 0000000..e60cc37 --- /dev/null +++ b/third_party/xxhash/xxhash.c @@ -0,0 +1,42 @@ +/* + * xxHash - Extremely Fast Hash algorithm + * Copyright (C) 2012-2023 Yann Collet + * + * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + * + * 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. + * + * 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. + * + * You can contact the author at: + * - xxHash homepage: https://www.xxhash.com + * - xxHash source repository: https://github.com/Cyan4973/xxHash + */ + +/* + * xxhash.c instantiates functions defined in xxhash.h + */ + +#define XXH_STATIC_LINKING_ONLY /* access advanced declarations */ +#define XXH_IMPLEMENTATION /* access definitions */ + +#include "xxhash.h" diff --git a/third_party/xxhash/xxhash.h b/third_party/xxhash/xxhash.h new file mode 100644 index 0000000..1ad2ddb --- /dev/null +++ b/third_party/xxhash/xxhash.h @@ -0,0 +1,7343 @@ +/* + * xxHash - Extremely Fast Hash algorithm + * Header File + * Copyright (C) 2012-2023 Yann Collet + * + * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) + * + * 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. + * + * 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. + * + * You can contact the author at: + * - xxHash homepage: https://www.xxhash.com + * - xxHash source repository: https://github.com/Cyan4973/xxHash + */ + +/*! + * @mainpage xxHash + * + * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed + * limits. + * + * It is proposed in four flavors, in three families: + * 1. @ref XXH32_family + * - Classic 32-bit hash function. Simple, compact, and runs on almost all + * 32-bit and 64-bit systems. + * 2. @ref XXH64_family + * - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most + * 64-bit systems (but _not_ 32-bit systems). + * 3. @ref XXH3_family + * - Modern 64-bit and 128-bit hash function family which features improved + * strength and performance across the board, especially on smaller data. + * It benefits greatly from SIMD and 64-bit without requiring it. + * + * Benchmarks + * --- + * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04. + * The open source benchmark program is compiled with clang v10.0 using -O3 flag. + * + * | Hash Name | ISA ext | Width | Large Data Speed | Small Data Velocity | + * | -------------------- | ------- | ----: | ---------------: | ------------------: | + * | XXH3_64bits() | @b AVX2 | 64 | 59.4 GB/s | 133.1 | + * | MeowHash | AES-NI | 128 | 58.2 GB/s | 52.5 | + * | XXH3_128bits() | @b AVX2 | 128 | 57.9 GB/s | 118.1 | + * | CLHash | PCLMUL | 64 | 37.1 GB/s | 58.1 | + * | XXH3_64bits() | @b SSE2 | 64 | 31.5 GB/s | 133.1 | + * | XXH3_128bits() | @b SSE2 | 128 | 29.6 GB/s | 118.1 | + * | RAM sequential read | | N/A | 28.0 GB/s | N/A | + * | ahash | AES-NI | 64 | 22.5 GB/s | 107.2 | + * | City64 | | 64 | 22.0 GB/s | 76.6 | + * | T1ha2 | | 64 | 22.0 GB/s | 99.0 | + * | City128 | | 128 | 21.7 GB/s | 57.7 | + * | FarmHash | AES-NI | 64 | 21.3 GB/s | 71.9 | + * | XXH64() | | 64 | 19.4 GB/s | 71.0 | + * | SpookyHash | | 64 | 19.3 GB/s | 53.2 | + * | Mum | | 64 | 18.0 GB/s | 67.0 | + * | CRC32C | SSE4.2 | 32 | 13.0 GB/s | 57.9 | + * | XXH32() | | 32 | 9.7 GB/s | 71.9 | + * | City32 | | 32 | 9.1 GB/s | 66.0 | + * | Blake3* | @b AVX2 | 256 | 4.4 GB/s | 8.1 | + * | Murmur3 | | 32 | 3.9 GB/s | 56.1 | + * | SipHash* | | 64 | 3.0 GB/s | 43.2 | + * | Blake3* | @b SSE2 | 256 | 2.4 GB/s | 8.1 | + * | HighwayHash | | 64 | 1.4 GB/s | 6.0 | + * | FNV64 | | 64 | 1.2 GB/s | 62.7 | + * | Blake2* | | 256 | 1.1 GB/s | 5.1 | + * | SHA1* | | 160 | 0.8 GB/s | 5.6 | + * | MD5* | | 128 | 0.6 GB/s | 7.8 | + * @note + * - Hashes which require a specific ISA extension are noted. SSE2 is also noted, + * even though it is mandatory on x64. + * - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic + * by modern standards. + * - Small data velocity is a rough average of algorithm's efficiency for small + * data. For more accurate information, see the wiki. + * - More benchmarks and strength tests are found on the wiki: + * https://github.com/Cyan4973/xxHash/wiki + * + * Usage + * ------ + * All xxHash variants use a similar API. Changing the algorithm is a trivial + * substitution. + * + * @pre + * For functions which take an input and length parameter, the following + * requirements are assumed: + * - The range from [`input`, `input + length`) is valid, readable memory. + * - The only exception is if the `length` is `0`, `input` may be `NULL`. + * - For C++, the objects must have the *TriviallyCopyable* property, as the + * functions access bytes directly as if it was an array of `unsigned char`. + * + * @anchor single_shot_example + * **Single Shot** + * + * These functions are stateless functions which hash a contiguous block of memory, + * immediately returning the result. They are the easiest and usually the fastest + * option. + * + * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits() + * + * @code{.c} + * #include + * #include "xxhash.h" + * + * // Example for a function which hashes a null terminated string with XXH32(). + * XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed) + * { + * // NULL pointers are only valid if the length is zero + * size_t length = (string == NULL) ? 0 : strlen(string); + * return XXH32(string, length, seed); + * } + * @endcode + * + * + * @anchor streaming_example + * **Streaming** + * + * These groups of functions allow incremental hashing of unknown size, even + * more than what would fit in a size_t. + * + * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset() + * + * @code{.c} + * #include + * #include + * #include "xxhash.h" + * // Example for a function which hashes a FILE incrementally with XXH3_64bits(). + * XXH64_hash_t hashFile(FILE* f) + * { + * // Allocate a state struct. Do not just use malloc() or new. + * XXH3_state_t* state = XXH3_createState(); + * assert(state != NULL && "Out of memory!"); + * // Reset the state to start a new hashing session. + * XXH3_64bits_reset(state); + * char buffer[4096]; + * size_t count; + * // Read the file in chunks + * while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) { + * // Run update() as many times as necessary to process the data + * XXH3_64bits_update(state, buffer, count); + * } + * // Retrieve the finalized hash. This will not change the state. + * XXH64_hash_t result = XXH3_64bits_digest(state); + * // Free the state. Do not use free(). + * XXH3_freeState(state); + * return result; + * } + * @endcode + * + * Streaming functions generate the xxHash value from an incremental input. + * This method is slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * An XXH state must first be allocated using `XXH*_createState()`. + * + * Start a new hash by initializing the state with a seed using `XXH*_reset()`. + * + * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. + * + * The function returns an error code, with 0 meaning OK, and any other value + * meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a + * digest, and generate new hash values later on by invoking `XXH*_digest()`. + * + * When done, release the state using `XXH*_freeState()`. + * + * + * @anchor canonical_representation_example + * **Canonical Representation** + * + * The default return values from XXH functions are unsigned 32, 64 and 128 bit + * integers. + * This the simplest and fastest format for further post-processing. + * + * However, this leaves open the question of what is the order on the byte level, + * since little and big endian conventions will store the same number differently. + * + * The canonical representation settles this issue by mandating big-endian + * convention, the same convention as human-readable numbers (large digits first). + * + * When writing hash values to storage, sending them over a network, or printing + * them, it's highly recommended to use the canonical representation to ensure + * portability across a wider range of systems, present and future. + * + * The following functions allow transformation of hash values to and from + * canonical format. + * + * XXH32_canonicalFromHash(), XXH32_hashFromCanonical(), + * XXH64_canonicalFromHash(), XXH64_hashFromCanonical(), + * XXH128_canonicalFromHash(), XXH128_hashFromCanonical(), + * + * @code{.c} + * #include + * #include "xxhash.h" + * + * // Example for a function which prints XXH32_hash_t in human readable format + * void printXxh32(XXH32_hash_t hash) + * { + * XXH32_canonical_t cano; + * XXH32_canonicalFromHash(&cano, hash); + * size_t i; + * for(i = 0; i < sizeof(cano.digest); ++i) { + * printf("%02x", cano.digest[i]); + * } + * printf("\n"); + * } + * + * // Example for a function which converts XXH32_canonical_t to XXH32_hash_t + * XXH32_hash_t convertCanonicalToXxh32(XXH32_canonical_t cano) + * { + * XXH32_hash_t hash = XXH32_hashFromCanonical(&cano); + * return hash; + * } + * @endcode + * + * + * @file xxhash.h + * xxHash prototypes and implementation + */ + +#if defined(__cplusplus) && !defined(XXH_NO_EXTERNC_GUARD) +extern "C" { +#endif + +/* **************************** + * INLINE mode + ******************************/ +/*! + * @defgroup public Public API + * Contains details on the public xxHash functions. + * @{ + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Gives access to internal state declaration, required for static allocation. + * + * Incompatible with dynamic linking, due to risks of ABI changes. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #include "xxhash.h" + * @endcode + */ +# define XXH_STATIC_LINKING_ONLY +/* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */ + +/*! + * @brief Gives access to internal definitions. + * + * Usage: + * @code{.c} + * #define XXH_STATIC_LINKING_ONLY + * #define XXH_IMPLEMENTATION + * #include "xxhash.h" + * @endcode + */ +# define XXH_IMPLEMENTATION +/* Do not undef XXH_IMPLEMENTATION for Doxygen */ + +/*! + * @brief Exposes the implementation and marks all functions as `inline`. + * + * Use these build macros to inline xxhash into the target unit. + * Inlining improves performance on small inputs, especially when the length is + * expressed as a compile-time constant: + * + * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html + * + * It also keeps xxHash symbols private to the unit, so they are not exported. + * + * Usage: + * @code{.c} + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * @endcode + * Do not compile and link xxhash.o as a separate object, as it is not useful. + */ +# define XXH_INLINE_ALL +# undef XXH_INLINE_ALL +/*! + * @brief Exposes the implementation without marking functions as inline. + */ +# define XXH_PRIVATE_API +# undef XXH_PRIVATE_API +/*! + * @brief Emulate a namespace by transparently prefixing all symbols. + * + * If you want to include _and expose_ xxHash functions from within your own + * library, but also want to avoid symbol collisions with other libraries which + * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix + * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE + * (therefore, avoid empty or numeric values). + * + * Note that no change is required within the calling program as long as it + * includes `xxhash.h`: Regular symbol names will be automatically translated + * by this header. + */ +# define XXH_NAMESPACE /* YOUR NAME HERE */ +# undef XXH_NAMESPACE +#endif + +#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ + && !defined(XXH_INLINE_ALL_31684351384) + /* this section should be traversed only once */ +# define XXH_INLINE_ALL_31684351384 + /* give access to the advanced API, required to compile implementations */ +# undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */ +# define XXH_STATIC_LINKING_ONLY + /* make all functions private */ +# undef XXH_PUBLIC_API +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((__unused__)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* note: this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif + + /* + * This part deals with the special case where a unit wants to inline xxHash, + * but "xxhash.h" has previously been included without XXH_INLINE_ALL, + * such as part of some previously included *.h header file. + * Without further action, the new include would just be ignored, + * and functions would effectively _not_ be inlined (silent failure). + * The following macros solve this situation by prefixing all inlined names, + * avoiding naming collision with previous inclusions. + */ + /* Before that, we unconditionally #undef all symbols, + * in case they were already defined with XXH_NAMESPACE. + * They will then be redefined for XXH_INLINE_ALL + */ +# undef XXH_versionNumber + /* XXH32 */ +# undef XXH32 +# undef XXH32_createState +# undef XXH32_freeState +# undef XXH32_reset +# undef XXH32_update +# undef XXH32_digest +# undef XXH32_copyState +# undef XXH32_canonicalFromHash +# undef XXH32_hashFromCanonical + /* XXH64 */ +# undef XXH64 +# undef XXH64_createState +# undef XXH64_freeState +# undef XXH64_reset +# undef XXH64_update +# undef XXH64_digest +# undef XXH64_copyState +# undef XXH64_canonicalFromHash +# undef XXH64_hashFromCanonical + /* XXH3_64bits */ +# undef XXH3_64bits +# undef XXH3_64bits_withSecret +# undef XXH3_64bits_withSeed +# undef XXH3_64bits_withSecretandSeed +# undef XXH3_createState +# undef XXH3_freeState +# undef XXH3_copyState +# undef XXH3_64bits_reset +# undef XXH3_64bits_reset_withSeed +# undef XXH3_64bits_reset_withSecret +# undef XXH3_64bits_update +# undef XXH3_64bits_digest +# undef XXH3_generateSecret + /* XXH3_128bits */ +# undef XXH128 +# undef XXH3_128bits +# undef XXH3_128bits_withSeed +# undef XXH3_128bits_withSecret +# undef XXH3_128bits_reset +# undef XXH3_128bits_reset_withSeed +# undef XXH3_128bits_reset_withSecret +# undef XXH3_128bits_reset_withSecretandSeed +# undef XXH3_128bits_update +# undef XXH3_128bits_digest +# undef XXH128_isEqual +# undef XXH128_cmp +# undef XXH128_canonicalFromHash +# undef XXH128_hashFromCanonical + /* Finally, free the namespace itself */ +# undef XXH_NAMESPACE + + /* employ the namespace for XXH_INLINE_ALL */ +# define XXH_NAMESPACE XXH_INLINE_ + /* + * Some identifiers (enums, type names) are not symbols, + * but they must nonetheless be renamed to avoid redeclaration. + * Alternative solution: do not redeclare them. + * However, this requires some #ifdefs, and has a more dispersed impact. + * Meanwhile, renaming can be achieved in a single place. + */ +# define XXH_IPREF(Id) XXH_NAMESPACE ## Id +# define XXH_OK XXH_IPREF(XXH_OK) +# define XXH_ERROR XXH_IPREF(XXH_ERROR) +# define XXH_errorcode XXH_IPREF(XXH_errorcode) +# define XXH32_canonical_t XXH_IPREF(XXH32_canonical_t) +# define XXH64_canonical_t XXH_IPREF(XXH64_canonical_t) +# define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t) +# define XXH32_state_s XXH_IPREF(XXH32_state_s) +# define XXH32_state_t XXH_IPREF(XXH32_state_t) +# define XXH64_state_s XXH_IPREF(XXH64_state_s) +# define XXH64_state_t XXH_IPREF(XXH64_state_t) +# define XXH3_state_s XXH_IPREF(XXH3_state_s) +# define XXH3_state_t XXH_IPREF(XXH3_state_t) +# define XXH128_hash_t XXH_IPREF(XXH128_hash_t) + /* Ensure the header is parsed again, even if it was previously included */ +# undef XXHASH_H_5627135585666179 +# undef XXHASH_H_STATIC_13879238742 +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/* **************************************************************** + * Stable API + *****************************************************************/ +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +/*! @brief Marks a global symbol. */ +#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) +# if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif + +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +/* XXH32 */ +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +/* XXH64 */ +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +/* XXH3_64bits */ +# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) +# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) +# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) +# define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) +# define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) +# define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) +# define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) +# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) +# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) +# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) +# define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) +# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) +# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) +# define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) +# define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) +/* XXH3_128bits */ +# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) +# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) +# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) +# define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) +# define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) +# define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) +# define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) +# define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) +# define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) +# define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) +# define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) +# define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) +# define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) +# define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) +# define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) +#endif + + +/* ************************************* +* Compiler specifics +***************************************/ + +/* specific declaration modes for Windows */ +#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) +# if defined(_WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) +# ifdef XXH_EXPORT +# define XXH_PUBLIC_API __declspec(dllexport) +# elif XXH_IMPORT +# define XXH_PUBLIC_API __declspec(dllimport) +# endif +# else +# define XXH_PUBLIC_API /* do nothing */ +# endif +#endif + +#if defined (__GNUC__) +# define XXH_CONSTF __attribute__((__const__)) +# define XXH_PUREF __attribute__((__pure__)) +# define XXH_MALLOCF __attribute__((__malloc__)) +#else +# define XXH_CONSTF /* disable */ +# define XXH_PUREF +# define XXH_MALLOCF +#endif + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 8 +#define XXH_VERSION_RELEASE 3 +/*! @brief Version number, encoded as two digits each */ +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) + +/*! + * @brief Obtains the xxHash version. + * + * This is mostly useful when xxHash is compiled as a shared library, + * since the returned value comes from the library, as opposed to header file. + * + * @return @ref XXH_VERSION_NUMBER of the invoked library. + */ +XXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void); + + +/* **************************** +* Common basic types +******************************/ +#include /* size_t */ +/*! + * @brief Exit code for the streaming API. + */ +typedef enum { + XXH_OK = 0, /*!< OK */ + XXH_ERROR /*!< Error */ +} XXH_errorcode; + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +#if defined(XXH_DOXYGEN) /* Don't show include */ +/*! + * @brief An unsigned 32-bit integer. + * + * Not necessarily defined to `uint32_t` but functionally equivalent. + */ +typedef uint32_t XXH32_hash_t; + +#elif !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# ifdef _AIX +# include +# else +# include +# endif + typedef uint32_t XXH32_hash_t; + +#else +# include +# if UINT_MAX == 0xFFFFFFFFUL + typedef unsigned int XXH32_hash_t; +# elif ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long XXH32_hash_t; +# else +# error "unsupported platform: need a 32-bit type" +# endif +#endif + +/*! + * @} + * + * @defgroup XXH32_family XXH32 family + * @ingroup public + * Contains functions used in the classic 32-bit xxHash algorithm. + * + * @note + * XXH32 is useful for older platforms, with no or poor 64-bit performance. + * Note that the @ref XXH3_family provides competitive speed for both 32-bit + * and 64-bit systems, and offers true 64/128 bit hash results. + * + * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families + * @see @ref XXH32_impl for implementation details + * @{ + */ + +/*! + * @brief Calculates the 32-bit hash of @p input using xxHash32. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 32-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 32-bit xxHash32 value. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); + +#ifndef XXH_NO_STREAM +/*! + * @typedef struct XXH32_state_s XXH32_state_t + * @brief The opaque state struct for the XXH32 streaming API. + * + * @see XXH32_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ +typedef struct XXH32_state_s XXH32_state_t; + +/*! + * @brief Allocates an @ref XXH32_state_t. + * + * @return An allocated pointer of @ref XXH32_state_t on success. + * @return `NULL` on failure. + * + * @note Must be freed with XXH32_freeState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void); +/*! + * @brief Frees an @ref XXH32_state_t. + * + * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). + * + * @return @ref XXH_OK. + * + * @note @p statePtr must be allocated with XXH32_createState(). + * + * @see @ref streaming_example "Streaming Example" + * + */ +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +/*! + * @brief Copies one @ref XXH32_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +/*! + * @brief Resets an @ref XXH32_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 32-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note This function resets and seeds a state. Call it before @ref XXH32_update(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH32_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); + +/*! + * @brief Returns the calculated hash value from an @ref XXH32_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated 32-bit xxHash32 value from that state. + * + * @note + * Calling XXH32_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ + +/******* Canonical representation *******/ + +/*! + * @brief Canonical (big endian) representation of @ref XXH32_hash_t. + */ +typedef struct { + unsigned char digest[4]; /*!< Hash bytes, big endian */ +} XXH32_canonical_t; + +/*! + * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. + * + * @param dst The @ref XXH32_canonical_t pointer to be stored to. + * @param hash The @ref XXH32_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); + +/*! + * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. + * + * @param src The @ref XXH32_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + + +/*! @cond Doxygen ignores this part */ +#ifdef __has_attribute +# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +# define XXH_HAS_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * C23 __STDC_VERSION__ number hasn't been specified yet. For now + * leave as `201711L` (C17 + 1). + * TODO: Update to correct value when its been specified. + */ +#define XXH_C23_VN 201711L +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* C-language Attributes are added in C23. */ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) && defined(__has_c_attribute) +# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) +#else +# define XXH_HAS_C_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define XXH_HAS_CPP_ATTRIBUTE(x) 0 +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute + * introduced in CPP17 and C23. + * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough + * C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough + */ +#if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough) +# define XXH_FALLTHROUGH [[fallthrough]] +#elif XXH_HAS_ATTRIBUTE(__fallthrough__) +# define XXH_FALLTHROUGH __attribute__ ((__fallthrough__)) +#else +# define XXH_FALLTHROUGH /* fallthrough */ +#endif +/*! @endcond */ + +/*! @cond Doxygen ignores this part */ +/* + * Define XXH_NOESCAPE for annotated pointers in public API. + * https://clang.llvm.org/docs/AttributeReference.html#noescape + * As of writing this, only supported by clang. + */ +#if XXH_HAS_ATTRIBUTE(noescape) +# define XXH_NOESCAPE __attribute__((__noescape__)) +#else +# define XXH_NOESCAPE +#endif +/*! @endcond */ + + +/*! + * @} + * @ingroup public + * @{ + */ + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +#if defined(XXH_DOXYGEN) /* don't include */ +/*! + * @brief An unsigned 64-bit integer. + * + * Not necessarily defined to `uint64_t` but functionally equivalent. + */ +typedef uint64_t XXH64_hash_t; +#elif !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# ifdef _AIX +# include +# else +# include +# endif + typedef uint64_t XXH64_hash_t; +#else +# include +# if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL + /* LP64 ABI says uint64_t is unsigned long */ + typedef unsigned long XXH64_hash_t; +# else + /* the following type must have a width of 64-bit */ + typedef unsigned long long XXH64_hash_t; +# endif +#endif + +/*! + * @} + * + * @defgroup XXH64_family XXH64 family + * @ingroup public + * @{ + * Contains functions used in the classic 64-bit xxHash algorithm. + * + * @note + * XXH3 provides competitive speed for both 32-bit and 64-bit systems, + * and offers true 64/128 bit hash results. + * It provides better speed for systems with vector processing capabilities. + */ + +/*! + * @brief Calculates the 64-bit hash of @p input using xxHash64. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit xxHash64 value. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); + +/******* Streaming *******/ +#ifndef XXH_NO_STREAM +/*! + * @brief The opaque state struct for the XXH64 streaming API. + * + * @see XXH64_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ + +/*! + * @brief Allocates an @ref XXH64_state_t. + * + * @return An allocated pointer of @ref XXH64_state_t on success. + * @return `NULL` on failure. + * + * @note Must be freed with XXH64_freeState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void); + +/*! + * @brief Frees an @ref XXH64_state_t. + * + * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState(). + * + * @return @ref XXH_OK. + * + * @note @p statePtr must be allocated with XXH64_createState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); + +/*! + * @brief Copies one @ref XXH64_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +/*! + * @brief Resets an @ref XXH64_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note This function resets and seeds a state. Call it before @ref XXH64_update(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed); + +/*! + * @brief Consumes a block of @p input to an @ref XXH64_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Returns the calculated hash value from an @ref XXH64_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated 64-bit xxHash64 value from that state. + * + * @note + * Calling XXH64_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ +/******* Canonical representation *******/ + +/*! + * @brief Canonical (big endian) representation of @ref XXH64_hash_t. + */ +typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; + +/*! + * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t. + * + * @param dst The @ref XXH64_canonical_t pointer to be stored to. + * @param hash The @ref XXH64_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash); + +/*! + * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t. + * + * @param src The @ref XXH64_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src); + +#ifndef XXH_NO_XXH3 + +/*! + * @} + * ************************************************************************ + * @defgroup XXH3_family XXH3 family + * @ingroup public + * @{ + * + * XXH3 is a more recent hash algorithm featuring: + * - Improved speed for both small and large inputs + * - True 64-bit and 128-bit outputs + * - SIMD acceleration + * - Improved 32-bit viability + * + * Speed analysis methodology is explained here: + * + * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html + * + * Compared to XXH64, expect XXH3 to run approximately + * ~2x faster on large inputs and >3x faster on small ones, + * exact differences vary depending on platform. + * + * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, + * but does not require it. + * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3 + * at competitive speeds, even without vector support. Further details are + * explained in the implementation. + * + * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD + * implementations for many common platforms: + * - AVX512 + * - AVX2 + * - SSE2 + * - ARM NEON + * - WebAssembly SIMD128 + * - POWER8 VSX + * - s390x ZVector + * This can be controlled via the @ref XXH_VECTOR macro, but it automatically + * selects the best version according to predefined macros. For the x86 family, an + * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c. + * + * XXH3 implementation is portable: + * it has a generic C90 formulation that can be compiled on any platform, + * all implementations generate exactly the same hash value on all platforms. + * Starting from v0.8.0, it's also labelled "stable", meaning that + * any future version will also generate the same hash value. + * + * XXH3 offers 2 variants, _64bits and _128bits. + * + * When only 64 bits are needed, prefer invoking the _64bits variant, as it + * reduces the amount of mixing, resulting in faster speed on small inputs. + * It's also generally simpler to manipulate a scalar return type than a struct. + * + * The API supports one-shot hashing, streaming mode, and custom secrets. + */ + +/*! + * @ingroup tuning + * @brief Possible values for @ref XXH_VECTOR. + * + * Unless set explicitly, determined automatically. + */ +# define XXH_SCALAR 0 /*!< Portable scalar version */ +# define XXH_SSE2 1 /*!< SSE2 for Pentium 4, Opteron, all x86_64. */ +# define XXH_AVX2 2 /*!< AVX2 for Haswell and Bulldozer */ +# define XXH_AVX512 3 /*!< AVX512 for Skylake and Icelake */ +# define XXH_NEON 4 /*!< NEON for most ARMv7-A, all AArch64, and WASM SIMD128 */ +# define XXH_VSX 5 /*!< VSX and ZVector for POWER8/z13 (64-bit) */ +# define XXH_SVE 6 /*!< SVE for some ARMv8-A and ARMv9-A */ +# define XXH_LSX 7 /*!< LSX (128-bit SIMD) for LoongArch64 */ +# define XXH_LASX 8 /*!< LASX (256-bit SIMD) for LoongArch64 */ + + +/*-********************************************************************** +* XXH3 64-bit variant +************************************************************************/ + +/*! + * @brief Calculates 64-bit unseeded variant of XXH3 hash of @p input. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @note + * This is equivalent to @ref XXH3_64bits_withSeed() with a seed of `0`, however + * it may have slightly better performance due to constant propagation of the + * defaults. + * + * @see + * XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Calculates 64-bit seeded variant of XXH3 hash of @p input. + * + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @note + * seed == 0 produces the same results as @ref XXH3_64bits(). + * + * This variant generates a custom secret on the fly based on default secret + * altered using the @p seed value. + * + * While this operation is decently fast, note that it's not completely free. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); + +/*! + * The bare minimum size for a custom secret. + * + * @see + * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), + * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). + */ +#define XXH3_SECRET_SIZE_MIN 136 + +/*! + * @brief Calculates 64-bit variant of XXH3 with a custom "secret". + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @return The calculated 64-bit XXH3 hash value. + * + * @pre + * The memory between @p data and @p data + @p len must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p data may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN). + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. + * Avoid "trivial" or structured data such as repeated sequences or a text document. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing @ref XXH3_generateSecret() instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); + + +/******* Streaming *******/ +#ifndef XXH_NO_STREAM +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + */ + +/*! + * @brief The opaque state struct for the XXH3 streaming API. + * + * @see XXH3_state_s for details. + * @see @ref streaming_example "Streaming Example" + */ +typedef struct XXH3_state_s XXH3_state_t; +XXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); + +/*! + * @brief Copies one @ref XXH3_state_t to another. + * + * @param dst_state The state to copy to. + * @param src_state The state to copy from. + * @pre + * @p dst_state and @p src_state must not be `NULL` and must not overlap. + */ +XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state); + +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret with default parameters. + * - Call this function before @ref XXH3_64bits_update(). + * - Digest will be equivalent to `XXH3_64bits()`. + * + * @see @ref streaming_example "Streaming Example" + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret from `seed`. + * - Call this function before @ref XXH3_64bits_update(). + * - Digest will be equivalent to `XXH3_64bits_withSeed()`. + * + * @see @ref streaming_example "Streaming Example" + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); + +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * `secret` is referenced, it _must outlive_ the hash streaming session. + * + * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN, + * and the quality of produced hash values depends on secret's entropy + * (secret's content should look like a bunch of random bytes). + * When in doubt about the randomness of a candidate `secret`, + * consider employing `XXH3_generateSecret()` instead (see below). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); + +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * @pre + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note Call this to incrementally consume blocks of data. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 64-bit hash value from that state. + * + * @note + * Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ + +/* note : canonical representation of XXH3 is the same as XXH64 + * since they both produce XXH64_hash_t values */ + + +/*-********************************************************************** +* XXH3 128-bit variant +************************************************************************/ + +/*! + * @brief The return value from 128-bit hashes. + * + * Stored in little endian order, although the fields themselves are in native + * endianness. + */ +typedef struct { + XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ + XXH64_hash_t high64; /*!< `value >> 64` */ +} XXH128_hash_t; + +/*! + * @brief Calculates 128-bit unseeded variant of XXH3 of @p data. + * + * @param data The block of data to be hashed, at least @p length bytes in size. + * @param len The length of @p data, in bytes. + * + * @return The calculated 128-bit variant of XXH3 value. + * + * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead + * for shorter inputs. + * + * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of `0`, however + * it may have slightly better performance due to constant propagation of the + * defaults. + * + * @see XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len); +/*! @brief Calculates 128-bit seeded variant of XXH3 hash of @p data. + * + * @param data The block of data to be hashed, at least @p length bytes in size. + * @param len The length of @p data, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @return The calculated 128-bit variant of XXH3 value. + * + * @note + * seed == 0 produces the same results as @ref XXH3_64bits(). + * + * This variant generates a custom secret on the fly based on default secret + * altered using the @p seed value. + * + * While this operation is decently fast, note that it's not completely free. + * + * @see XXH3_128bits(), XXH3_128bits_withSecret(): other seeding variants + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); +/*! + * @brief Calculates 128-bit variant of XXH3 with a custom "secret". + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @return The calculated 128-bit variant of XXH3 value. + * + * It's possible to provide any blob of bytes as a "secret" to generate the hash. + * This makes it more difficult for an external actor to prepare an intentional collision. + * The main condition is that @p secretSize *must* be large enough (>= @ref XXH3_SECRET_SIZE_MIN). + * However, the quality of the secret impacts the dispersion of the hash algorithm. + * Therefore, the secret _must_ look like a bunch of random bytes. + * Avoid "trivial" or structured data such as repeated sequences or a text document. + * Whenever in doubt about the "randomness" of the blob of bytes, + * consider employing @ref XXH3_generateSecret() instead (see below). + * It will generate a proper high entropy secret derived from the blob of bytes. + * Another advantage of using XXH3_generateSecret() is that + * it guarantees that all bits within the initial blob of bytes + * will impact every bit of the output. + * This is not necessarily the case when using the blob of bytes directly + * because, when hashing _small_ inputs, only a portion of the secret is employed. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); + +/******* Streaming *******/ +#ifndef XXH_NO_STREAM +/* + * Streaming requires state maintenance. + * This operation costs memory and CPU. + * As a consequence, streaming is slower than one-shot hashing. + * For better performance, prefer one-shot functions whenever applicable. + * + * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). + * Use already declared XXH3_createState() and XXH3_freeState(). + * + * All reset and streaming functions have same meaning as their 64-bit counterpart. + */ + +/*! + * @brief Resets an @ref XXH3_state_t to begin a new hash. + * + * @param statePtr The state struct to reset. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret with default parameters. + * - Call it before @ref XXH3_128bits_update(). + * - Digest will be equivalent to `XXH3_128bits()`. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); + +/*! + * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * - This function resets `statePtr` and generate a secret from `seed`. + * - Call it before @ref XXH3_128bits_update(). + * - Digest will be equivalent to `XXH3_128bits_withSeed()`. + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr The state struct to reset. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * `secret` is referenced, it _must outlive_ the hash streaming session. + * Similar to one-shot API, `secretSize` must be >= @ref XXH3_SECRET_SIZE_MIN, + * and the quality of produced hash values depends on secret's entropy + * (secret's content should look like a bunch of random bytes). + * When in doubt about the randomness of a candidate `secret`, + * consider employing `XXH3_generateSecret()` instead (see below). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); + +/*! + * @brief Consumes a block of @p input to an @ref XXH3_state_t. + * + * Call this to incrementally consume blocks of data. + * + * @param statePtr The state struct to update. + * @param input The block of data to be hashed, at least @p length bytes in size. + * @param length The length of @p input, in bytes. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @note + * The memory between @p input and @p input + @p length must be valid, + * readable, contiguous memory. However, if @p length is `0`, @p input may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + */ +XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); + +/*! + * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t. + * + * @param statePtr The state struct to calculate the hash from. + * + * @pre + * @p statePtr must not be `NULL`. + * + * @return The calculated XXH3 128-bit hash value from that state. + * + * @note + * Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update, + * digest, and update again. + * + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); +#endif /* !XXH_NO_STREAM */ + +/* Following helper functions make it possible to compare XXH128_hast_t values. + * Since XXH128_hash_t is a structure, this capability is not offered by the language. + * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ + +/*! + * @brief Check equality of two XXH128_hash_t values + * + * @param h1 The 128-bit hash value. + * @param h2 Another 128-bit hash value. + * + * @return `1` if `h1` and `h2` are equal. + * @return `0` if they are not. + */ +XXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); + +/*! + * @brief Compares two @ref XXH128_hash_t + * + * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. + * + * @param h128_1 Left-hand side value + * @param h128_2 Right-hand side value + * + * @return >0 if @p h128_1 > @p h128_2 + * @return =0 if @p h128_1 == @p h128_2 + * @return <0 if @p h128_1 < @p h128_2 + */ +XXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2); + + +/******* Canonical representation *******/ +typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; + + +/*! + * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t. + * + * @param dst The @ref XXH128_canonical_t pointer to be stored to. + * @param hash The @ref XXH128_hash_t to be converted. + * + * @pre + * @p dst must not be `NULL`. + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash); + +/*! + * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t. + * + * @param src The @ref XXH128_canonical_t to convert. + * + * @pre + * @p src must not be `NULL`. + * + * @return The converted hash. + * @see @ref canonical_representation_example "Canonical Representation Example" + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src); + + +#endif /* !XXH_NO_XXH3 */ +#endif /* XXH_NO_LONG_LONG */ + +/*! + * @} + */ +#endif /* XXHASH_H_5627135585666179 */ + + + +#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) +#define XXHASH_H_STATIC_13879238742 +/* **************************************************************************** + * This section contains declarations which are not guaranteed to remain stable. + * They may change in future versions, becoming incompatible with a different + * version of the library. + * These declarations should only be used with static linking. + * Never use them in association with dynamic linking! + ***************************************************************************** */ + +/* + * These definitions are only present to allow static allocation + * of XXH states, on stack or in a struct, for example. + * Never **ever** access their members directly. + */ + +/*! + * @internal + * @brief Structure for XXH32 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH32_state_t. + * Do not access the members of this struct directly. + * @see XXH64_state_s, XXH3_state_s + */ +struct XXH32_state_s { + XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ + XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ + XXH32_hash_t acc[4]; /*!< Accumulator lanes */ + unsigned char buffer[16]; /*!< Internal buffer for partial reads. */ + XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */ + XXH32_hash_t reserved; /*!< Reserved field. Do not read nor write to it. */ +}; /* typedef'd to XXH32_state_t */ + + +#ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ + +/*! + * @internal + * @brief Structure for XXH64 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is + * an opaque type. This allows fields to safely be changed. + * + * Typedef'd to @ref XXH64_state_t. + * Do not access the members of this struct directly. + * @see XXH32_state_s, XXH3_state_s + */ +struct XXH64_state_s { + XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ + XXH64_hash_t acc[4]; /*!< Accumulator lanes */ + unsigned char buffer[32]; /*!< Internal buffer for partial reads.. */ + XXH32_hash_t bufferedSize; /*!< Amount of data in @ref buffer */ + XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ + XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it. */ +}; /* typedef'd to XXH64_state_t */ + +#ifndef XXH_NO_XXH3 + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ +# define XXH_ALIGN(n) _Alignas(n) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ +/* In C++ alignas() is a keyword */ +# define XXH_ALIGN(n) alignas(n) +#elif defined(__GNUC__) +# define XXH_ALIGN(n) __attribute__ ((aligned(n))) +#elif defined(_MSC_VER) +# define XXH_ALIGN(n) __declspec(align(n)) +#else +# define XXH_ALIGN(n) /* disabled */ +#endif + +/* Old GCC versions only accept the attribute after the type in structures. */ +#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ + && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ + && defined(__GNUC__) +# define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) +#else +# define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type +#endif + +/*! + * @internal + * @brief The size of the internal XXH3 buffer. + * + * This is the optimal update size for incremental hashing. + * + * @see XXH3_64b_update(), XXH3_128b_update(). + */ +#define XXH3_INTERNALBUFFER_SIZE 256 + +/*! + * @def XXH3_SECRET_DEFAULT_SIZE + * @brief Default Secret's size + * + * This is the size of internal XXH3_kSecret + * and is needed by XXH3_generateSecret_fromSeed(). + * + * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. + */ +#define XXH3_SECRET_DEFAULT_SIZE 192 + +/*! + * @internal + * @brief Structure for XXH3 streaming API. + * + * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, + * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. + * Otherwise it is an opaque type. + * Never use this definition in combination with dynamic library. + * This allows fields to safely be changed in the future. + * + * @note ** This structure has a strict alignment requirement of 64 bytes!! ** + * Do not allocate this with `malloc()` or `new`, + * it will not be sufficiently aligned. + * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. + * + * Typedef'd to @ref XXH3_state_t. + * Do never access the members of this struct directly. + * + * @see XXH3_INITSTATE() for stack initialization. + * @see XXH3_createState(), XXH3_freeState(). + * @see XXH32_state_s, XXH64_state_s + */ +struct XXH3_state_s { + XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); + /*!< The 8 accumulators. See @ref XXH32_state_s::acc and @ref XXH64_state_s::acc */ + XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); + /*!< Used to store a custom secret generated from a seed. */ + XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); + /*!< The internal buffer. @see XXH32_state_s::mem32 */ + XXH32_hash_t bufferedSize; + /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ + XXH32_hash_t useSeed; + /*!< Reserved field. Needed for padding on 64-bit. */ + size_t nbStripesSoFar; + /*!< Number or stripes processed. */ + XXH64_hash_t totalLen; + /*!< Total length hashed. 64-bit even on 32-bit targets. */ + size_t nbStripesPerBlock; + /*!< Number of stripes per block. */ + size_t secretLimit; + /*!< Size of @ref customSecret or @ref extSecret */ + XXH64_hash_t seed; + /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ + XXH64_hash_t reserved64; + /*!< Reserved field. */ + const unsigned char* extSecret; + /*!< Reference to an external secret for the _withSecret variants, NULL + * for other variants. */ + /* note: there may be some padding at the end due to alignment on 64 bytes */ +}; /* typedef'd to XXH3_state_t */ + +#undef XXH_ALIGN_MEMBER + +/*! + * @brief Initializes a stack-allocated `XXH3_state_s`. + * + * When the @ref XXH3_state_t structure is merely emplaced on stack, + * it should be initialized with XXH3_INITSTATE() or a memset() + * in case its first reset uses XXH3_NNbits_reset_withSeed(). + * This init can be omitted if the first reset uses default or _withSecret mode. + * This operation isn't necessary when the state is created with XXH3_createState(). + * Note that this doesn't prepare the state for a streaming operation, + * it's still necessary to use XXH3_NNbits_reset*() afterwards. + */ +#define XXH3_INITSTATE(XXH3_state_ptr) \ + do { \ + XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \ + tmp_xxh3_state_ptr->seed = 0; \ + tmp_xxh3_state_ptr->extSecret = NULL; \ + } while(0) + + +/*! + * @brief Calculates the 128-bit hash of @p data using XXH3. + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param seed The 64-bit seed to alter the hash's output predictably. + * + * @pre + * The memory between @p data and @p data + @p len must be valid, + * readable, contiguous memory. However, if @p len is `0`, @p data may be + * `NULL`. In C++, this also must be *TriviallyCopyable*. + * + * @return The calculated 128-bit XXH3 value. + * + * @see @ref single_shot_example "Single Shot Example" for an example. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); + + +/* === Experimental API === */ +/* Symbols defined below must be considered tied to a specific library version. */ + +/*! + * @brief Derive a high-entropy secret from any user-defined content, named customSeed. + * + * @param secretBuffer A writable buffer for derived high-entropy secret data. + * @param secretSize Size of secretBuffer, in bytes. Must be >= XXH3_SECRET_SIZE_MIN. + * @param customSeed A user-defined content. + * @param customSeedSize Size of customSeed, in bytes. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * The generated secret can be used in combination with `*_withSecret()` functions. + * The `_withSecret()` variants are useful to provide a higher level of protection + * than 64-bit seed, as it becomes much more difficult for an external actor to + * guess how to impact the calculation logic. + * + * The function accepts as input a custom seed of any length and any content, + * and derives from it a high-entropy secret of length @p secretSize into an + * already allocated buffer @p secretBuffer. + * + * The generated secret can then be used with any `*_withSecret()` variant. + * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(), + * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret() + * are part of this list. They all accept a `secret` parameter + * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN) + * _and_ feature very high entropy (consist of random-looking bytes). + * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can + * be employed to ensure proper quality. + * + * @p customSeed can be anything. It can have any size, even small ones, + * and its content can be anything, even "poor entropy" sources such as a bunch + * of zeroes. The resulting `secret` will nonetheless provide all required qualities. + * + * @pre + * - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN + * - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior. + * + * Example code: + * @code{.c} + * #include + * #include + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Hashes argv[2] using the entropy from argv[1]. + * int main(int argc, char* argv[]) + * { + * char secret[XXH3_SECRET_SIZE_MIN]; + * if (argv != 3) { return 1; } + * XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1])); + * XXH64_hash_t h = XXH3_64bits_withSecret( + * argv[2], strlen(argv[2]), + * secret, sizeof(secret) + * ); + * printf("%016llx\n", (unsigned long long) h); + * } + * @endcode + */ +XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize); + +/*! + * @brief Generate the same secret as the _withSeed() variants. + * + * @param secretBuffer A writable buffer of @ref XXH3_SECRET_DEFAULT_SIZE bytes + * @param seed The 64-bit seed to alter the hash result predictably. + * + * The generated secret can be used in combination with + *`*_withSecret()` and `_withSecretandSeed()` variants. + * + * Example C++ `std::string` hash class: + * @code{.cpp} + * #include + * #define XXH_STATIC_LINKING_ONLY // expose unstable API + * #include "xxhash.h" + * // Slow, seeds each time + * class HashSlow { + * XXH64_hash_t seed; + * public: + * HashSlow(XXH64_hash_t s) : seed{s} {} + * size_t operator()(const std::string& x) const { + * return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)}; + * } + * }; + * // Fast, caches the seeded secret for future uses. + * class HashFast { + * unsigned char secret[XXH3_SECRET_DEFAULT_SIZE]; + * public: + * HashFast(XXH64_hash_t s) { + * XXH3_generateSecret_fromSeed(secret, seed); + * } + * size_t operator()(const std::string& x) const { + * return size_t{ + * XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret)) + * }; + * } + * }; + * @endcode + */ +XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed); + +/*! + * @brief Maximum size of "short" key in bytes. + */ +#define XXH3_MIDSIZE_MAX 240 + +/*! + * @brief Calculates 64/128-bit seeded variant of XXH3 hash of @p data. + * + * @param data The block of data to be hashed, at least @p len bytes in size. + * @param len The length of @p data, in bytes. + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed The 64-bit seed to alter the hash result predictably. + * + * These variants generate hash values using either: + * - @p seed for "short" keys (< @ref XXH3_MIDSIZE_MAX = 240 bytes) + * - @p secret for "large" keys (>= @ref XXH3_MIDSIZE_MAX). + * + * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. + * `_withSeed()` has to generate the secret on the fly for "large" keys. + * It's fast, but can be perceptible for "not so large" keys (< 1 KB). + * `_withSecret()` has to generate the masks on the fly for "small" keys, + * which requires more instructions than _withSeed() variants. + * Therefore, _withSecretandSeed variant combines the best of both worlds. + * + * When @p secret has been generated by XXH3_generateSecret_fromSeed(), + * this variant produces *exactly* the same results as `_withSeed()` variant, + * hence offering only a pure speed benefit on "large" input, + * by skipping the need to regenerate the secret for every large input. + * + * Another usage scenario is to hash the secret to a 64-bit hash value, + * for example with XXH3_64bits(), which then becomes the seed, + * and then employ both the seed and the secret in _withSecretandSeed(). + * On top of speed, an added benefit is that each bit in the secret + * has a 50% chance to swap each bit in the output, via its impact to the seed. + * + * This is not guaranteed when using the secret directly in "small data" scenarios, + * because only portions of the secret are employed for small data. + */ +XXH_PUBLIC_API XXH_PUREF XXH64_hash_t +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed); + +/*! + * @brief Calculates 128-bit seeded variant of XXH3 hash of @p data. + * + * @param input The memory segment to be hashed, at least @p len bytes in size. + * @param length The length of @p data, in bytes. + * @param secret The secret used to alter hash result predictably. + * @param secretSize The length of @p secret, in bytes (must be >= XXH3_SECRET_SIZE_MIN) + * @param seed64 The 64-bit seed to alter the hash result predictably. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(): contract is the same. + */ +XXH_PUBLIC_API XXH_PUREF XXH128_hash_t +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); + +#ifndef XXH_NO_STREAM +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed64 The 64-bit seed to alter the hash result predictably. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(). Contract is identical. + */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); + +/*! + * @brief Resets an @ref XXH3_state_t with secret data to begin a new hash. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * @param secret The secret data. + * @param secretSize The length of @p secret, in bytes. + * @param seed64 The 64-bit seed to alter the hash result predictably. + * + * @return @ref XXH_OK on success. + * @return @ref XXH_ERROR on failure. + * + * @see XXH3_64bits_withSecretandSeed(). Contract is identical. + * + * Note: there was a bug in an earlier version of this function (<= v0.8.2) + * that would make it generate an incorrect hash value + * when @p seed == 0 and @p length < XXH3_MIDSIZE_MAX + * and @p secret is different from XXH3_generateSecret_fromSeed(). + * As stated in the contract, the correct hash result must be + * the same as XXH3_128bits_withSeed() when @p length <= XXH3_MIDSIZE_MAX. + * Results generated by this older version are wrong, hence not comparable. + */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, + XXH_NOESCAPE const void* secret, size_t secretSize, + XXH64_hash_t seed64); + +#endif /* !XXH_NO_STREAM */ + +#endif /* !XXH_NO_XXH3 */ +#endif /* XXH_NO_LONG_LONG */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# define XXH_IMPLEMENTATION +#endif + +#endif /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */ + + +/* ======================================================================== */ +/* ======================================================================== */ +/* ======================================================================== */ + + +/*-********************************************************************** + * xxHash implementation + *-********************************************************************** + * xxHash's implementation used to be hosted inside xxhash.c. + * + * However, inlining requires implementation to be visible to the compiler, + * hence be included alongside the header. + * Previously, implementation was hosted inside xxhash.c, + * which was then #included when inlining was activated. + * This construction created issues with a few build and install systems, + * as it required xxhash.c to be stored in /include directory. + * + * xxHash implementation is now directly integrated within xxhash.h. + * As a consequence, xxhash.c is no longer needed in /include. + * + * xxhash.c is still available and is still useful. + * In a "normal" setup, when xxhash is not inlined, + * xxhash.h only exposes the prototypes and public symbols, + * while xxhash.c can be built into an object file xxhash.o + * which can then be linked into the final binary. + ************************************************************************/ + +#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ + || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387) +# define XXH_IMPLEM_13a8737387 + +/* ************************************* +* Tuning parameters +***************************************/ + +/*! + * @defgroup tuning Tuning parameters + * @{ + * + * Various macros to control xxHash's behavior. + */ +#ifdef XXH_DOXYGEN +/*! + * @brief Define this to disable 64-bit code. + * + * Useful if only using the @ref XXH32_family and you have a strict C90 compiler. + */ +# define XXH_NO_LONG_LONG +# undef XXH_NO_LONG_LONG /* don't actually */ +/*! + * @brief Controls how unaligned memory is accessed. + * + * By default, access to unaligned memory is controlled by `memcpy()`, which is + * safe and portable. + * + * Unfortunately, on some target/compiler combinations, the generated assembly + * is sub-optimal. + * + * The below switch allow selection of a different access method + * in the search for improved performance. + * + * @par Possible options: + * + * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` + * @par + * Use `memcpy()`. Safe and portable. Note that most modern compilers will + * eliminate the function call and treat it as an unaligned access. + * + * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))` + * @par + * Depends on compiler extensions and is therefore not portable. + * This method is safe _if_ your compiler supports it, + * and *generally* as fast or faster than `memcpy`. + * + * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast + * @par + * Casts directly and dereferences. This method doesn't depend on the + * compiler, but it violates the C standard as it directly dereferences an + * unaligned pointer. It can generate buggy code on targets which do not + * support unaligned memory accesses, but in some circumstances, it's the + * only known way to get the most performance. + * + * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift + * @par + * Also portable. This can generate the best code on old compilers which don't + * inline small `memcpy()` calls, and it might also be faster on big-endian + * systems which lack a native byteswap instruction. However, some compilers + * will emit literal byteshifts even if the target supports unaligned access. + * + * + * @warning + * Methods 1 and 2 rely on implementation-defined behavior. Use these with + * care, as what works on one compiler/platform/optimization level may cause + * another to read garbage data or even crash. + * + * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. + * + * Prefer these methods in priority order (0 > 3 > 1 > 2) + */ +# define XXH_FORCE_MEMORY_ACCESS 0 + +/*! + * @def XXH_SIZE_OPT + * @brief Controls how much xxHash optimizes for size. + * + * xxHash, when compiled, tends to result in a rather large binary size. This + * is mostly due to heavy usage to forced inlining and constant folding of the + * @ref XXH3_family to increase performance. + * + * However, some developers prefer size over speed. This option can + * significantly reduce the size of the generated code. When using the `-Os` + * or `-Oz` options on GCC or Clang, this is defined to 1 by default, + * otherwise it is defined to 0. + * + * Most of these size optimizations can be controlled manually. + * + * This is a number from 0-2. + * - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed + * comes first. + * - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more + * conservative and disables hacks that increase code size. It implies the + * options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0, + * and @ref XXH3_NEON_LANES == 8 if they are not already defined. + * - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible. + * Performance may cry. For example, the single shot functions just use the + * streaming API. + */ +# define XXH_SIZE_OPT 0 + +/*! + * @def XXH_FORCE_ALIGN_CHECK + * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() + * and XXH64() only). + * + * This is an important performance trick for architectures without decent + * unaligned memory access performance. + * + * It checks for input alignment, and when conditions are met, uses a "fast + * path" employing direct 32-bit/64-bit reads, resulting in _dramatically + * faster_ read speed. + * + * The check costs one initial branch per hash, which is generally negligible, + * but not zero. + * + * Moreover, it's not useful to generate an additional code path if memory + * access uses the same instruction for both aligned and unaligned + * addresses (e.g. x86 and aarch64). + * + * In these cases, the alignment check can be removed by setting this macro to 0. + * Then the code will always use unaligned memory access. + * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips + * which are platforms known to offer good unaligned memory accesses performance. + * + * It is also disabled by default when @ref XXH_SIZE_OPT >= 1. + * + * This option does not affect XXH3 (only XXH32 and XXH64). + */ +# define XXH_FORCE_ALIGN_CHECK 0 + +/*! + * @def XXH_NO_INLINE_HINTS + * @brief When non-zero, sets all functions to `static`. + * + * By default, xxHash tries to force the compiler to inline almost all internal + * functions. + * + * This can usually improve performance due to reduced jumping and improved + * constant folding, but significantly increases the size of the binary which + * might not be favorable. + * + * Additionally, sometimes the forced inlining can be detrimental to performance, + * depending on the architecture. + * + * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the + * compiler full control on whether to inline or not. + * + * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if + * @ref XXH_SIZE_OPT >= 1, this will automatically be defined. + */ +# define XXH_NO_INLINE_HINTS 0 + +/*! + * @def XXH3_INLINE_SECRET + * @brief Determines whether to inline the XXH3 withSecret code. + * + * When the secret size is known, the compiler can improve the performance + * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret(). + * + * However, if the secret size is not known, it doesn't have any benefit. This + * happens when xxHash is compiled into a global symbol. Therefore, if + * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0. + * + * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers + * that are *sometimes* force inline on -Og, and it is impossible to automatically + * detect this optimization level. + */ +# define XXH3_INLINE_SECRET 0 + +/*! + * @def XXH32_ENDJMP + * @brief Whether to use a jump for `XXH32_finalize`. + * + * For performance, `XXH32_finalize` uses multiple branches in the finalizer. + * This is generally preferable for performance, + * but depending on exact architecture, a jmp may be preferable. + * + * This setting is only possibly making a difference for very small inputs. + */ +# define XXH32_ENDJMP 0 + +/*! + * @internal + * @brief Redefines old internal names. + * + * For compatibility with code that uses xxHash's internals before the names + * were changed to improve namespacing. There is no other reason to use this. + */ +# define XXH_OLD_NAMES +# undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ + +/*! + * @def XXH_NO_STREAM + * @brief Disables the streaming API. + * + * When xxHash is not inlined and the streaming functions are not used, disabling + * the streaming functions can improve code size significantly, especially with + * the @ref XXH3_family which tends to make constant folded copies of itself. + */ +# define XXH_NO_STREAM +# undef XXH_NO_STREAM /* don't actually */ +#endif /* XXH_DOXYGEN */ +/*! + * @} + */ + +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ + /* prefer __packed__ structures (method 1) for GCC + * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy + * which for some reason does unaligned loads. */ +# if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED)) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +#ifndef XXH_SIZE_OPT + /* default to 1 for -Os or -Oz */ +# if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__) +# define XXH_SIZE_OPT 1 +# else +# define XXH_SIZE_OPT 0 +# endif +#endif + +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ + /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */ +# if XXH_SIZE_OPT >= 1 || \ + defined(__i386) || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \ + || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM) /* visual */ +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + +#ifndef XXH_NO_INLINE_HINTS +# if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__) /* -O0, -fno-inline */ +# define XXH_NO_INLINE_HINTS 1 +# else +# define XXH_NO_INLINE_HINTS 0 +# endif +#endif + +#ifndef XXH3_INLINE_SECRET +# if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \ + || !defined(XXH_INLINE_ALL) +# define XXH3_INLINE_SECRET 0 +# else +# define XXH3_INLINE_SECRET 1 +# endif +#endif + +#ifndef XXH32_ENDJMP +/* generally preferable for performance */ +# define XXH32_ENDJMP 0 +#endif + +/*! + * @defgroup impl Implementation + * @{ + */ + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +#if defined(XXH_NO_STREAM) +/* nothing */ +#elif defined(XXH_NO_STDLIB) + +/* When requesting to disable any mention of stdlib, + * the library loses the ability to invoked malloc / free. + * In practice, it means that functions like `XXH*_createState()` + * will always fail, and return NULL. + * This flag is useful in situations where + * xxhash.h is integrated into some kernel, embedded or limited environment + * without access to dynamic allocation. + */ + +static XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; } +static void XXH_free(void* p) { (void)p; } + +#else + +/* + * Modify the local functions below should you wish to use + * different memory routines for malloc() and free() + */ +#include + +/*! + * @internal + * @brief Modify this function to use a different routine than malloc(). + */ +static XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); } + +/*! + * @internal + * @brief Modify this function to use a different routine than free(). + */ +static void XXH_free(void* p) { free(p); } + +#endif /* XXH_NO_STDLIB */ + +#ifndef XXH_memcpy +/*! + * @internal + * @brief XXH_memcpy() macro can be redirected at compile time + */ +# include +# define XXH_memcpy memcpy +#endif + +#ifndef XXH_memset +/*! + * @internal + * @brief XXH_memset() macro can be redirected at compile time + */ +# include +# define XXH_memset memset +#endif + +#ifndef XXH_memcmp +/*! + * @internal + * @brief XXH_memcmp() macro can be redirected at compile time + * Note: only needed by XXH128. + */ +# include +# define XXH_memcmp memcmp +#endif + + + +#include /* ULLONG_MAX */ + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio warning fix */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + +#if XXH_NO_INLINE_HINTS /* disable inlining hints */ +# if defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __attribute__((__unused__)) +# else +# define XXH_FORCE_INLINE static +# endif +# define XXH_NO_INLINE static +/* enable inlining hints */ +#elif defined(__GNUC__) || defined(__clang__) +# define XXH_FORCE_INLINE static __inline__ __attribute__((__always_inline__, __unused__)) +# define XXH_NO_INLINE static __attribute__((__noinline__)) +#elif defined(_MSC_VER) /* Visual Studio */ +# define XXH_FORCE_INLINE static __forceinline +# define XXH_NO_INLINE static __declspec(noinline) +#elif defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ +# define XXH_FORCE_INLINE static inline +# define XXH_NO_INLINE static +#else +# define XXH_FORCE_INLINE static +# define XXH_NO_INLINE static +#endif + +#if defined(XXH_INLINE_ALL) +# define XXH_STATIC XXH_FORCE_INLINE +#else +# define XXH_STATIC static +#endif + +#if XXH3_INLINE_SECRET +# define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE +#else +# define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE +#endif + +#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ +# define XXH_RESTRICT /* disable */ +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ +# define XXH_RESTRICT restrict +#elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \ + || (defined (__clang__)) \ + || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \ + || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300)) +/* + * There are a LOT more compilers that recognize __restrict but this + * covers the major ones. + */ +# define XXH_RESTRICT __restrict +#else +# define XXH_RESTRICT /* disable */ +#endif + +/* ************************************* +* Debug +***************************************/ +/*! + * @ingroup tuning + * @def XXH_DEBUGLEVEL + * @brief Sets the debugging level. + * + * XXH_DEBUGLEVEL is expected to be defined externally, typically via the + * compiler's command line options. The value must be a number. + */ +#ifndef XXH_DEBUGLEVEL +# ifdef DEBUGLEVEL /* backwards compat */ +# define XXH_DEBUGLEVEL DEBUGLEVEL +# else +# define XXH_DEBUGLEVEL 0 +# endif +#endif + +#if (XXH_DEBUGLEVEL>=1) +# include /* note: can still be disabled with NDEBUG */ +# define XXH_ASSERT(c) assert(c) +#else +# if defined(__INTEL_COMPILER) +# define XXH_ASSERT(c) XXH_ASSUME((unsigned char) (c)) +# else +# define XXH_ASSERT(c) XXH_ASSUME(c) +# endif +#endif + +/* note: use after variable declarations */ +#ifndef XXH_STATIC_ASSERT +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0) +# elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) +# else +# define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) +# endif +# define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) +#endif + +/*! + * @internal + * @def XXH_COMPILER_GUARD(var) + * @brief Used to prevent unwanted optimizations for @p var. + * + * It uses an empty GCC inline assembly statement with a register constraint + * which forces @p var into a general purpose register (eg eax, ebx, ecx + * on x86) and marks it as modified. + * + * This is used in a few places to avoid unwanted autovectorization (e.g. + * XXH32_round()). All vectorization we want is explicit via intrinsics, + * and _usually_ isn't wanted elsewhere. + * + * We also use it to prevent unwanted constant folding for AArch64 in + * XXH3_initCustomSecret_scalar(). + */ +#if defined(__GNUC__) || defined(__clang__) +# define XXH_COMPILER_GUARD(var) __asm__("" : "+r" (var)) +#else +# define XXH_COMPILER_GUARD(var) ((void)0) +#endif + +/* Specifically for NEON vectors which use the "w" constraint, on + * Clang. */ +#if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__) +# define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__("" : "+w" (var)) +#else +# define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0) +#endif + +/* ************************************* +* Basic Types +***************************************/ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# ifdef _AIX +# include +# else +# include +# endif + typedef uint8_t xxh_u8; +#else + typedef unsigned char xxh_u8; +#endif +typedef XXH32_hash_t xxh_u32; + +#ifdef XXH_OLD_NAMES +# warning "XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly" +# define BYTE xxh_u8 +# define U8 xxh_u8 +# define U32 xxh_u32 +#endif + +/* *** Memory access *** */ + +/*! + * @internal + * @fn xxh_u32 XXH_read32(const void* ptr) + * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit native endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32(const void* ptr) + * @brief Reads an unaligned 32-bit little endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readBE32(const void* ptr) + * @brief Reads an unaligned 32-bit big endian integer from @p ptr. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * + * @param ptr The pointer to read from. + * @return The 32-bit big endian integer from the bytes at @p ptr. + */ + +/*! + * @internal + * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) + * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. + * + * Affected by @ref XXH_FORCE_MEMORY_ACCESS. + * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is + * always @ref XXH_alignment::XXH_unaligned. + * + * @param ptr The pointer to read from. + * @param align Whether @p ptr is aligned. + * @pre + * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte + * aligned. + * @return The 32-bit little endian integer from the bytes at @p ptr. + */ + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +/* + * Manual byteshift. Best for old compilers which don't inline memcpy. + * We actually directly use XXH_readLE32 and XXH_readBE32. + */ +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* + * Force direct memory access. Only works on CPU which support unaligned memory + * access in hardware. + */ +static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. + */ +#ifdef XXH_OLD_NAMES +typedef union { xxh_u32 u32; } __attribute__((__packed__)) unalign; +#endif +static xxh_u32 XXH_read32(const void* ptr) +{ + typedef __attribute__((__aligned__(1))) xxh_u32 xxh_unalign32; + return *((const xxh_unalign32*)ptr); +} + +#else + +/* + * Portable and safe solution. Generally efficient. + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + */ +static xxh_u32 XXH_read32(const void* memPtr) +{ + xxh_u32 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* *** Endianness *** */ + +/*! + * @ingroup tuning + * @def XXH_CPU_LITTLE_ENDIAN + * @brief Whether the target is little endian. + * + * Defined to 1 if the target is little endian, or 0 if it is big endian. + * It can be defined externally, for example on the compiler command line. + * + * If it is not defined, + * a runtime check (which is usually constant folded) is used instead. + * + * @note + * This is not necessarily defined to an integer constant. + * + * @see XXH_isLittleEndian() for the runtime check. + */ +#ifndef XXH_CPU_LITTLE_ENDIAN +/* + * Try to detect endianness automatically, to avoid the nonstandard behavior + * in `XXH_isLittleEndian()` + */ +# if defined(_WIN32) /* Windows is always little endian */ \ + || defined(__LITTLE_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 1 +# elif defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_CPU_LITTLE_ENDIAN 0 +# else +/*! + * @internal + * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. + * + * Most compilers will constant fold this. + */ +static int XXH_isLittleEndian(void) +{ + /* + * Portable and well-defined behavior. + * Don't use static: it is detrimental to performance. + */ + const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 }; + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +# endif +#endif + + + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#ifdef __has_builtin +# define XXH_HAS_BUILTIN(x) __has_builtin(x) +#else +# define XXH_HAS_BUILTIN(x) 0 +#endif + + + +/* + * C23 and future versions have standard "unreachable()". + * Once it has been implemented reliably we can add it as an + * additional case: + * + * ``` + * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) + * # include + * # ifdef unreachable + * # define XXH_UNREACHABLE() unreachable() + * # endif + * #endif + * ``` + * + * Note C++23 also has std::unreachable() which can be detected + * as follows: + * ``` + * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) + * # include + * # define XXH_UNREACHABLE() std::unreachable() + * #endif + * ``` + * NB: `__cpp_lib_unreachable` is defined in the `` header. + * We don't use that as including `` in `extern "C"` blocks + * doesn't work on GCC12 + */ + +#if XXH_HAS_BUILTIN(__builtin_unreachable) +# define XXH_UNREACHABLE() __builtin_unreachable() + +#elif defined(_MSC_VER) +# define XXH_UNREACHABLE() __assume(0) + +#else +# define XXH_UNREACHABLE() +#endif + +#if XXH_HAS_BUILTIN(__builtin_assume) +# define XXH_ASSUME(c) __builtin_assume(c) +#else +# define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); } +#endif + +/*! + * @internal + * @def XXH_rotl32(x,r) + * @brief 32-bit rotate left. + * + * @param x The 32-bit integer to be rotated. + * @param r The number of bits to rotate. + * @pre + * @p r > 0 && @p r < 32 + * @note + * @p x and @p r may be evaluated multiple times. + * @return The rotated result. + */ +#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ + && XXH_HAS_BUILTIN(__builtin_rotateleft64) +# define XXH_rotl32 __builtin_rotateleft32 +# define XXH_rotl64 __builtin_rotateleft64 +#elif XXH_HAS_BUILTIN(__builtin_stdc_rotate_left) +# define XXH_rotl32 __builtin_stdc_rotate_left +# define XXH_rotl64 __builtin_stdc_rotate_left +/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ +#elif defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +#endif + +/*! + * @internal + * @fn xxh_u32 XXH_swap32(xxh_u32 x) + * @brief A 32-bit byteswap. + * + * @param x The 32-bit integer to byteswap. + * @return @p x, byteswapped. + */ +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static xxh_u32 XXH_swap32 (xxh_u32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* *************************** +* Memory reads +*****************************/ + +/*! + * @internal + * @brief Enum to indicate whether a pointer is aligned. + */ +typedef enum { + XXH_aligned, /*!< Aligned */ + XXH_unaligned /*!< Possibly unaligned */ +} XXH_alignment; + +/* + * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. + * + * This is ideal for older compilers which don't inline memcpy. + */ +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) + +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[0] + | ((xxh_u32)bytePtr[1] << 8) + | ((xxh_u32)bytePtr[2] << 16) + | ((xxh_u32)bytePtr[3] << 24); +} + +XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[3] + | ((xxh_u32)bytePtr[2] << 8) + | ((xxh_u32)bytePtr[1] << 16) + | ((xxh_u32)bytePtr[0] << 24); +} + +#else +XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); +} + +static xxh_u32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} +#endif + +XXH_FORCE_INLINE xxh_u32 +XXH_readLE32_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) { + return XXH_readLE32(ptr); + } else { + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); + } +} + + +/* ************************************* +* Misc +***************************************/ +/*! @ingroup public */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +/*! + * @} + * @defgroup XXH32_impl XXH32 implementation + * @ingroup impl + * + * Details on the XXH32 implementation. + * @{ + */ + /* #define instead of static const, to be used as initializers */ +#define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ +#define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ +#define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ +#define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ +#define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ + +#ifdef XXH_OLD_NAMES +# define PRIME32_1 XXH_PRIME32_1 +# define PRIME32_2 XXH_PRIME32_2 +# define PRIME32_3 XXH_PRIME32_3 +# define PRIME32_4 XXH_PRIME32_4 +# define PRIME32_5 XXH_PRIME32_5 +#endif + +/*! + * @internal + * @brief Normal stripe processing routine. + * + * This shuffles the bits so that any bit from @p input impacts several bits in + * @p acc. + * + * @param acc The accumulator lane. + * @param input The stripe of input to mix. + * @return The mixed accumulator lane. + */ +static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) +{ + acc += input * XXH_PRIME32_2; + acc = XXH_rotl32(acc, 13); + acc *= XXH_PRIME32_1; +#if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* + * UGLY HACK: + * A compiler fence is used to prevent GCC and Clang from + * autovectorizing the XXH32 loop (pragmas and attributes don't work for some + * reason) without globally disabling SSE4.1. + * + * The reason we want to avoid vectorization is because despite working on + * 4 integers at a time, there are multiple factors slowing XXH32 down on + * SSE4: + * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on + * newer chips!) making it slightly slower to multiply four integers at + * once compared to four integers independently. Even when pmulld was + * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE + * just to multiply unless doing a long operation. + * + * - Four instructions are required to rotate, + * movqda tmp, v // not required with VEX encoding + * pslld tmp, 13 // tmp <<= 13 + * psrld v, 19 // x >>= 19 + * por v, tmp // x |= tmp + * compared to one for scalar: + * roll v, 13 // reliably fast across the board + * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason + * + * - Instruction level parallelism is actually more beneficial here because + * the SIMD actually serializes this operation: While v1 is rotating, v2 + * can load data, while v3 can multiply. SSE forces them to operate + * together. + * + * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing + * the loop. NEON is only faster on the A53, and with the newer cores, it is less + * than half the speed. + * + * Additionally, this is used on WASM SIMD128 because it JITs to the same + * SIMD instructions and has the same issue. + */ + XXH_COMPILER_GUARD(acc); +#endif + return acc; +} + +/*! + * @internal + * @brief Mixes all bits to finalize the hash. + * + * The final mix ensures that all input bits have a chance to impact any bit in + * the output digest, resulting in an unbiased distribution. + * + * @param hash The hash to avalanche. + * @return The avalanched hash. + */ +static xxh_u32 XXH32_avalanche(xxh_u32 hash) +{ + hash ^= hash >> 15; + hash *= XXH_PRIME32_2; + hash ^= hash >> 13; + hash *= XXH_PRIME32_3; + hash ^= hash >> 16; + return hash; +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, align) + +/*! + * @internal + * @brief Sets up the initial accumulator state for XXH32(). + */ +XXH_FORCE_INLINE void +XXH32_initAccs(xxh_u32 *acc, xxh_u32 seed) +{ + XXH_ASSERT(acc != NULL); + acc[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; + acc[1] = seed + XXH_PRIME32_2; + acc[2] = seed + 0; + acc[3] = seed - XXH_PRIME32_1; +} + +/*! + * @internal + * @brief Consumes a block of data for XXH32(). + * + * @return the end input pointer. + */ +XXH_FORCE_INLINE const xxh_u8 * +XXH32_consumeLong( + xxh_u32 *XXH_RESTRICT acc, + xxh_u8 const *XXH_RESTRICT input, + size_t len, + XXH_alignment align +) +{ + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 15; + XXH_ASSERT(acc != NULL); + XXH_ASSERT(input != NULL); + XXH_ASSERT(len >= 16); + do { + acc[0] = XXH32_round(acc[0], XXH_get32bits(input)); input += 4; + acc[1] = XXH32_round(acc[1], XXH_get32bits(input)); input += 4; + acc[2] = XXH32_round(acc[2], XXH_get32bits(input)); input += 4; + acc[3] = XXH32_round(acc[3], XXH_get32bits(input)); input += 4; + } while (input < limit); + + return input; +} + +/*! + * @internal + * @brief Merges the accumulator lanes together for XXH32() + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u32 +XXH32_mergeAccs(const xxh_u32 *acc) +{ + XXH_ASSERT(acc != NULL); + return XXH_rotl32(acc[0], 1) + XXH_rotl32(acc[1], 7) + + XXH_rotl32(acc[2], 12) + XXH_rotl32(acc[3], 18); +} + +/*! + * @internal + * @brief Processes the last 0-15 bytes of @p ptr. + * + * There may be up to 15 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 16. + * @param align Whether @p ptr is aligned. + * @return The finalized hash. + * @see XXH64_finalize(). + */ +static XXH_PUREF xxh_u32 +XXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ +#define XXH_PROCESS1 do { \ + hash += (*ptr++) * XXH_PRIME32_5; \ + hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1; \ +} while (0) + +#define XXH_PROCESS4 do { \ + hash += XXH_get32bits(ptr) * XXH_PRIME32_3; \ + ptr += 4; \ + hash = XXH_rotl32(hash, 17) * XXH_PRIME32_4; \ +} while (0) + + if (ptr==NULL) XXH_ASSERT(len == 0); + + /* Compact rerolled version; generally faster */ + if (!XXH32_ENDJMP) { + len &= 15; + while (len >= 4) { + XXH_PROCESS4; + len -= 4; + } + while (len > 0) { + XXH_PROCESS1; + --len; + } + return XXH32_avalanche(hash); + } else { + switch(len&15) /* or switch(bEnd - p) */ { + case 12: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 8: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 4: XXH_PROCESS4; + return XXH32_avalanche(hash); + + case 13: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 9: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 5: XXH_PROCESS4; + XXH_PROCESS1; + return XXH32_avalanche(hash); + + case 14: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 10: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 6: XXH_PROCESS4; + XXH_PROCESS1; + XXH_PROCESS1; + return XXH32_avalanche(hash); + + case 15: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 11: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 7: XXH_PROCESS4; + XXH_FALLTHROUGH; /* fallthrough */ + case 3: XXH_PROCESS1; + XXH_FALLTHROUGH; /* fallthrough */ + case 2: XXH_PROCESS1; + XXH_FALLTHROUGH; /* fallthrough */ + case 1: XXH_PROCESS1; + XXH_FALLTHROUGH; /* fallthrough */ + case 0: return XXH32_avalanche(hash); + } + XXH_ASSERT(0); + return hash; /* reaching this point is deemed impossible */ + } +} + +#ifdef XXH_OLD_NAMES +# define PROCESS1 XXH_PROCESS1 +# define PROCESS4 XXH_PROCESS4 +#else +# undef XXH_PROCESS1 +# undef XXH_PROCESS4 +#endif + +/*! + * @internal + * @brief The implementation for @ref XXH32(). + * + * @param input , len , seed Directly passed from @ref XXH32(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u32 +XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) +{ + xxh_u32 h32; + + if (input==NULL) XXH_ASSERT(len == 0); + + if (len>=16) { + xxh_u32 acc[4]; + XXH32_initAccs(acc, seed); + + input = XXH32_consumeLong(acc, input, len, align); + + h32 = XXH32_mergeAccs(acc); + } else { + h32 = seed + XXH_PRIME32_5; + } + + h32 += (xxh_u32)len; + + return XXH32_finalize(h32, input, len&15, align); +} + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) +{ +#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, (const xxh_u8*)input, len); + return XXH32_digest(&state); +#else + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); +#endif +} + + + +/******* Hash streaming *******/ +#ifndef XXH_NO_STREAM +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + XXH_memcpy(dstState, srcState, sizeof(*dstState)); +} + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) +{ + XXH_ASSERT(statePtr != NULL); + XXH_memset(statePtr, 0, sizeof(*statePtr)); + XXH32_initAccs(statePtr->acc, seed); + return XXH_OK; +} + + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH_errorcode +XXH32_update(XXH32_state_t* state, const void* input, size_t len) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + state->total_len_32 += (XXH32_hash_t)len; + state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); + + XXH_ASSERT(state->bufferedSize < sizeof(state->buffer)); + if (len < sizeof(state->buffer) - state->bufferedSize) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + + { const xxh_u8* xinput = (const xxh_u8*)input; + const xxh_u8* const bEnd = xinput + len; + + if (state->bufferedSize) { /* non-empty buffer: complete first */ + XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize); + xinput += sizeof(state->buffer) - state->bufferedSize; + /* then process one round */ + (void)XXH32_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned); + state->bufferedSize = 0; + } + + XXH_ASSERT(xinput <= bEnd); + if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) { + /* Process the remaining data */ + xinput = XXH32_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned); + } + + if (xinput < bEnd) { + /* Copy the leftover to the tmp buffer */ + XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput)); + state->bufferedSize = (unsigned)(bEnd-xinput); + } + } + + return XXH_OK; +} + + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) +{ + xxh_u32 h32; + + if (state->large_len) { + h32 = XXH32_mergeAccs(state->acc); + } else { + h32 = state->acc[2] /* == seed */ + XXH_PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->buffer, state->bufferedSize, XXH_aligned); +} +#endif /* !XXH_NO_STREAM */ + +/******* Canonical representation *******/ + +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); +} +/*! @ingroup XXH32_family */ +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ +/*! + * @} + * @ingroup impl + * @{ + */ +/******* Memory access *******/ + +typedef XXH64_hash_t xxh_u64; + +#ifdef XXH_OLD_NAMES +# define U64 xxh_u64 +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) +/* + * Manual byteshift. Best for old compilers which don't inline memcpy. + * We actually directly use XXH_readLE64 and XXH_readBE64. + */ +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static xxh_u64 XXH_read64(const void* memPtr) +{ + return *(const xxh_u64*) memPtr; +} + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* + * __attribute__((aligned(1))) is supported by gcc and clang. Originally the + * documentation claimed that it only increased the alignment, but actually it + * can decrease it on gcc, clang, and icc: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, + * https://gcc.godbolt.org/z/xYez1j67Y. + */ +#ifdef XXH_OLD_NAMES +typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((__packed__)) unalign64; +#endif +static xxh_u64 XXH_read64(const void* ptr) +{ + typedef __attribute__((__aligned__(1))) xxh_u64 xxh_unalign64; + return *((const xxh_unalign64*)ptr); +} + +#else + +/* + * Portable and safe solution. Generally efficient. + * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html + */ +static xxh_u64 XXH_read64(const void* memPtr) +{ + xxh_u64 val; + XXH_memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static xxh_u64 XXH_swap64(xxh_u64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + + +/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */ +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) + +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[0] + | ((xxh_u64)bytePtr[1] << 8) + | ((xxh_u64)bytePtr[2] << 16) + | ((xxh_u64)bytePtr[3] << 24) + | ((xxh_u64)bytePtr[4] << 32) + | ((xxh_u64)bytePtr[5] << 40) + | ((xxh_u64)bytePtr[6] << 48) + | ((xxh_u64)bytePtr[7] << 56); +} + +XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr) +{ + const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; + return bytePtr[7] + | ((xxh_u64)bytePtr[6] << 8) + | ((xxh_u64)bytePtr[5] << 16) + | ((xxh_u64)bytePtr[4] << 24) + | ((xxh_u64)bytePtr[3] << 32) + | ((xxh_u64)bytePtr[2] << 40) + | ((xxh_u64)bytePtr[1] << 48) + | ((xxh_u64)bytePtr[0] << 56); +} + +#else +XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); +} + +static xxh_u64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} +#endif + +XXH_FORCE_INLINE xxh_u64 +XXH_readLE64_align(const void* ptr, XXH_alignment align) +{ + if (align==XXH_unaligned) + return XXH_readLE64(ptr); + else + return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); +} + + +/******* xxh64 *******/ +/*! + * @} + * @defgroup XXH64_impl XXH64 implementation + * @ingroup impl + * + * Details on the XXH64 implementation. + * @{ + */ +/* #define rather that static const, to be used as initializers */ +#define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ +#define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ +#define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ +#define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ +#define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ + +#ifdef XXH_OLD_NAMES +# define PRIME64_1 XXH_PRIME64_1 +# define PRIME64_2 XXH_PRIME64_2 +# define PRIME64_3 XXH_PRIME64_3 +# define PRIME64_4 XXH_PRIME64_4 +# define PRIME64_5 XXH_PRIME64_5 +#endif + +/*! @copydoc XXH32_round */ +static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) +{ + acc += input * XXH_PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= XXH_PRIME64_1; +#if (defined(__AVX512F__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) + /* + * DISABLE AUTOVECTORIZATION: + * A compiler fence is used to prevent GCC and Clang from + * autovectorizing the XXH64 loop (pragmas and attributes don't work for some + * reason) without globally disabling AVX512. + * + * Autovectorization of XXH64 tends to be detrimental, + * though the exact outcome may change depending on exact cpu and compiler version. + * For information, it has been reported as detrimental for Skylake-X, + * but possibly beneficial for Zen4. + * + * The default is to disable auto-vectorization, + * but you can select to enable it instead using `XXH_ENABLE_AUTOVECTORIZE` build variable. + */ + XXH_COMPILER_GUARD(acc); +#endif + return acc; +} + +static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; + return acc; +} + +/*! @copydoc XXH32_avalanche */ +static xxh_u64 XXH64_avalanche(xxh_u64 hash) +{ + hash ^= hash >> 33; + hash *= XXH_PRIME64_2; + hash ^= hash >> 29; + hash *= XXH_PRIME64_3; + hash ^= hash >> 32; + return hash; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, align) + +/*! + * @internal + * @brief Sets up the initial accumulator state for XXH64(). + */ +XXH_FORCE_INLINE void +XXH64_initAccs(xxh_u64 *acc, xxh_u64 seed) +{ + XXH_ASSERT(acc != NULL); + acc[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; + acc[1] = seed + XXH_PRIME64_2; + acc[2] = seed + 0; + acc[3] = seed - XXH_PRIME64_1; +} + +/*! + * @internal + * @brief Consumes a block of data for XXH64(). + * + * @return the end input pointer. + */ +XXH_FORCE_INLINE const xxh_u8 * +XXH64_consumeLong( + xxh_u64 *XXH_RESTRICT acc, + xxh_u8 const *XXH_RESTRICT input, + size_t len, + XXH_alignment align +) +{ + const xxh_u8* const bEnd = input + len; + const xxh_u8* const limit = bEnd - 31; + XXH_ASSERT(acc != NULL); + XXH_ASSERT(input != NULL); + XXH_ASSERT(len >= 32); + do { + /* reroll on 32-bit */ + if (sizeof(void *) < sizeof(xxh_u64)) { + size_t i; + for (i = 0; i < 4; i++) { + acc[i] = XXH64_round(acc[i], XXH_get64bits(input)); + input += 8; + } + } else { + acc[0] = XXH64_round(acc[0], XXH_get64bits(input)); input += 8; + acc[1] = XXH64_round(acc[1], XXH_get64bits(input)); input += 8; + acc[2] = XXH64_round(acc[2], XXH_get64bits(input)); input += 8; + acc[3] = XXH64_round(acc[3], XXH_get64bits(input)); input += 8; + } + } while (input < limit); + + return input; +} + +/*! + * @internal + * @brief Merges the accumulator lanes together for XXH64() + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u64 +XXH64_mergeAccs(const xxh_u64 *acc) +{ + XXH_ASSERT(acc != NULL); + { + xxh_u64 h64 = XXH_rotl64(acc[0], 1) + XXH_rotl64(acc[1], 7) + + XXH_rotl64(acc[2], 12) + XXH_rotl64(acc[3], 18); + /* reroll on 32-bit */ + if (sizeof(void *) < sizeof(xxh_u64)) { + size_t i; + for (i = 0; i < 4; i++) { + h64 = XXH64_mergeRound(h64, acc[i]); + } + } else { + h64 = XXH64_mergeRound(h64, acc[0]); + h64 = XXH64_mergeRound(h64, acc[1]); + h64 = XXH64_mergeRound(h64, acc[2]); + h64 = XXH64_mergeRound(h64, acc[3]); + } + return h64; + } +} + +/*! + * @internal + * @brief Processes the last 0-31 bytes of @p ptr. + * + * There may be up to 31 bytes remaining to consume from the input. + * This final stage will digest them to ensure that all input bytes are present + * in the final mix. + * + * @param hash The hash to finalize. + * @param ptr The pointer to the remaining input. + * @param len The remaining length, modulo 32. + * @param align Whether @p ptr is aligned. + * @return The finalized hash + * @see XXH32_finalize(). + */ +XXH_STATIC XXH_PUREF xxh_u64 +XXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) +{ + if (ptr==NULL) XXH_ASSERT(len == 0); + len &= 31; + while (len >= 8) { + xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); + ptr += 8; + hash ^= k1; + hash = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4; + len -= 8; + } + if (len >= 4) { + hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; + ptr += 4; + hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; + len -= 4; + } + while (len > 0) { + hash ^= (*ptr++) * XXH_PRIME64_5; + hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1; + --len; + } + return XXH64_avalanche(hash); +} + +#ifdef XXH_OLD_NAMES +# define PROCESS1_64 XXH_PROCESS1_64 +# define PROCESS4_64 XXH_PROCESS4_64 +# define PROCESS8_64 XXH_PROCESS8_64 +#else +# undef XXH_PROCESS1_64 +# undef XXH_PROCESS4_64 +# undef XXH_PROCESS8_64 +#endif + +/*! + * @internal + * @brief The implementation for @ref XXH64(). + * + * @param input , len , seed Directly passed from @ref XXH64(). + * @param align Whether @p input is aligned. + * @return The calculated hash. + */ +XXH_FORCE_INLINE XXH_PUREF xxh_u64 +XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) +{ + xxh_u64 h64; + if (input==NULL) XXH_ASSERT(len == 0); + + if (len>=32) { /* Process a large block of data */ + xxh_u64 acc[4]; + XXH64_initAccs(acc, seed); + + input = XXH64_consumeLong(acc, input, len, align); + + h64 = XXH64_mergeAccs(acc); + } else { + h64 = seed + XXH_PRIME64_5; + } + + h64 += (xxh_u64) len; + + return XXH64_finalize(h64, input, len, align); +} + + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64 (XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) +{ +#if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, (const xxh_u8*)input, len); + return XXH64_digest(&state); +#else + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); + } } + + return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); + +#endif +} + +/******* Hash Streaming *******/ +#ifndef XXH_NO_STREAM +/*! @ingroup XXH64_family*/ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + XXH_memcpy(dstState, srcState, sizeof(*dstState)); +} + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed) +{ + XXH_ASSERT(statePtr != NULL); + XXH_memset(statePtr, 0, sizeof(*statePtr)); + XXH64_initAccs(statePtr->acc, seed); + return XXH_OK; +} + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH_errorcode +XXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + state->total_len += len; + + XXH_ASSERT(state->bufferedSize <= sizeof(state->buffer)); + if (len < sizeof(state->buffer) - state->bufferedSize) { /* fill in tmp buffer */ + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + + { const xxh_u8* xinput = (const xxh_u8*)input; + const xxh_u8* const bEnd = xinput + len; + + if (state->bufferedSize) { /* non-empty buffer => complete first */ + XXH_memcpy(state->buffer + state->bufferedSize, xinput, sizeof(state->buffer) - state->bufferedSize); + xinput += sizeof(state->buffer) - state->bufferedSize; + /* and process one round */ + (void)XXH64_consumeLong(state->acc, state->buffer, sizeof(state->buffer), XXH_aligned); + state->bufferedSize = 0; + } + + XXH_ASSERT(xinput <= bEnd); + if ((size_t)(bEnd - xinput) >= sizeof(state->buffer)) { + /* Process the remaining data */ + xinput = XXH64_consumeLong(state->acc, xinput, (size_t)(bEnd - xinput), XXH_unaligned); + } + + if (xinput < bEnd) { + /* Copy the leftover to the tmp buffer */ + XXH_memcpy(state->buffer, xinput, (size_t)(bEnd-xinput)); + state->bufferedSize = (unsigned)(bEnd-xinput); + } + } + + return XXH_OK; +} + + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state) +{ + xxh_u64 h64; + + if (state->total_len >= 32) { + h64 = XXH64_mergeAccs(state->acc); + } else { + h64 = state->acc[2] /*seed*/ + XXH_PRIME64_5; + } + + h64 += (xxh_u64) state->total_len; + + return XXH64_finalize(h64, state->buffer, (size_t)state->total_len, XXH_aligned); +} +#endif /* !XXH_NO_STREAM */ + +/******* Canonical representation *******/ + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + XXH_memcpy(dst, &hash, sizeof(*dst)); +} + +/*! @ingroup XXH64_family */ +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#ifndef XXH_NO_XXH3 + +/* ********************************************************************* +* XXH3 +* New generation hash designed for speed on small keys and vectorization +************************************************************************ */ +/*! + * @} + * @defgroup XXH3_impl XXH3 implementation + * @ingroup impl + * @{ + */ + +/* === Compiler specifics === */ + + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) \ + || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ + || defined(__clang__) +# define XXH_likely(x) __builtin_expect(x, 1) +# define XXH_unlikely(x) __builtin_expect(x, 0) +#else +# define XXH_likely(x) (x) +# define XXH_unlikely(x) (x) +#endif + +#ifndef XXH_HAS_INCLUDE +# ifdef __has_include +/* + * Not defined as XXH_HAS_INCLUDE(x) (function-like) because + * this causes segfaults in Apple Clang 4.2 (on Mac OS X 10.7 Lion) + */ +# define XXH_HAS_INCLUDE __has_include +# else +# define XXH_HAS_INCLUDE(x) 0 +# endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +# if defined(__ARM_FEATURE_SVE) +# include +# endif +# if defined(__ARM_NEON__) || defined(__ARM_NEON) \ + || (defined(_M_ARM) && _M_ARM >= 7) \ + || defined(_M_ARM64) || defined(_M_ARM64EC) \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* WASM SIMD128 via SIMDe */ +# define inline __inline__ /* circumvent a clang bug */ +# include +# undef inline +# elif defined(__AVX2__) +# include +# elif defined(__SSE2__) +# include +# elif defined(__loongarch_asx) +# include +# include +# elif defined(__loongarch_sx) +# include +# endif +#endif + +#if defined(_MSC_VER) +# include +#endif + +/* + * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while + * remaining a true 64-bit/128-bit hash function. + * + * This is done by prioritizing a subset of 64-bit operations that can be + * emulated without too many steps on the average 32-bit machine. + * + * For example, these two lines seem similar, and run equally fast on 64-bit: + * + * xxh_u64 x; + * x ^= (x >> 47); // good + * x ^= (x >> 13); // bad + * + * However, to a 32-bit machine, there is a major difference. + * + * x ^= (x >> 47) looks like this: + * + * x.lo ^= (x.hi >> (47 - 32)); + * + * while x ^= (x >> 13) looks like this: + * + * // note: funnel shifts are not usually cheap. + * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); + * x.hi ^= (x.hi >> 13); + * + * The first one is significantly faster than the second, simply because the + * shift is larger than 32. This means: + * - All the bits we need are in the upper 32 bits, so we can ignore the lower + * 32 bits in the shift. + * - The shift result will always fit in the lower 32 bits, and therefore, + * we can ignore the upper 32 bits in the xor. + * + * Thanks to this optimization, XXH3 only requires these features to be efficient: + * + * - Usable unaligned access + * - A 32-bit or 64-bit ALU + * - If 32-bit, a decent ADC instruction + * - A 32 or 64-bit multiply with a 64-bit result + * - For the 128-bit variant, a decent byteswap helps short inputs. + * + * The first two are already required by XXH32, and almost all 32-bit and 64-bit + * platforms which can run XXH32 can run XXH3 efficiently. + * + * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one + * notable exception. + * + * First of all, Thumb-1 lacks support for the UMULL instruction which + * performs the important long multiply. This means numerous __aeabi_lmul + * calls. + * + * Second of all, the 8 functional registers are just not enough. + * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need + * Lo registers, and this shuffling results in thousands more MOVs than A32. + * + * A32 and T32 don't have this limitation. They can access all 14 registers, + * do a 32->64 multiply with UMULL, and the flexible operand allowing free + * shifts is helpful, too. + * + * Therefore, we do a quick sanity check. + * + * If compiling Thumb-1 for a target which supports ARM instructions, we will + * emit a warning, as it is not a "sane" platform to compile for. + * + * Usually, if this happens, it is because of an accident and you probably need + * to specify -march, as you likely meant to compile for a newer architecture. + * + * Credit: large sections of the vectorial and asm source code paths + * have been contributed by @easyaspi314 + */ +#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) +# warning "XXH3 is highly inefficient without ARM or Thumb-2." +#endif + +/* ========================================== + * Vectorization detection + * ========================================== */ + +#ifdef XXH_DOXYGEN +/*! + * @ingroup tuning + * @brief Overrides the vectorization implementation chosen for XXH3. + * + * Can be defined to 0 to disable SIMD, + * or any other authorized value of @ref XXH_VECTOR. + * + * If this is not defined, it uses predefined macros to determine the best + * implementation. + */ +# define XXH_VECTOR XXH_SCALAR +/*! + * @ingroup tuning + * @brief Selects the minimum alignment for XXH3's accumulators. + * + * When using SIMD, this should match the alignment required for said vector + * type, so, for example, 32 for AVX2. + * + * Default: Auto detected. + */ +# define XXH_ACC_ALIGN 8 +#endif + +/* Actual definition */ +#ifndef XXH_DOXYGEN +#endif + +#ifndef XXH_VECTOR /* can be defined on command line */ +# if defined(__ARM_FEATURE_SVE) +# define XXH_VECTOR XXH_SVE +# elif ( \ + defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ + || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \ + || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* wasm simd128 via SIMDe */ \ + ) && ( \ + defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ + ) +# define XXH_VECTOR XXH_NEON +# elif defined(__AVX512F__) +# define XXH_VECTOR XXH_AVX512 +# elif defined(__AVX2__) +# define XXH_VECTOR XXH_AVX2 +# elif defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) +# define XXH_VECTOR XXH_SSE2 +# elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ + || (defined(__s390x__) && defined(__VEC__)) \ + && defined(__GNUC__) /* TODO: IBM XL */ +# define XXH_VECTOR XXH_VSX +# elif defined(__loongarch_asx) +# define XXH_VECTOR XXH_LASX +# elif defined(__loongarch_sx) +# define XXH_VECTOR XXH_LSX +# else +# define XXH_VECTOR XXH_SCALAR +# endif +#endif + +/* __ARM_FEATURE_SVE is only supported by GCC & Clang. */ +#if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE) +# ifdef _MSC_VER +# pragma warning(once : 4606) +# else +# warning "__ARM_FEATURE_SVE isn't supported. Use SCALAR instead." +# endif +# undef XXH_VECTOR +# define XXH_VECTOR XXH_SCALAR +#endif + +/* + * Controls the alignment of the accumulator, + * for compatibility with aligned vector loads, which are usually faster. + */ +#ifndef XXH_ACC_ALIGN +# if defined(XXH_X86DISPATCH) +# define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ +# elif XXH_VECTOR == XXH_SCALAR /* scalar */ +# define XXH_ACC_ALIGN 8 +# elif XXH_VECTOR == XXH_SSE2 /* sse2 */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_AVX2 /* avx2 */ +# define XXH_ACC_ALIGN 32 +# elif XXH_VECTOR == XXH_NEON /* neon */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_VSX /* vsx */ +# define XXH_ACC_ALIGN 16 +# elif XXH_VECTOR == XXH_AVX512 /* avx512 */ +# define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_SVE /* sve */ +# define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_LASX /* lasx */ +# define XXH_ACC_ALIGN 64 +# elif XXH_VECTOR == XXH_LSX /* lsx */ +# define XXH_ACC_ALIGN 64 +# endif +#endif + +#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ + || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 +# define XXH_SEC_ALIGN XXH_ACC_ALIGN +#elif XXH_VECTOR == XXH_SVE +# define XXH_SEC_ALIGN XXH_ACC_ALIGN +#else +# define XXH_SEC_ALIGN 8 +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define XXH_ALIASING __attribute__((__may_alias__)) +#else +# define XXH_ALIASING /* nothing */ +#endif + +/* + * UGLY HACK: + * GCC usually generates the best code with -O3 for xxHash. + * + * However, when targeting AVX2, it is overzealous in its unrolling resulting + * in code roughly 3/4 the speed of Clang. + * + * There are other issues, such as GCC splitting _mm256_loadu_si256 into + * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which + * only applies to Sandy and Ivy Bridge... which don't even support AVX2. + * + * That is why when compiling the AVX2 version, it is recommended to use either + * -O2 -mavx2 -march=haswell + * or + * -O2 -mavx2 -mno-avx256-split-unaligned-load + * for decent performance, or to use Clang instead. + * + * Fortunately, we can control the first one with a pragma that forces GCC into + * -O2, but the other one we can't control without "failed to inline always + * inline function due to target mismatch" warnings. + */ +#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ + && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */ +# pragma GCC push_options +# pragma GCC optimize("-O2") +#endif + +#if XXH_VECTOR == XXH_NEON + +/* + * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3 + * optimizes out the entire hashLong loop because of the aliasing violation. + * + * However, GCC is also inefficient at load-store optimization with vld1q/vst1q, + * so the only option is to mark it as aliasing. + */ +typedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING; + +/*! + * @internal + * @brief `vld1q_u64` but faster and alignment-safe. + * + * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only + * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86). + * + * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it + * prohibits load-store optimizations. Therefore, a direct dereference is used. + * + * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe + * unaligned load. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */ +{ + return *(xxh_aliasing_uint64x2_t const *)ptr; +} +#else +XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) +{ + return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr)); +} +#endif + +/*! + * @internal + * @brief `vmlal_u32` on low and high halves of a vector. + * + * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with + * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32` + * with `vmlal_u32`. + */ +#if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11 +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* Inline assembly is the only way */ + __asm__("umlal %0.2d, %1.2s, %2.2s" : "+w" (acc) : "w" (lhs), "w" (rhs)); + return acc; +} +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + /* This intrinsic works as expected */ + return vmlal_high_u32(acc, lhs, rhs); +} +#else +/* Portable intrinsic versions */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs)); +} +/*! @copydoc XXH_vmlal_low_u32 + * Assume the compiler converts this to vmlal_high_u32 on aarch64 */ +XXH_FORCE_INLINE uint64x2_t +XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) +{ + return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs)); +} +#endif + +/*! + * @ingroup tuning + * @brief Controls the NEON to scalar ratio for XXH3 + * + * This can be set to 2, 4, 6, or 8. + * + * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used. + * + * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those + * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU + * bandwidth. + * + * This is even more noticeable on the more advanced cores like the Cortex-A76 which + * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. + * + * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes + * and 2 scalar lanes, which is chosen by default. + * + * This does not apply to Apple processors or 32-bit processors, which run better with + * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes. + * + * This change benefits CPUs with large micro-op buffers without negatively affecting + * most other CPUs: + * + * | Chipset | Dispatch type | NEON only | 6:2 hybrid | Diff. | + * |:----------------------|:--------------------|----------:|-----------:|------:| + * | Snapdragon 730 (A76) | 2 NEON/8 micro-ops | 8.8 GB/s | 10.1 GB/s | ~16% | + * | Snapdragon 835 (A73) | 2 NEON/3 micro-ops | 5.1 GB/s | 5.3 GB/s | ~5% | + * | Marvell PXA1928 (A53) | In-order dual-issue | 1.9 GB/s | 1.9 GB/s | 0% | + * | Apple M1 | 4 NEON/8 micro-ops | 37.3 GB/s | 36.1 GB/s | ~-3% | + * + * It also seems to fix some bad codegen on GCC, making it almost as fast as clang. + * + * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning + * it effectively becomes worse 4. + * + * @see XXH3_accumulate_512_neon() + */ +# ifndef XXH3_NEON_LANES +# if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \ + && !defined(__APPLE__) && XXH_SIZE_OPT <= 0 +# define XXH3_NEON_LANES 6 +# else +# define XXH3_NEON_LANES XXH_ACC_NB +# endif +# endif +#endif /* XXH_VECTOR == XXH_NEON */ + +/* + * VSX and Z Vector helpers. + * + * This is very messy, and any pull requests to clean this up are welcome. + * + * There are a lot of problems with supporting VSX and s390x, due to + * inconsistent intrinsics, spotty coverage, and multiple endiannesses. + */ +#if XXH_VECTOR == XXH_VSX +/* Annoyingly, these headers _may_ define three macros: `bool`, `vector`, + * and `pixel`. This is a problem for obvious reasons. + * + * These keywords are unnecessary; the spec literally says they are + * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd + * after including the header. + * + * We use pragma push_macro/pop_macro to keep the namespace clean. */ +# pragma push_macro("bool") +# pragma push_macro("vector") +# pragma push_macro("pixel") +/* silence potential macro redefined warnings */ +# undef bool +# undef vector +# undef pixel + +# if defined(__s390x__) +# include +# else +# include +# endif + +/* Restore the original macro values, if applicable. */ +# pragma pop_macro("pixel") +# pragma pop_macro("vector") +# pragma pop_macro("bool") + +typedef __vector unsigned long long xxh_u64x2; +typedef __vector unsigned char xxh_u8x16; +typedef __vector unsigned xxh_u32x4; + +/* + * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue. + */ +typedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING; + +# ifndef XXH_VSX_BE +# if defined(__BIG_ENDIAN__) \ + || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define XXH_VSX_BE 1 +# elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ +# warning "-maltivec=be is not recommended. Please use native endianness." +# define XXH_VSX_BE 1 +# else +# define XXH_VSX_BE 0 +# endif +# endif /* !defined(XXH_VSX_BE) */ + +# if XXH_VSX_BE +# if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) +# define XXH_vec_revb vec_revb +# else +/*! + * A polyfill for POWER9's vec_revb(). + */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) +{ + xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 }; + return vec_perm(val, val, vByteSwap); +} +# endif +# endif /* XXH_VSX_BE */ + +/*! + * Performs an unaligned vector load and byte swaps it on big endian. + */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) +{ + xxh_u64x2 ret; + XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); +# if XXH_VSX_BE + ret = XXH_vec_revb(ret); +# endif + return ret; +} + +/* + * vec_mulo and vec_mule are very problematic intrinsics on PowerPC + * + * These intrinsics weren't added until GCC 8, despite existing for a while, + * and they are endian dependent. Also, their meaning swap depending on version. + * */ +# if defined(__s390x__) + /* s390x is always big endian, no issue on this platform */ +# define XXH_vec_mulo vec_mulo +# define XXH_vec_mule vec_mule +# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__) +/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ + /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */ +# define XXH_vec_mulo __builtin_altivec_vmulouw +# define XXH_vec_mule __builtin_altivec_vmuleuw +# else +/* gcc needs inline assembly */ +/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) +{ + xxh_u64x2 result; + __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) +{ + xxh_u64x2 result; + __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); + return result; +} +# endif /* XXH_vec_mulo, XXH_vec_mule */ +#endif /* XXH_VECTOR == XXH_VSX */ + +#if XXH_VECTOR == XXH_SVE +#define ACCRND(acc, offset) \ +do { \ + svuint64_t input_vec = svld1_u64(mask, xinput + offset); \ + svuint64_t secret_vec = svld1_u64(mask, xsecret + offset); \ + svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec); \ + svuint64_t swapped = svtbl_u64(input_vec, kSwap); \ + svuint64_t mixed_lo = svextw_u64_x(mask, mixed); \ + svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32); \ + svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \ + acc = svadd_u64_x(mask, acc, mul); \ +} while (0) +#endif /* XXH_VECTOR == XXH_SVE */ + +/* prefetch + * can be disabled, by declaring XXH_NO_PREFETCH build macro */ +#if defined(XXH_NO_PREFETCH) +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +#else +# if XXH_SIZE_OPT >= 1 +# define XXH_PREFETCH(ptr) (void)(ptr) +# elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ +# include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ +# define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) +# elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) +# define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) +# else +# define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ +# endif +#endif /* XXH_NO_PREFETCH */ + + +/* ========================================== + * XXH3 default settings + * ========================================== */ + +#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ + +#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) +# error "default keyset is not large enough" +#endif + +/*! + * @internal + * @def XXH3_kSecret + * @brief Pseudorandom secret taken directly from FARSH. */ +XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, +}; + +static const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL; /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */ +static const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL; /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */ + +#ifdef XXH_OLD_NAMES +# define kSecret XXH3_kSecret +#endif + +#ifdef XXH_DOXYGEN +/*! + * @brief Calculates a 32-bit to 64-bit long multiply. + * + * Implemented as a macro. + * + * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't + * need to (but it shouldn't need to anyways, it is about 7 instructions to do + * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we + * use that instead of the normal method. + * + * If you are compiling for platforms like Thumb-1 and don't have a better option, + * you may also want to write your own long multiply routine here. + * + * @param x, y Numbers to be multiplied + * @return 64-bit product of the low 32 bits of @p x and @p y. + */ +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64(xxh_u64 x, xxh_u64 y) +{ + return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); +} +#elif defined(_MSC_VER) && defined(_M_IX86) +# define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) +#else +/* + * Downcast + upcast is usually better than masking on older compilers like + * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers. + * + * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands + * and perform a full 64x64 multiply -- entirely redundant on 32-bit. + */ +# define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) +#endif + +/*! + * @brief Calculates a 64->128-bit long multiply. + * + * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar + * version. + * + * @param lhs , rhs The 64-bit integers to be multiplied + * @return The 128-bit result represented in an @ref XXH128_hash_t. + */ +static XXH128_hash_t +XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) +{ + /* + * GCC/Clang __uint128_t method. + * + * On most 64-bit targets, GCC and Clang define a __uint128_t type. + * This is usually the best way as it usually uses a native long 64-bit + * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. + * + * Usually. + * + * Despite being a 32-bit platform, Clang (and emscripten) define this type + * despite not having the arithmetic for it. This results in a laggy + * compiler builtin call which calculates a full 128-bit multiply. + * In that case it is best to use the portable one. + * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 + */ +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \ + && defined(__SIZEOF_INT128__) \ + || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) + + __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; + XXH128_hash_t r128; + r128.low64 = (xxh_u64)(product); + r128.high64 = (xxh_u64)(product >> 64); + return r128; + + /* + * MSVC for x64's _umul128 method. + * + * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); + * + * This compiles to single operand MUL on x64. + */ +#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(_umul128) +#endif + xxh_u64 product_high; + xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); + XXH128_hash_t r128; + r128.low64 = product_low; + r128.high64 = product_high; + return r128; + + /* + * MSVC for ARM64's __umulh method. + * + * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. + */ +#elif defined(_M_ARM64) || defined(_M_ARM64EC) + +#ifndef _MSC_VER +# pragma intrinsic(__umulh) +#endif + XXH128_hash_t r128; + r128.low64 = lhs * rhs; + r128.high64 = __umulh(lhs, rhs); + return r128; + +#else + /* + * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. + * + * This is a fast and simple grade school multiply, which is shown below + * with base 10 arithmetic instead of base 0x100000000. + * + * 9 3 // D2 lhs = 93 + * x 7 5 // D2 rhs = 75 + * ---------- + * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 + * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 + * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 + * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 + * --------- + * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 + * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 + * --------- + * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 + * + * The reasons for adding the products like this are: + * 1. It avoids manual carry tracking. Just like how + * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. + * This avoids a lot of complexity. + * + * 2. It hints for, and on Clang, compiles to, the powerful UMAAL + * instruction available in ARM's Digital Signal Processing extension + * in 32-bit ARMv6 and later, which is shown below: + * + * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) + * { + * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; + * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); + * *RdHi = (xxh_u32)(product >> 32); + * } + * + * This instruction was designed for efficient long multiplication, and + * allows this to be calculated in only 4 instructions at speeds + * comparable to some 64-bit ALUs. + * + * 3. It isn't terrible on other platforms. Usually this will be a couple + * of 32-bit ADD/ADCs. + */ + + /* First calculate all of the cross products. */ + xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + /* Now add the products together. These will never overflow. */ + xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + XXH128_hash_t r128; + r128.low64 = lower; + r128.high64 = upper; + return r128; +#endif +} + +/*! + * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. + * + * The reason for the separate function is to prevent passing too many structs + * around by value. This will hopefully inline the multiply, but we don't force it. + * + * @param lhs , rhs The 64-bit integers to multiply + * @return The low 64 bits of the product XOR'd by the high 64 bits. + * @see XXH_mult64to128() + */ +static xxh_u64 +XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) +{ + XXH128_hash_t product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; +} + +/*! Seems to produce slightly better code on GCC for some reason. */ +XXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) +{ + XXH_ASSERT(0 <= shift && shift < 64); + return v64 ^ (v64 >> shift); +} + +/* + * This is a fast avalanche stage, + * suitable when input bits are already partially mixed + */ +static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) +{ + h64 = XXH_xorshift64(h64, 37); + h64 *= PRIME_MX1; + h64 = XXH_xorshift64(h64, 32); + return h64; +} + +/* + * This is a stronger avalanche, + * inspired by Pelle Evensen's rrmxmx + * preferable when input has not been previously mixed + */ +static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) +{ + /* this mix is inspired by Pelle Evensen's rrmxmx */ + h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); + h64 *= PRIME_MX2; + h64 ^= (h64 >> 35) + len ; + h64 *= PRIME_MX2; + return XXH_xorshift64(h64, 28); +} + + +/* ========================================== + * Short keys + * ========================================== + * One of the shortcomings of XXH32 and XXH64 was that their performance was + * sub-optimal on short lengths. It used an iterative algorithm which strongly + * favored lengths that were a multiple of 4 or 8. + * + * Instead of iterating over individual inputs, we use a set of single shot + * functions which piece together a range of lengths and operate in constant time. + * + * Additionally, the number of multiplies has been significantly reduced. This + * reduces latency, especially when emulating 64-bit multiplies on 32-bit. + * + * Depending on the platform, this may or may not be faster than XXH32, but it + * is almost guaranteed to be faster than XXH64. + */ + +/* + * At very short lengths, there isn't enough input to fully hide secrets, or use + * the entire secret. + * + * There is also only a limited amount of mixing we can do before significantly + * impacting performance. + * + * Therefore, we use different sections of the secret and always mix two secret + * samples with an XOR. This should have no effect on performance on the + * seedless or withSeed variants because everything _should_ be constant folded + * by modern compilers. + * + * The XOR mixing hides individual parts of the secret and increases entropy. + * + * This adds an extra layer of strength for custom secrets. + */ +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combined = { input[0], 0x01, input[0], input[0] } + * len = 2: combined = { input[1], 0x02, input[0], input[1] } + * len = 3: combined = { input[2], 0x03, input[0], input[1] } + */ + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) + | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; + xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; + return XXH64_avalanche(keyed); + } +} + +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { xxh_u32 const input1 = XXH_readLE32(input); + xxh_u32 const input2 = XXH_readLE32(input + len - 4); + xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed; + xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); + xxh_u64 const keyed = input64 ^ bitflip; + return XXH3_rrmxmx(keyed, len); + } +} + +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; + xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; + xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; + xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; + xxh_u64 const acc = len + + XXH_swap64(input_lo) + input_hi + + XXH3_mul128_fold64(input_lo, input_hi); + return XXH3_avalanche(acc); + } +} + +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); + if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); + if (len) return XXH3_len_1to3_64b(input, len, secret, seed); + return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64))); + } +} + +/* + * DISCLAIMER: There are known *seed-dependent* multicollisions here due to + * multiplication by zero, affecting hashes of lengths 17 to 240. + * + * However, they are very unlikely. + * + * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all + * unseeded non-cryptographic hashes, it does not attempt to defend itself + * against specially crafted inputs, only random inputs. + * + * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes + * cancelling out the secret is taken an arbitrary number of times (addressed + * in XXH3_accumulate_512), this collision is very unlikely with random inputs + * and/or proper seeding: + * + * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a + * function that is only called up to 16 times per hash with up to 240 bytes of + * input. + * + * This is not too bad for a non-cryptographic hash function, especially with + * only 64 bit outputs. + * + * The 128-bit variant (which trades some speed for strength) is NOT affected + * by this, although it is always a good idea to use a proper seed if you care + * about strength. + */ +XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) +{ +#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ + && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ + /* + * UGLY HACK: + * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in + * slower code. + * + * By forcing seed64 into a register, we disrupt the cost model and + * cause it to scalarize. See `XXH32_round()` + * + * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, + * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on + * GCC 9.2, despite both emitting scalar code. + * + * GCC generates much better scalar code than Clang for the rest of XXH3, + * which is why finding a more optimal codepath is an interest. + */ + XXH_COMPILER_GUARD(seed64); +#endif + { xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 const input_hi = XXH_readLE64(input+8); + return XXH3_mul128_fold64( + input_lo ^ (XXH_readLE64(secret) + seed64), + input_hi ^ (XXH_readLE64(secret+8) - seed64) + ); + } +} + +/* For mid range keys, XXH3 uses a Mum-hash variant. */ +XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { xxh_u64 acc = len * XXH_PRIME64_1; +#if XXH_SIZE_OPT >= 1 + /* Smaller and cleaner, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc += XXH3_mix16B(input+16 * i, secret+32*i, seed); + acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed); + } while (i-- != 0); +#else + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc += XXH3_mix16B(input+48, secret+96, seed); + acc += XXH3_mix16B(input+len-64, secret+112, seed); + } + acc += XXH3_mix16B(input+32, secret+64, seed); + acc += XXH3_mix16B(input+len-48, secret+80, seed); + } + acc += XXH3_mix16B(input+16, secret+32, seed); + acc += XXH3_mix16B(input+len-32, secret+48, seed); + } + acc += XXH3_mix16B(input+0, secret+0, seed); + acc += XXH3_mix16B(input+len-16, secret+16, seed); +#endif + return XXH3_avalanche(acc); + } +} + +XXH_NO_INLINE XXH_PUREF XXH64_hash_t +XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + #define XXH3_MIDSIZE_STARTOFFSET 3 + #define XXH3_MIDSIZE_LASTOFFSET 17 + + { xxh_u64 acc = len * XXH_PRIME64_1; + xxh_u64 acc_end; + unsigned int const nbRounds = (unsigned int)len / 16; + unsigned int i; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + for (i=0; i<8; i++) { + acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); + } + /* last bytes */ + acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); + XXH_ASSERT(nbRounds >= 8); + acc = XXH3_avalanche(acc); +#if defined(__clang__) /* Clang */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. + * In everywhere else, it uses scalar code. + * + * For 64->128-bit multiplies, even if the NEON was 100% optimal, it + * would still be slower than UMAAL (see XXH_mult64to128). + * + * Unfortunately, Clang doesn't handle the long multiplies properly and + * converts them to the nonexistent "vmulq_u64" intrinsic, which is then + * scalarized into an ugly mess of VMOV.32 instructions. + * + * This mess is difficult to avoid without turning autovectorization + * off completely, but they are usually relatively minor and/or not + * worth it to fix. + * + * This loop is the easiest to fix, as unlike XXH32, this pragma + * _actually works_ because it is a loop vectorization instead of an + * SLP vectorization. + */ + #pragma clang loop vectorize(disable) +#endif + for (i=8 ; i < nbRounds; i++) { + /* + * Prevents clang for unrolling the acc loop and interleaving with this one. + */ + XXH_COMPILER_GUARD(acc); + acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); + } + return XXH3_avalanche(acc + acc_end); + } +} + + +/* ======= Long Keys ======= */ + +#define XXH_STRIPE_LEN 64 +#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ +#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) + +#ifdef XXH_OLD_NAMES +# define STRIPE_LEN XXH_STRIPE_LEN +# define ACC_NB XXH_ACC_NB +#endif + +#ifndef XXH_PREFETCH_DIST +# ifdef __clang__ +# define XXH_PREFETCH_DIST 320 +# else +# if (XXH_VECTOR == XXH_AVX512) +# define XXH_PREFETCH_DIST 512 +# else +# define XXH_PREFETCH_DIST 384 +# endif +# endif /* __clang__ */ +#endif /* XXH_PREFETCH_DIST */ + +/* + * These macros are to generate an XXH3_accumulate() function. + * The two arguments select the name suffix and target attribute. + * + * The name of this symbol is XXH3_accumulate_() and it calls + * XXH3_accumulate_512_(). + * + * It may be useful to hand implement this function if the compiler fails to + * optimize the inline function. + */ +#define XXH3_ACCUMULATE_TEMPLATE(name) \ +void \ +XXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc, \ + const xxh_u8* XXH_RESTRICT input, \ + const xxh_u8* XXH_RESTRICT secret, \ + size_t nbStripes) \ +{ \ + size_t n; \ + for (n = 0; n < nbStripes; n++ ) { \ + const xxh_u8* const in = input + n*XXH_STRIPE_LEN; \ + XXH_PREFETCH(in + XXH_PREFETCH_DIST); \ + XXH3_accumulate_512_##name( \ + acc, \ + in, \ + secret + n*XXH_SECRET_CONSUME_RATE); \ + } \ +} + + +XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) +{ + if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); + XXH_memcpy(dst, &v64, sizeof(v64)); +} + +/* Several intrinsic functions below are supposed to accept __int64 as argument, + * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . + * However, several environments do not define __int64 type, + * requiring a workaround. + */ +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) + typedef int64_t xxh_i64; +#else + /* the following type must have a width of 64-bit */ + typedef long long xxh_i64; +#endif + + +/* + * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. + * + * It is a hardened version of UMAC, based off of FARSH's implementation. + * + * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD + * implementations, and it is ridiculously fast. + * + * We harden it by mixing the original input to the accumulators as well as the product. + * + * This means that in the (relatively likely) case of a multiply by zero, the + * original input is preserved. + * + * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve + * cross-pollination, as otherwise the upper and lower halves would be + * essentially independent. + * + * This doesn't matter on 64-bit hashes since they all get merged together in + * the end, so we skip the extra step. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + +#if (XXH_VECTOR == XXH_AVX512) \ + || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) + +#ifndef XXH_TARGET_AVX512 +# define XXH_TARGET_AVX512 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + __m512i* const xacc = (__m512i *) acc; + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + + { + /* data_vec = input[0]; */ + __m512i const data_vec = _mm512_loadu_si512 (input); + /* key_vec = secret[0]; */ + __m512i const key_vec = _mm512_loadu_si512 (secret); + /* data_key = data_vec ^ key_vec; */ + __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); + /* xacc[0] += swap(data_vec); */ + __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2)); + __m512i const sum = _mm512_add_epi64(*xacc, data_swap); + /* xacc[0] += product; */ + *xacc = _mm512_add_epi64(product, sum); + } +} +XXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512) + +/* + * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. + * + * Multiplication isn't perfect, as explained by Google in HighwayHash: + * + * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to + * // varying degrees. In descending order of goodness, bytes + * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. + * // As expected, the upper and lower bytes are much worse. + * + * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 + * + * Since our algorithm uses a pseudorandom secret to add some variance into the + * mix, we don't need to (or want to) mix as often or as much as HighwayHash does. + * + * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid + * extraction. + * + * Both XXH3_64bits and XXH3_128bits use this subroutine. + */ + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 63) == 0); + XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); + { __m512i* const xacc = (__m512i*) acc; + const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); + + /* xacc[0] ^= (xacc[0] >> 47) */ + __m512i const acc_vec = *xacc; + __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); + /* xacc[0] ^= secret; */ + __m512i const key_vec = _mm512_loadu_si512 (secret); + __m512i const data_key = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */); + + /* xacc[0] *= XXH_PRIME32_1; */ + __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32); + __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); + __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); + *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); + } +} + +XXH_FORCE_INLINE XXH_TARGET_AVX512 void +XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); + XXH_ASSERT(((size_t)customSecret & 63) == 0); + (void)(&XXH_writeLE64); + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); + __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64); + __m512i const seed = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos); + + const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); + __m512i* const dest = ( __m512i*) customSecret; + int i; + XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 63) == 0); + for (i=0; i < nbRounds; ++i) { + dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_AVX2) \ + || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) + +#ifndef XXH_TARGET_AVX2 +# define XXH_TARGET_AVX2 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void +XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { __m256i* const xacc = (__m256i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xinput = (const __m256i *) input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xsecret = (const __m256i *) secret; + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { + /* data_vec = xinput[i]; */ + __m256i const data_vec = _mm256_loadu_si256 (xinput+i); + /* key_vec = xsecret[i]; */ + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + /* data_key = data_vec ^ key_vec; */ + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); + __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm256_add_epi64(product, sum); + } } +} +XXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2) + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void +XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { __m256i* const xacc = (__m256i*) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ + const __m256i* const xsecret = (const __m256i *) secret; + const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m256i const acc_vec = xacc[i]; + __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47); + __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted); + /* xacc[i] ^= xsecret; */ + __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); + __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32); + __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); + __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); + } + } +} + +XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); + XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); + (void)(&XXH_writeLE64); + XXH_PREFETCH(customSecret); + { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); + + const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); + __m256i* dest = ( __m256i*) customSecret; + +# if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + XXH_COMPILER_GUARD(dest); +# endif + XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dest & 31) == 0); + + /* GCC -O2 need unroll loop manually */ + dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed); + dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed); + dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed); + dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed); + dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed); + dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed); + } +} + +#endif + +/* x86dispatch always generates SSE2 */ +#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) + +#ifndef XXH_TARGET_SSE2 +# define XXH_TARGET_SSE2 /* disable attribute target */ +#endif + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void +XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + /* SSE2 is just a half-scale version of the AVX2 version. */ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { __m128i* const xacc = (__m128i *) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xinput = (const __m128i *) input; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xsecret = (const __m128i *) secret; + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { + /* data_vec = xinput[i]; */ + __m128i const data_vec = _mm_loadu_si128 (xinput+i); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = _mm_mul_epu32 (data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); + __m128i const sum = _mm_add_epi64(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = _mm_add_epi64(product, sum); + } } +} +XXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2) + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void +XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { __m128i* const xacc = (__m128i*) acc; + /* Unaligned. This is mainly for pointer arithmetic, and because + * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ + const __m128i* const xsecret = (const __m128i *) secret; + const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); + + size_t i; + for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); + __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = _mm_loadu_si128 (xsecret+i); + __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); + __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); + __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); + xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); + } + } +} + +XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + (void)(&XXH_writeLE64); + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); + +# if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 + /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ + XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; + __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); +# else + __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); +# endif + int i; + + const void* const src16 = XXH3_kSecret; + __m128i* dst16 = (__m128i*) customSecret; +# if defined(__GNUC__) || defined(__clang__) + /* + * On GCC & Clang, marking 'dest' as modified will cause the compiler: + * - do not extract the secret from sse registers in the internal loop + * - use less common registers, and avoid pushing these reg into stack + */ + XXH_COMPILER_GUARD(dst16); +# endif + XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ + XXH_ASSERT(((size_t)dst16 & 15) == 0); + + for (i=0; i < nbRounds; ++i) { + dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_NEON) + +/* forward declarations for the scalar routines */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, size_t lane); + +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, size_t lane); + +/*! + * @internal + * @brief The bulk processing loop for NEON and WASM SIMD128. + * + * The NEON code path is actually partially scalar when running on AArch64. This + * is to optimize the pipelining and can have up to 15% speedup depending on the + * CPU, and it also mitigates some GCC codegen issues. + * + * @see XXH3_NEON_LANES for configuring this and details about this optimization. + * + * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit + * integers instead of the other platforms which mask full 64-bit vectors, + * so the setup is more complicated than just shifting right. + * + * Additionally, there is an optimization for 4 lanes at once noted below. + * + * Since, as stated, the most optimal amount of lanes for Cortexes is 6, + * there needs to be *three* versions of the accumulate operation used + * for the remaining 2 lanes. + * + * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap + * nearly perfectly. + */ + +XXH_FORCE_INLINE void +XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0); + { /* GCC for darwin arm64 does not like aliasing here */ + xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc; + /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ + uint8_t const* xinput = (const uint8_t *) input; + uint8_t const* xsecret = (const uint8_t *) secret; + + size_t i; +#ifdef __wasm_simd128__ + /* + * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret + * is constant propagated, which results in it converting it to this + * inside the loop: + * + * a = v128.load(XXH3_kSecret + 0 + $secret_offset, offset = 0) + * b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0) + * ... + * + * This requires a full 32-bit address immediate (and therefore a 6 byte + * instruction) as well as an add for each offset. + * + * Putting an asm guard prevents it from folding (at the cost of losing + * the alignment hint), and uses the free offset in `v128.load` instead + * of adding secret_offset each time which overall reduces code size by + * about a kilobyte and improves performance. + */ + XXH_COMPILER_GUARD(xsecret); +#endif + /* Scalar lanes use the normal scalarRound routine */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } + i = 0; + /* 4 NEON lanes at a time. */ + for (; i+1 < XXH3_NEON_LANES / 2; i+=2) { + /* data_vec = xinput[i]; */ + uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput + (i * 16)); + uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput + ((i+1) * 16)); + /* key_vec = xsecret[i]; */ + uint64x2_t key_vec_1 = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t key_vec_2 = XXH_vld1q_u64(xsecret + ((i+1) * 16)); + /* data_swap = swap(data_vec) */ + uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1); + uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1); + /* data_key = data_vec ^ key_vec; */ + uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1); + uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2); + + /* + * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a + * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to + * get one vector with the low 32 bits of each lane, and one vector + * with the high 32 bits of each lane. + * + * The intrinsic returns a double vector because the original ARMv7-a + * instruction modified both arguments in place. AArch64 and SIMD128 emit + * two instructions from this intrinsic. + * + * [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ] + * [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ] + */ + uint32x4x2_t unzipped = vuzpq_u32( + vreinterpretq_u32_u64(data_key_1), + vreinterpretq_u32_u64(data_key_2) + ); + /* data_key_lo = data_key & 0xFFFFFFFF */ + uint32x4_t data_key_lo = unzipped.val[0]; + /* data_key_hi = data_key >> 32 */ + uint32x4_t data_key_hi = unzipped.val[1]; + /* + * Then, we can split the vectors horizontally and multiply which, as for most + * widening intrinsics, have a variant that works on both high half vectors + * for free on AArch64. A similar instruction is available on SIMD128. + * + * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi + */ + uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi); + uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi); + /* + * Clang reorders + * a += b * c; // umlal swap.2d, dkl.2s, dkh.2s + * c += a; // add acc.2d, acc.2d, swap.2d + * to + * c += a; // add acc.2d, acc.2d, swap.2d + * c += b * c; // umlal acc.2d, dkl.2s, dkh.2s + * + * While it would make sense in theory since the addition is faster, + * for reasons likely related to umlal being limited to certain NEON + * pipelines, this is worse. A compiler guard fixes this. + */ + XXH_COMPILER_GUARD_CLANG_NEON(sum_1); + XXH_COMPILER_GUARD_CLANG_NEON(sum_2); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64(xacc[i], sum_1); + xacc[i+1] = vaddq_u64(xacc[i+1], sum_2); + } + /* Operate on the remaining NEON lanes 2 at a time. */ + for (; i < XXH3_NEON_LANES / 2; i++) { + /* data_vec = xinput[i]; */ + uint64x2_t data_vec = XXH_vld1q_u64(xinput + (i * 16)); + /* key_vec = xsecret[i]; */ + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + /* acc_vec_2 = swap(data_vec) */ + uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1); + /* data_key = data_vec ^ key_vec; */ + uint64x2_t data_key = veorq_u64(data_vec, key_vec); + /* For two lanes, just use VMOVN and VSHRN. */ + /* data_key_lo = data_key & 0xFFFFFFFF; */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* data_key_hi = data_key >> 32; */ + uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32); + /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */ + uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi); + /* Same Clang workaround as before */ + XXH_COMPILER_GUARD_CLANG_NEON(sum); + /* xacc[i] = acc_vec + sum; */ + xacc[i] = vaddq_u64 (xacc[i], sum); + } + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon) + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { xxh_aliasing_uint64x2_t* xacc = (xxh_aliasing_uint64x2_t*) acc; + uint8_t const* xsecret = (uint8_t const*) secret; + + size_t i; + /* WASM uses operator overloads and doesn't need these. */ +#ifndef __wasm_simd128__ + /* { prime32_1, prime32_1 } */ + uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1); + /* { 0, prime32_1, 0, prime32_1 } */ + uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32)); +#endif + + /* AArch64 uses both scalar and neon at the same time */ + for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } + for (i=0; i < XXH3_NEON_LANES / 2; i++) { + /* xacc[i] ^= (xacc[i] >> 47); */ + uint64x2_t acc_vec = xacc[i]; + uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); + uint64x2_t data_vec = veorq_u64(acc_vec, shifted); + + /* xacc[i] ^= xsecret[i]; */ + uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); + uint64x2_t data_key = veorq_u64(data_vec, key_vec); + /* xacc[i] *= XXH_PRIME32_1 */ +#ifdef __wasm_simd128__ + /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */ + xacc[i] = data_key * XXH_PRIME32_1; +#else + /* + * Expanded version with portable NEON intrinsics + * + * lo(x) * lo(y) + (hi(x) * lo(y) << 32) + * + * prod_hi = hi(data_key) * lo(prime) << 32 + * + * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector + * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits + * and avoid the shift. + */ + uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi); + /* Extract low bits for vmlal_u32 */ + uint32x2_t data_key_lo = vmovn_u64(data_key); + /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */ + xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo); +#endif + } + } +} +#endif + +#if (XXH_VECTOR == XXH_VSX) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + /* presumed aligned */ + xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + xxh_u8 const* const xinput = (xxh_u8 const*) input; /* no alignment restriction */ + xxh_u8 const* const xsecret = (xxh_u8 const*) secret; /* no alignment restriction */ + xxh_u64x2 const v32 = { 32, 32 }; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + /* data_vec = xinput[i]; */ + xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i); + /* key_vec = xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + /* shuffled = (data_key << 32) | (data_key >> 32); */ + xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); + /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ + xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); + /* acc_vec = xacc[i]; */ + xxh_u64x2 acc_vec = xacc[i]; + acc_vec += product; + + /* swap high and low halves */ +#ifdef __s390x__ + acc_vec += vec_permi(data_vec, data_vec, 2); +#else + acc_vec += vec_xxpermdi(data_vec, data_vec, 2); +#endif + xacc[i] = acc_vec; + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx) + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + + { xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; + const xxh_u8* const xsecret = (const xxh_u8*) secret; + /* constants */ + xxh_u64x2 const v32 = { 32, 32 }; + xxh_u64x2 const v47 = { 47, 47 }; + xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 }; + size_t i; + for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { + /* xacc[i] ^= (xacc[i] >> 47); */ + xxh_u64x2 const acc_vec = xacc[i]; + xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); + + /* xacc[i] ^= xsecret[i]; */ + xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); + xxh_u64x2 const data_key = data_vec ^ key_vec; + + /* xacc[i] *= XXH_PRIME32_1 */ + /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ + xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); + /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ + xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); + xacc[i] = prod_odd + (prod_even << v32); + } } +} + +#endif + +#if (XXH_VECTOR == XXH_SVE) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_sve( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc); + ACCRND(vacc, 0); + svst1_u64(mask, xacc, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } +} + +XXH_FORCE_INLINE void +XXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, + const xxh_u8* XXH_RESTRICT secret, + size_t nbStripes) +{ + if (nbStripes != 0) { + uint64_t *xacc = (uint64_t *)acc; + const uint64_t *xinput = (const uint64_t *)(const void *)input; + const uint64_t *xsecret = (const uint64_t *)(const void *)secret; + svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); + uint64_t element_count = svcntd(); + if (element_count >= 8) { + svbool_t mask = svptrue_pat_b64(SV_VL8); + svuint64_t vacc = svld1_u64(mask, xacc + 0); + do { + /* svprfd(svbool_t, void *, enum svfprop); */ + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(vacc, 0); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, vacc); + } else if (element_count == 2) { /* sve128 */ + svbool_t mask = svptrue_pat_b64(SV_VL2); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 2); + svuint64_t acc2 = svld1_u64(mask, xacc + 4); + svuint64_t acc3 = svld1_u64(mask, xacc + 6); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 2); + ACCRND(acc2, 4); + ACCRND(acc3, 6); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 2, acc1); + svst1_u64(mask, xacc + 4, acc2); + svst1_u64(mask, xacc + 6, acc3); + } else { + svbool_t mask = svptrue_pat_b64(SV_VL4); + svuint64_t acc0 = svld1_u64(mask, xacc + 0); + svuint64_t acc1 = svld1_u64(mask, xacc + 4); + do { + svprfd(mask, xinput + 128, SV_PLDL1STRM); + ACCRND(acc0, 0); + ACCRND(acc1, 4); + xinput += 8; + xsecret += 1; + nbStripes--; + } while (nbStripes != 0); + + svst1_u64(mask, xacc + 0, acc0); + svst1_u64(mask, xacc + 4, acc1); + } + } +} + +#endif + +#if (XXH_VECTOR == XXH_LSX) +#define _LSX_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w)) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_lsx( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + __m128i* const xacc = (__m128i *) acc; + const __m128i* const xinput = (const __m128i *) input; + const __m128i* const xsecret = (const __m128i *) secret; + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* data_vec = xinput[i]; */ + __m128i const data_vec = __lsx_vld(xinput + i, 0); + /* key_vec = xsecret[i]; */ + __m128i const key_vec = __lsx_vld(xsecret + i, 0); + /* data_key = data_vec ^ key_vec; */ + __m128i const data_key = __lsx_vxor_v(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32); + // __m128i const data_key_lo = __lsx_vsrli_d(data_key, 32); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m128i const product = __lsx_vmulwev_d_wu(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m128i const data_swap = __lsx_vshuf4i_w(data_vec, _LSX_SHUFFLE(1, 0, 3, 2)); + __m128i const sum = __lsx_vadd_d(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = __lsx_vadd_d(product, sum); + } + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(lsx) + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_lsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 15) == 0); + { + __m128i* const xacc = (__m128i*) acc; + const __m128i* const xsecret = (const __m128i *) secret; + const __m128i prime32 = __lsx_vreplgr2vr_d(XXH_PRIME32_1); + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m128i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m128i const acc_vec = xacc[i]; + __m128i const shifted = __lsx_vsrli_d(acc_vec, 47); + __m128i const data_vec = __lsx_vxor_v(acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m128i const key_vec = __lsx_vld(xsecret + i, 0); + __m128i const data_key = __lsx_vxor_v(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + xacc[i] = __lsx_vmul_d(data_key, prime32); + } + } +} + +#endif + +#if (XXH_VECTOR == XXH_LASX) +#define _LASX_SHUFFLE(z, y, x, w) (((z) << 6) | ((y) << 4) | ((x) << 2) | (w)) + +XXH_FORCE_INLINE void +XXH3_accumulate_512_lasx( void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { + __m256i* const xacc = (__m256i *) acc; + const __m256i* const xinput = (const __m256i *) input; + const __m256i* const xsecret = (const __m256i *) secret; + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { + /* data_vec = xinput[i]; */ + __m256i const data_vec = __lasx_xvld(xinput + i, 0); + /* key_vec = xsecret[i]; */ + __m256i const key_vec = __lasx_xvld(xsecret + i, 0); + /* data_key = data_vec ^ key_vec; */ + __m256i const data_key = __lasx_xvxor_v(data_vec, key_vec); + /* data_key_lo = data_key >> 32; */ + __m256i const data_key_lo = __lasx_xvsrli_d(data_key, 32); + // __m256i const data_key_lo = __lasx_xvsrli_d(data_key, 32); + /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ + __m256i const product = __lasx_xvmulwev_d_wu(data_key, data_key_lo); + /* xacc[i] += swap(data_vec); */ + __m256i const data_swap = __lasx_xvshuf4i_w(data_vec, _LASX_SHUFFLE(1, 0, 3, 2)); + __m256i const sum = __lasx_xvadd_d(xacc[i], data_swap); + /* xacc[i] += product; */ + xacc[i] = __lasx_xvadd_d(product, sum); + } + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(lasx) + +XXH_FORCE_INLINE void +XXH3_scrambleAcc_lasx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + XXH_ASSERT((((size_t)acc) & 31) == 0); + { + __m256i* const xacc = (__m256i*) acc; + const __m256i* const xsecret = (const __m256i *) secret; + const __m256i prime32 = __lasx_xvreplgr2vr_d(XXH_PRIME32_1); + + for (size_t i = 0; i < XXH_STRIPE_LEN / sizeof(__m256i); i++) { + /* xacc[i] ^= (xacc[i] >> 47) */ + __m256i const acc_vec = xacc[i]; + __m256i const shifted = __lasx_xvsrli_d(acc_vec, 47); + __m256i const data_vec = __lasx_xvxor_v(acc_vec, shifted); + /* xacc[i] ^= xsecret[i]; */ + __m256i const key_vec = __lasx_xvld(xsecret + i, 0); + __m256i const data_key = __lasx_xvxor_v(data_vec, key_vec); + + /* xacc[i] *= XXH_PRIME32_1; */ + xacc[i] = __lasx_xvmul_d(data_key, prime32); + } + } +} + +#endif + +/* scalar variants - universal */ + +#if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) +/* + * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they + * emit an excess mask and a full 64-bit multiply-add (MADD X-form). + * + * While this might not seem like much, as AArch64 is a 64-bit architecture, only + * big Cortex designs have a full 64-bit multiplier. + * + * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit + * multiplies expand to 2-3 multiplies in microcode. This has a major penalty + * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline. + * + * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does + * not have this penalty and does the mask automatically. + */ +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + xxh_u64 ret; + /* note: %x = 64-bit register, %w = 32-bit register */ + __asm__("umaddl %x0, %w1, %w2, %x3" : "=r" (ret) : "r" (lhs), "r" (rhs), "r" (acc)); + return ret; +} +#else +XXH_FORCE_INLINE xxh_u64 +XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) +{ + return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc; +} +#endif + +/*! + * @internal + * @brief Scalar round for @ref XXH3_accumulate_512_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT input, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* xacc = (xxh_u64*) acc; + xxh_u8 const* xinput = (xxh_u8 const*) input; + xxh_u8 const* xsecret = (xxh_u8 const*) secret; + XXH_ASSERT(lane < XXH_ACC_NB); + XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); + { + xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8); + xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); + xacc[lane ^ 1] += data_val; /* swap adjacent lanes */ + xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]); + } +} + +/*! + * @internal + * @brief Processes a 64 byte block of data using the scalar path. + */ +XXH_FORCE_INLINE void +XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, + const void* XXH_RESTRICT input, + const void* XXH_RESTRICT secret) +{ + size_t i; + /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */ +#if defined(__GNUC__) && !defined(__clang__) \ + && (defined(__arm__) || defined(__thumb2__)) \ + && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \ + && XXH_SIZE_OPT <= 0 +# pragma GCC unroll 8 +#endif + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarRound(acc, input, secret, i); + } +} +XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar) + +/*! + * @internal + * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar(). + * + * This is extracted to its own function because the NEON path uses a combination + * of NEON and scalar. + */ +XXH_FORCE_INLINE void +XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, + void const* XXH_RESTRICT secret, + size_t lane) +{ + xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ + const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ + XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); + XXH_ASSERT(lane < XXH_ACC_NB); + { + xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8); + xxh_u64 acc64 = xacc[lane]; + acc64 = XXH_xorshift64(acc64, 47); + acc64 ^= key64; + acc64 *= XXH_PRIME32_1; + xacc[lane] = acc64; + } +} + +/*! + * @internal + * @brief Scrambles the accumulators after a large chunk has been read + */ +XXH_FORCE_INLINE void +XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) +{ + size_t i; + for (i=0; i < XXH_ACC_NB; i++) { + XXH3_scalarScrambleRound(acc, secret, i); + } +} + +XXH_FORCE_INLINE void +XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) +{ + /* + * We need a separate pointer for the hack below, + * which requires a non-const pointer. + * Any decent compiler will optimize this out otherwise. + */ + const xxh_u8* kSecretPtr = XXH3_kSecret; + XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); + +#if defined(__GNUC__) && defined(__aarch64__) + /* + * UGLY HACK: + * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are + * placed sequentially, in order, at the top of the unrolled loop. + * + * While MOVK is great for generating constants (2 cycles for a 64-bit + * constant compared to 4 cycles for LDR), it fights for bandwidth with + * the arithmetic instructions. + * + * I L S + * MOVK + * MOVK + * MOVK + * MOVK + * ADD + * SUB STR + * STR + * By forcing loads from memory (as the asm line causes the compiler to assume + * that XXH3_kSecretPtr has been changed), the pipelines are used more + * efficiently: + * I L S + * LDR + * ADD LDR + * SUB STR + * STR + * + * See XXH3_NEON_LANES for details on the pipsline. + * + * XXH3_64bits_withSeed, len == 256, Snapdragon 835 + * without hack: 2654.4 MB/s + * with hack: 3202.9 MB/s + */ + XXH_COMPILER_GUARD(kSecretPtr); +#endif + { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + int i; + for (i=0; i < nbRounds; i++) { + /* + * The asm hack causes the compiler to assume that kSecretPtr aliases with + * customSecret, and on aarch64, this prevented LDP from merging two + * loads together for free. Putting the loads together before the stores + * properly generates LDP. + */ + xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64; + xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64; + XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo); + XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi); + } } +} + + +typedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t); +typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); +typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); + + +#if (XXH_VECTOR == XXH_AVX512) + +#define XXH3_accumulate_512 XXH3_accumulate_512_avx512 +#define XXH3_accumulate XXH3_accumulate_avx512 +#define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 +#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 + +#elif (XXH_VECTOR == XXH_AVX2) + +#define XXH3_accumulate_512 XXH3_accumulate_512_avx2 +#define XXH3_accumulate XXH3_accumulate_avx2 +#define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 +#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 + +#elif (XXH_VECTOR == XXH_SSE2) + +#define XXH3_accumulate_512 XXH3_accumulate_512_sse2 +#define XXH3_accumulate XXH3_accumulate_sse2 +#define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 +#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 + +#elif (XXH_VECTOR == XXH_NEON) + +#define XXH3_accumulate_512 XXH3_accumulate_512_neon +#define XXH3_accumulate XXH3_accumulate_neon +#define XXH3_scrambleAcc XXH3_scrambleAcc_neon +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_VSX) + +#define XXH3_accumulate_512 XXH3_accumulate_512_vsx +#define XXH3_accumulate XXH3_accumulate_vsx +#define XXH3_scrambleAcc XXH3_scrambleAcc_vsx +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_SVE) +#define XXH3_accumulate_512 XXH3_accumulate_512_sve +#define XXH3_accumulate XXH3_accumulate_sve +#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_LASX) +#define XXH3_accumulate_512 XXH3_accumulate_512_lasx +#define XXH3_accumulate XXH3_accumulate_lasx +#define XXH3_scrambleAcc XXH3_scrambleAcc_lasx +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#elif (XXH_VECTOR == XXH_LSX) +#define XXH3_accumulate_512 XXH3_accumulate_512_lsx +#define XXH3_accumulate XXH3_accumulate_lsx +#define XXH3_scrambleAcc XXH3_scrambleAcc_lsx +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#else /* scalar */ + +#define XXH3_accumulate_512 XXH3_accumulate_512_scalar +#define XXH3_accumulate XXH3_accumulate_scalar +#define XXH3_scrambleAcc XXH3_scrambleAcc_scalar +#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar + +#endif + +#if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */ +# undef XXH3_initCustomSecret +# define XXH3_initCustomSecret XXH3_initCustomSecret_scalar +#endif + +XXH_FORCE_INLINE void +XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, + const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble) +{ + size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; + size_t const nb_blocks = (len - 1) / block_len; + + size_t n; + + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + + for (n = 0; n < nb_blocks; n++) { + f_acc(acc, input + n*block_len, secret, nbStripesPerBlock); + f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); + } + + /* last partial block */ + XXH_ASSERT(len > XXH_STRIPE_LEN); + { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; + XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); + f_acc(acc, input + nb_blocks*block_len, secret, nbStripes); + + /* last stripe */ + { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; +#define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ + XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + } } +} + +XXH_FORCE_INLINE xxh_u64 +XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) +{ + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret+8) ); +} + +static XXH_PUREF XXH64_hash_t +XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) +{ + xxh_u64 result64 = start; + size_t i = 0; + + for (i = 0; i < 4; i++) { + result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i); +#if defined(__clang__) /* Clang */ \ + && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ + && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ + && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ + /* + * UGLY HACK: + * Prevent autovectorization on Clang ARMv7-a. Exact same problem as + * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. + * XXH3_64bits, len == 256, Snapdragon 835: + * without hack: 2063.7 MB/s + * with hack: 2560.7 MB/s + */ + XXH_COMPILER_GUARD(result64); +#endif + } + + return XXH3_avalanche(result64); +} + +/* do not align on 8, so that the secret is different from the accumulator */ +#define XXH_SECRET_MERGEACCS_START 11 + +static XXH_PUREF XXH64_hash_t +XXH3_finalizeLong_64b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 len) +{ + return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, len * XXH_PRIME64_1); +} + +#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ + XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, + const void* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_finalizeLong_64b(acc, (const xxh_u8*)secret, (xxh_u64)len); +} + +/* + * It's important for performance to transmit secret's size (when it's static) + * so that the compiler can properly optimize the vectorized loop. + * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. + */ +XXH3_WITH_SECRET_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; + return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc); +} + +/* + * It's preferable for performance that XXH3_hashLong is not inlined, + * as it results in a smaller function for small data, easier to the instruction cache. + * Note that inside this no_inline function, we do inline the internal loop, + * and provide a statically defined secret size to allow optimization of vector loop. + */ +XXH_NO_INLINE XXH_PUREF XXH64_hash_t +XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; (void)secret; (void)secretLen; + return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc); +} + +/* + * XXH3_hashLong_64b_withSeed(): + * Generate a custom key based on alteration of default XXH3_kSecret with the seed, + * and then use this key for long mode hashing. + * + * This operation is decently fast but nonetheless costs a little bit of time. + * Try to avoid it whenever possible (typically when seed==0). + * + * It's important for performance that XXH3_hashLong is not inlined. Not sure + * why (uop cache maybe?), but the difference is large and easily measurable. + */ +XXH_FORCE_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, + XXH64_hash_t seed, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) +{ +#if XXH_SIZE_OPT <= 0 + if (seed == 0) + return XXH3_hashLong_64b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc, f_scramble); +#endif + { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed); + return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), + f_acc, f_scramble); + } +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH64_hash_t +XXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) +{ + (void)secret; (void)secretLen; + return XXH3_hashLong_64b_withSeed_internal(input, len, seed, + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); +} + + +typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t, + XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH64_hash_t +XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong64_f f_hashLong) +{ + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secretLen` condition is not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + * Also, note that function signature doesn't offer room to return an error. + */ + if (len <= 16) + return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen); +} + + +/* === Public entry point === */ + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length) +{ + return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize) +{ + return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed) +{ + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); +} + +XXH_PUBLIC_API XXH64_hash_t +XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (length <= XXH3_MIDSIZE_MAX) + return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize); +} + + +/* === XXH3 streaming === */ +#ifndef XXH_NO_STREAM +/* + * Malloc's a pointer that is always aligned to @align. + * + * This must be freed with `XXH_alignedFree()`. + * + * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte + * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 + * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. + * + * This underalignment previously caused a rather obvious crash which went + * completely unnoticed due to XXH3_createState() not actually being tested. + * Credit to RedSpah for noticing this bug. + * + * The alignment is done manually: Functions like posix_memalign or _mm_malloc + * are avoided: To maintain portability, we would have to write a fallback + * like this anyways, and besides, testing for the existence of library + * functions without relying on external build tools is impossible. + * + * The method is simple: Overallocate, manually align, and store the offset + * to the original behind the returned pointer. + * + * Align must be a power of 2 and 8 <= align <= 128. + */ +static XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align) +{ + XXH_ASSERT(align <= 128 && align >= 8); /* range check */ + XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ + XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ + { /* Overallocate to make room for manual realignment and an offset byte */ + xxh_u8* base = (xxh_u8*)XXH_malloc(s + align); + if (base != NULL) { + /* + * Get the offset needed to align this pointer. + * + * Even if the returned pointer is aligned, there will always be + * at least one byte to store the offset to the original pointer. + */ + size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ + /* Add the offset for the now-aligned pointer */ + xxh_u8* ptr = base + offset; + + XXH_ASSERT((size_t)ptr % align == 0); + + /* Store the offset immediately before the returned pointer. */ + ptr[-1] = (xxh_u8)offset; + return ptr; + } + return NULL; + } +} +/* + * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass + * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. + */ +static void XXH_alignedFree(void* p) +{ + if (p != NULL) { + xxh_u8* ptr = (xxh_u8*)p; + /* Get the offset byte we added in XXH_malloc. */ + xxh_u8 offset = ptr[-1]; + /* Free the original malloc'd pointer */ + xxh_u8* base = ptr - offset; + XXH_free(base); + } +} +/*! @ingroup XXH3_family */ +/*! + * @brief Allocate an @ref XXH3_state_t. + * + * @return An allocated pointer of @ref XXH3_state_t on success. + * @return `NULL` on failure. + * + * @note Must be freed with XXH3_freeState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) +{ + XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); + if (state==NULL) return NULL; + XXH3_INITSTATE(state); + return state; +} + +/*! @ingroup XXH3_family */ +/*! + * @brief Frees an @ref XXH3_state_t. + * + * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). + * + * @return @ref XXH_OK. + * + * @note Must be allocated with XXH3_createState(). + * + * @see @ref streaming_example "Streaming Example" + */ +XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) +{ + XXH_alignedFree(statePtr); + return XXH_OK; +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API void +XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state) +{ + XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); +} + +static void +XXH3_reset_internal(XXH3_state_t* statePtr, + XXH64_hash_t seed, + const void* secret, size_t secretSize) +{ + size_t const initStart = offsetof(XXH3_state_t, bufferedSize); + size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; + XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); + XXH_ASSERT(statePtr != NULL); + /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ + XXH_memset((char*)statePtr + initStart, 0, initLength); + statePtr->acc[0] = XXH_PRIME32_3; + statePtr->acc[1] = XXH_PRIME64_1; + statePtr->acc[2] = XXH_PRIME64_2; + statePtr->acc[3] = XXH_PRIME64_3; + statePtr->acc[4] = XXH_PRIME64_4; + statePtr->acc[5] = XXH_PRIME32_2; + statePtr->acc[6] = XXH_PRIME64_5; + statePtr->acc[7] = XXH_PRIME32_1; + statePtr->seed = seed; + statePtr->useSeed = (seed != 0); + statePtr->extSecret = (const unsigned char*)secret; + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); + statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; + statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) +{ + if (statePtr == NULL) return XXH_ERROR; + XXH3_reset_internal(statePtr, 0, secret, secretSize); + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + return XXH_OK; +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + if (statePtr == NULL) return XXH_ERROR; + if (seed==0) return XXH3_64bits_reset(statePtr); + if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) + XXH3_initCustomSecret(statePtr->customSecret, seed); + XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); + return XXH_OK; +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64) +{ + if (statePtr == NULL) return XXH_ERROR; + if (secret == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; + XXH3_reset_internal(statePtr, seed64, secret, secretSize); + statePtr->useSeed = 1; /* always, even if seed64==0 */ + return XXH_OK; +} + +/*! + * @internal + * @brief Processes a large input for XXH3_update() and XXH3_digest_long(). + * + * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block. + * + * @param acc Pointer to the 8 accumulator lanes + * @param nbStripesSoFarPtr In/out pointer to the number of leftover stripes in the block* + * @param nbStripesPerBlock Number of stripes in a block + * @param input Input pointer + * @param nbStripes Number of stripes to process + * @param secret Secret pointer + * @param secretLimit Offset of the last block in @p secret + * @param f_acc Pointer to an XXH3_accumulate implementation + * @param f_scramble Pointer to an XXH3_scrambleAcc implementation + * @return Pointer past the end of @p input after processing + */ +XXH_FORCE_INLINE const xxh_u8 * +XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, + size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, + const xxh_u8* XXH_RESTRICT input, size_t nbStripes, + const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble) +{ + const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE; + /* Process full blocks */ + if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) { + /* Process the initial partial block... */ + size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr; + + do { + /* Accumulate and scramble */ + f_acc(acc, input, initialSecret, nbStripesThisIter); + f_scramble(acc, secret + secretLimit); + input += nbStripesThisIter * XXH_STRIPE_LEN; + nbStripes -= nbStripesThisIter; + /* Then continue the loop with the full block size */ + nbStripesThisIter = nbStripesPerBlock; + initialSecret = secret; + } while (nbStripes >= nbStripesPerBlock); + *nbStripesSoFarPtr = 0; + } + /* Process a partial block */ + if (nbStripes > 0) { + f_acc(acc, input, initialSecret, nbStripes); + input += nbStripes * XXH_STRIPE_LEN; + *nbStripesSoFarPtr += nbStripes; + } + /* Return end pointer */ + return input; +} + +#ifndef XXH3_STREAM_USE_STACK +# if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */ +# define XXH3_STREAM_USE_STACK 1 +# endif +#endif +/* This function accepts f_acc and f_scramble as function pointers, + * making it possible to implement multiple variants with different acc & scramble stages. + * This is notably useful to implement multiple vector variants with different intrinsics. + */ +XXH_FORCE_INLINE XXH_errorcode +XXH3_update(XXH3_state_t* XXH_RESTRICT const state, + const xxh_u8* XXH_RESTRICT input, size_t len, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble) +{ + if (input==NULL) { + XXH_ASSERT(len == 0); + return XXH_OK; + } + + XXH_ASSERT(state != NULL); + { const xxh_u8* const bEnd = input + len; + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* For some reason, gcc and MSVC seem to suffer greatly + * when operating accumulators directly into state. + * Operating into stack space seems to enable proper optimization. + * clang, on the other hand, doesn't seem to need this trick */ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; + XXH_memcpy(acc, state->acc, sizeof(acc)); +#else + xxh_u64* XXH_RESTRICT const acc = state->acc; +#endif + state->totalLen += len; + XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); + + /* small input : just fill in tmp buffer */ + if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) { + XXH_memcpy(state->buffer + state->bufferedSize, input, len); + state->bufferedSize += (XXH32_hash_t)len; + return XXH_OK; + } + + /* total input is now > XXH3_INTERNALBUFFER_SIZE */ + #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) + XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ + + /* + * Internal buffer is partially filled (always, except at beginning) + * Complete it, then consume it. + */ + if (state->bufferedSize) { + size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; + XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); + input += loadSize; + XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, XXH3_INTERNALBUFFER_STRIPES, + secret, state->secretLimit, + f_acc, f_scramble); + state->bufferedSize = 0; + } + XXH_ASSERT(input < bEnd); + if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { + size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; + input = XXH3_consumeStripes(acc, + &state->nbStripesSoFar, state->nbStripesPerBlock, + input, nbStripes, + secret, state->secretLimit, + f_acc, f_scramble); + XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); + + } + /* Some remaining input (always) : buffer it */ + XXH_ASSERT(input < bEnd); + XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); + XXH_ASSERT(state->bufferedSize == 0); + XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); + state->bufferedSize = (XXH32_hash_t)(bEnd-input); +#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 + /* save stack accumulators into state */ + XXH_memcpy(state->acc, acc, sizeof(acc)); +#endif + } + + return XXH_OK; +} + +/* + * Both XXH3_64bits_update and XXH3_128bits_update use this routine. + */ +XXH_NO_INLINE XXH_errorcode +XXH3_update_regular(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) +{ + return XXH3_update(state, (const xxh_u8*)input, len, + XXH3_accumulate, XXH3_scrambleAcc); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) +{ + return XXH3_update_regular(state, input, len); +} + + +XXH_FORCE_INLINE void +XXH3_digest_long (XXH64_hash_t* acc, + const XXH3_state_t* state, + const unsigned char* secret) +{ + xxh_u8 lastStripe[XXH_STRIPE_LEN]; + const xxh_u8* lastStripePtr; + + /* + * Digest on a local copy. This way, the state remains unaltered, and it can + * continue ingesting more input afterwards. + */ + XXH_memcpy(acc, state->acc, sizeof(state->acc)); + if (state->bufferedSize >= XXH_STRIPE_LEN) { + /* Consume remaining stripes then point to remaining data in buffer */ + size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; + size_t nbStripesSoFar = state->nbStripesSoFar; + XXH3_consumeStripes(acc, + &nbStripesSoFar, state->nbStripesPerBlock, + state->buffer, nbStripes, + secret, state->secretLimit, + XXH3_accumulate, XXH3_scrambleAcc); + lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN; + } else { /* bufferedSize < XXH_STRIPE_LEN */ + /* Copy to temp buffer */ + size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; + XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ + XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); + XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); + lastStripePtr = lastStripe; + } + /* Last stripe */ + XXH3_accumulate_512(acc, + lastStripePtr, + secret + state->secretLimit - XXH_SECRET_LASTACC_START); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state) +{ + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + return XXH3_finalizeLong_64b(acc, secret, (xxh_u64)state->totalLen); + } + /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ + if (state->useSeed) + return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); +} +#endif /* !XXH_NO_STREAM */ + + +/* ========================================== + * XXH3 128 bits (a.k.a XXH128) + * ========================================== + * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant, + * even without counting the significantly larger output size. + * + * For example, extra steps are taken to avoid the seed-dependent collisions + * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). + * + * This strength naturally comes at the cost of some speed, especially on short + * lengths. Note that longer hashes are about as fast as the 64-bit version + * due to it using only a slight modification of the 64-bit loop. + * + * XXH128 is also more oriented towards 64-bit machines. It is still extremely + * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). + */ + +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + /* A doubled version of 1to3_64b with different constants. */ + XXH_ASSERT(input != NULL); + XXH_ASSERT(1 <= len && len <= 3); + XXH_ASSERT(secret != NULL); + /* + * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } + * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } + * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } + */ + { xxh_u8 const c1 = input[0]; + xxh_u8 const c2 = input[len >> 1]; + xxh_u8 const c3 = input[len - 1]; + xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24) + | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); + xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); + xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; + xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed; + xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; + xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; + XXH128_hash_t h128; + h128.low64 = XXH64_avalanche(keyed_lo); + h128.high64 = XXH64_avalanche(keyed_hi); + return h128; + } +} + +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(4 <= len && len <= 8); + seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; + { xxh_u32 const input_lo = XXH_readLE32(input); + xxh_u32 const input_hi = XXH_readLE32(input + len - 4); + xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); + xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed; + xxh_u64 const keyed = input_64 ^ bitflip; + + /* Shift len to the left to ensure it is even, this avoids even multiplies. */ + XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); + + m128.high64 += (m128.low64 << 1); + m128.low64 ^= (m128.high64 >> 3); + + m128.low64 = XXH_xorshift64(m128.low64, 35); + m128.low64 *= PRIME_MX2; + m128.low64 = XXH_xorshift64(m128.low64, 28); + m128.high64 = XXH3_avalanche(m128.high64); + return m128; + } +} + +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(input != NULL); + XXH_ASSERT(secret != NULL); + XXH_ASSERT(9 <= len && len <= 16); + { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed; + xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed; + xxh_u64 const input_lo = XXH_readLE64(input); + xxh_u64 input_hi = XXH_readLE64(input + len - 8); + XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); + /* + * Put len in the middle of m128 to ensure that the length gets mixed to + * both the low and high bits in the 128x64 multiply below. + */ + m128.low64 += (xxh_u64)(len - 1) << 54; + input_hi ^= bitfliph; + /* + * Add the high 32 bits of input_hi to the high 32 bits of m128, then + * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to + * the high 64 bits of m128. + * + * The best approach to this operation is different on 32-bit and 64-bit. + */ + if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ + /* + * 32-bit optimized version, which is more readable. + * + * On 32-bit, it removes an ADC and delays a dependency between the two + * halves of m128.high64, but it generates an extra mask on 64-bit. + */ + m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); + } else { + /* + * 64-bit optimized (albeit more confusing) version. + * + * Uses some properties of addition and multiplication to remove the mask: + * + * Let: + * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) + * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) + * c = XXH_PRIME32_2 + * + * a + (b * c) + * Inverse Property: x + y - x == y + * a + (b * (1 + c - 1)) + * Distributive Property: x * (y + z) == (x * y) + (x * z) + * a + (b * 1) + (b * (c - 1)) + * Identity Property: x * 1 == x + * a + b + (b * (c - 1)) + * + * Substitute a, b, and c: + * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + * + * Since input_hi.hi + input_hi.lo == input_hi, we get this: + * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) + */ + m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); + } + /* m128 ^= XXH_swap64(m128 >> 64); */ + m128.low64 ^= XXH_swap64(m128.high64); + + { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ + XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); + h128.high64 += m128.high64 * XXH_PRIME64_2; + + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + } } +} + +/* + * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN + */ +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) +{ + XXH_ASSERT(len <= 16); + { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len) return XXH3_len_1to3_128b(input, len, secret, seed); + { XXH128_hash_t h128; + xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72); + xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88); + h128.low64 = XXH64_avalanche(seed ^ bitflipl); + h128.high64 = XXH64_avalanche( seed ^ bitfliph); + return h128; + } } +} + +/* + * A bit slower than XXH3_mix16B, but handles multiply by zero better. + */ +XXH_FORCE_INLINE XXH128_hash_t +XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, + const xxh_u8* secret, XXH64_hash_t seed) +{ + acc.low64 += XXH3_mix16B (input_1, secret+0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B (input_2, secret+16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; +} + + +XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(16 < len && len <= 128); + + { XXH128_hash_t acc; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + +#if XXH_SIZE_OPT >= 1 + { + /* Smaller, but slightly slower. */ + unsigned int i = (unsigned int)(len - 1) / 32; + do { + acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed); + } while (i-- != 0); + } +#else + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); + } + acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); + } + acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); + } + acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); +#endif + { XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + } + } +} + +XXH_NO_INLINE XXH_PUREF XXH128_hash_t +XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH64_hash_t seed) +{ + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; + XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); + + { XXH128_hash_t acc; + unsigned i; + acc.low64 = len * XXH_PRIME64_1; + acc.high64 = 0; + /* + * We set as `i` as offset + 32. We do this so that unchanged + * `len` can be used as upper bound. This reaches a sweet spot + * where both x86 and aarch64 get simple agen and good codegen + * for the loop. + */ + for (i = 32; i < 160; i += 32) { + acc = XXH128_mix32B(acc, + input + i - 32, + input + i - 16, + secret + i - 32, + seed); + } + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + /* + * NB: `i <= len` will duplicate the last 32-bytes if + * len % 32 was zero. This is an unfortunate necessity to keep + * the hash result stable. + */ + for (i=160; i <= len; i += 32) { + acc = XXH128_mix32B(acc, + input + i - 32, + input + i - 16, + secret + XXH3_MIDSIZE_STARTOFFSET + i - 160, + seed); + } + /* last bytes */ + acc = XXH128_mix32B(acc, + input + len - 16, + input + len - 32, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, + (XXH64_hash_t)0 - seed); + + { XXH128_hash_t h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + ((len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); + return h128; + } + } +} + +static XXH_PUREF XXH128_hash_t +XXH3_finalizeLong_128b(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, xxh_u64 len) +{ + XXH128_hash_t h128; + h128.low64 = XXH3_finalizeLong_64b(acc, secret, len); + h128.high64 = XXH3_mergeAccs(acc, secret + secretSize + - XXH_STRIPE_LEN - XXH_SECRET_MERGEACCS_START, + ~(len * XXH_PRIME64_2)); + return h128; +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, + const xxh_u8* XXH_RESTRICT secret, size_t secretSize, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble) +{ + XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; + + XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble); + + /* converge into final hash */ + XXH_STATIC_ASSERT(sizeof(acc) == 64); + XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_finalizeLong_128b(acc, secret, secretSize, (xxh_u64)len); +} + +/* + * It's important for performance that XXH3_hashLong() is not inlined. + */ +XXH_NO_INLINE XXH_PUREF XXH128_hash_t +XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; (void)secret; (void)secretLen; + return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_accumulate, XXH3_scrambleAcc); +} + +/* + * It's important for performance to pass @p secretLen (when it's static) + * to the compiler, so that it can properly optimize the vectorized loop. + * + * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE + * breaks -Og, this is XXH_NO_INLINE. + */ +XXH3_WITH_SECRET_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)seed64; + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, + XXH3_accumulate, XXH3_scrambleAcc); +} + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, + XXH64_hash_t seed64, + XXH3_f_accumulate f_acc, + XXH3_f_scrambleAcc f_scramble, + XXH3_f_initCustomSecret f_initSec) +{ + if (seed64 == 0) + return XXH3_hashLong_128b_internal(input, len, + XXH3_kSecret, sizeof(XXH3_kSecret), + f_acc, f_scramble); + { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; + f_initSec(secret, seed64); + return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), + f_acc, f_scramble); + } +} + +/* + * It's important for performance that XXH3_hashLong is not inlined. + */ +XXH_NO_INLINE XXH128_hash_t +XXH3_hashLong_128b_withSeed(const void* input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) +{ + (void)secret; (void)secretLen; + return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, + XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); +} + +typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, + XXH64_hash_t, const void* XXH_RESTRICT, size_t); + +XXH_FORCE_INLINE XXH128_hash_t +XXH3_128bits_internal(const void* input, size_t len, + XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, + XXH3_hashLong128_f f_hl128) +{ + XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); + /* + * If an action is to be taken if `secret` conditions are not respected, + * it should be done here. + * For now, it's a contract pre-condition. + * Adding a check and a branch here would cost performance at every hash. + */ + if (len <= 16) + return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); + if (len <= 128) + return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); + return f_hl128(input, len, seed64, secret, secretLen); +} + + +/* === Public XXH128 API === */ + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len) +{ + return XXH3_128bits_internal(input, len, 0, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_default); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize) +{ + return XXH3_128bits_internal(input, len, 0, + (const xxh_u8*)secret, secretSize, + XXH3_hashLong_128b_withSecret); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_internal(input, len, seed, + XXH3_kSecret, sizeof(XXH3_kSecret), + XXH3_hashLong_128b_withSeed); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); + return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) +{ + return XXH3_128bits_withSeed(input, len, seed); +} + + +/* === XXH3 128-bit streaming === */ +#ifndef XXH_NO_STREAM +/* + * All initialization and update functions are identical to 64-bit streaming variant. + * The only difference is the finalization routine. + */ + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) +{ + return XXH3_64bits_reset(statePtr); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) +{ + return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) +{ + return XXH3_64bits_reset_withSeed(statePtr, seed); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) +{ + return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) +{ + return XXH3_update_regular(state, input, len); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state) +{ + const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; + if (state->totalLen > XXH3_MIDSIZE_MAX) { + XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; + XXH3_digest_long(acc, state, secret); + XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); + return XXH3_finalizeLong_128b(acc, secret, state->secretLimit + XXH_STRIPE_LEN, (xxh_u64)state->totalLen); + } + /* len <= XXH3_MIDSIZE_MAX : short code */ + if (state->useSeed) + return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); + return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), + secret, state->secretLimit + XXH_STRIPE_LEN); +} +#endif /* !XXH_NO_STREAM */ +/* 128-bit utility functions */ + +/* return : 1 is equal, 0 if different */ +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) +{ + /* note : XXH128_hash_t is compact, it has no padding byte */ + return !(XXH_memcmp(&h1, &h2, sizeof(h1))); +} + +/* This prototype is compatible with stdlib's qsort(). + * @return : >0 if *h128_1 > *h128_2 + * <0 if *h128_1 < *h128_2 + * =0 if *h128_1 == *h128_2 */ +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2) +{ + XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; + XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; + int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); + /* note : bets that, in most cases, hash values are different */ + if (hcmp) return hcmp; + return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); +} + + +/*====== Canonical representation ======*/ +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API void +XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) { + hash.high64 = XXH_swap64(hash.high64); + hash.low64 = XXH_swap64(hash.low64); + } + XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); + XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH128_hash_t +XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src) +{ + XXH128_hash_t h; + h.high64 = XXH_readBE64(src); + h.low64 = XXH_readBE64(src->digest + 8); + return h; +} + + + +/* ========================================== + * Secret generators + * ========================================== + */ +#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) + +XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128) +{ + XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); + XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); +} + +/*! @ingroup XXH3_family */ +XXH_PUBLIC_API XXH_errorcode +XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize) +{ +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(secretBuffer != NULL); + XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); +#else + /* production mode, assert() are disabled */ + if (secretBuffer == NULL) return XXH_ERROR; + if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; +#endif + + if (customSeedSize == 0) { + customSeed = XXH3_kSecret; + customSeedSize = XXH_SECRET_DEFAULT_SIZE; + } +#if (XXH_DEBUGLEVEL >= 1) + XXH_ASSERT(customSeed != NULL); +#else + if (customSeed == NULL) return XXH_ERROR; +#endif + + /* Fill secretBuffer with a copy of customSeed - repeat as needed */ + { size_t pos = 0; + while (pos < secretSize) { + size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); + XXH_memcpy((char*)secretBuffer + pos, customSeed, toCopy); + pos += toCopy; + } } + + { size_t const nbSeg16 = secretSize / 16; + size_t n; + XXH128_canonical_t scrambler; + XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); + for (n=0; n Date: Wed, 27 May 2026 16:40:47 +0800 Subject: [PATCH 2/4] fix(build): update examples package targets --- docs/source/build_system.rst | 32 +++---- examples/CMakeLists.txt | 44 +++++++++ examples/clean_demo.cpp | 142 ++++++++++++++++++++++++++++ examples/read_write_demo.cpp | 174 +++++++++++++++++++++++++++++++++++ 4 files changed, 376 insertions(+), 16 deletions(-) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/clean_demo.cpp create mode 100644 examples/read_write_demo.cpp diff --git a/docs/source/build_system.rst b/docs/source/build_system.rst index 1f3f325..92fe926 100644 --- a/docs/source/build_system.rst +++ b/docs/source/build_system.rst @@ -52,10 +52,10 @@ for data format and file system. find_package(Paimon REQUIRED) add_executable(my_example my_example.cc) - target_link_libraries(my_example PRIVATE arrow_shared - paimon_shared - paimon_parquet_file_format_shared - paimon_local_file_system_shared) + target_link_libraries(my_example PRIVATE Arrow::arrow_shared + Paimon::paimon_shared + Paimon::paimon_parquet_file_format_shared + Paimon::paimon_local_file_system_shared) Available variables and targets ------------------------------- @@ -64,10 +64,10 @@ The directive ``find_package(Paimon REQUIRED)`` instructs CMake to locate a Paimon C++ installation on your system. If successful, it sets ``Paimon_FOUND`` to true if the Paimon C++ libraries were found. -It also defines the following linkable targets (plain strings, not variables): +It also defines the following linkable imported targets: -* ``paimon_shared`` links to the Paimon shared libraries -* ``paimon_static`` links to the Paimon static libraries +* ``Paimon::paimon_shared`` links to the Paimon shared libraries +* ``Paimon::paimon_static`` links to the Paimon static libraries In most cases, it is recommended to use the Paimon shared libraries. @@ -78,21 +78,21 @@ Paimon provides a set of built-in optional plugins that you can link to as neede - File format plugins: - - ``paimon_parquet_file_format_shared`` / ``paimon_parquet_file_format_static`` - - ``paimon_orc_file_format_shared`` / ``paimon_orc_file_format_static`` - - ``paimon_avro_file_format_shared`` / ``paimon_avro_file_format_static`` - - ``paimon_blob_file_format_shared`` / ``paimon_blob_file_format_static`` - - ``paimon_lance_file_format_shared`` / ``paimon_lance_file_format_static`` + - ``Paimon::paimon_parquet_file_format_shared`` / ``Paimon::paimon_parquet_file_format_static`` + - ``Paimon::paimon_orc_file_format_shared`` / ``Paimon::paimon_orc_file_format_static`` + - ``Paimon::paimon_avro_file_format_shared`` / ``Paimon::paimon_avro_file_format_static`` + - ``Paimon::paimon_blob_file_format_shared`` / ``Paimon::paimon_blob_file_format_static`` + - ``Paimon::paimon_lance_file_format_shared`` / ``Paimon::paimon_lance_file_format_static`` - File system plugins: - - ``paimon_local_file_system_shared`` / ``paimon_local_file_system_static`` - - ``paimon_jindo_file_system_shared`` / ``paimon_jindo_file_system_static`` + - ``Paimon::paimon_local_file_system_shared`` / ``Paimon::paimon_local_file_system_static`` + - ``Paimon::paimon_jindo_file_system_shared`` / ``Paimon::paimon_jindo_file_system_static`` - Index plugins: - - ``paimon_file_index_shared`` / ``paimon_file_index_static`` - - ``paimon_lumina_index_shared`` / ``paimon_lumina_index_static`` + - ``Paimon::paimon_file_index_shared`` / ``Paimon::paimon_file_index_static`` + - ``Paimon::paimon_lumina_index_shared`` / ``Paimon::paimon_lumina_index_static`` .. note:: diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..230c90f --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +cmake_minimum_required(VERSION 3.16) + +project(example) + +set(CMAKE_CXX_STANDARD 17) + +find_package(Arrow CONFIG REQUIRED) +find_package(Paimon CONFIG REQUIRED) + +add_executable(read_write_demo read_write_demo.cpp) +add_executable(clean_demo clean_demo.cpp) + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed ${CMAKE_EXE_LINKER_FLAGS}") + +target_link_libraries(read_write_demo + PRIVATE Arrow::arrow_shared + Paimon::paimon_shared + Paimon::paimon_parquet_file_format_shared + Paimon::paimon_orc_file_format_shared + Paimon::paimon_local_file_system_shared) + +target_link_libraries(clean_demo + PRIVATE Arrow::arrow_shared + Paimon::paimon_shared + Paimon::paimon_parquet_file_format_shared + Paimon::paimon_orc_file_format_shared + Paimon::paimon_local_file_system_shared) diff --git a/examples/clean_demo.cpp b/examples/clean_demo.cpp new file mode 100644 index 0000000..d98b431 --- /dev/null +++ b/examples/clean_demo.cpp @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "arrow/api.h" +#include "arrow/c/bridge.h" +#include "arrow/ipc/api.h" +#include "paimon/api.h" +#include "paimon/catalog/catalog.h" +#include "paimon/orphan_files_cleaner.h" + +namespace fs = std::filesystem; +namespace paimon { +Status CleanOrphanFiles(const std::string& table_path, int64_t older_than_ms) { + CleanContextBuilder clean_context_builder(table_path); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr clean_context, + clean_context_builder.WithOlderThanMs(older_than_ms).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr orphan_files_cleaner, + OrphanFilesCleaner::Create(std::move(clean_context))); + PAIMON_ASSIGN_OR_RAISE(std::set cleaned_paths, orphan_files_cleaner->Clean()); + + for (const auto& clean_file : cleaned_paths) { + std::cout << "clean_file_path : " << clean_file << std::endl; + } + + return Status::OK(); +} + +Status DropPartition(const std::string& table_path, + const std::vector>& partitions) { + CommitContextBuilder commit_context_builder(table_path, /*commit_user=*/"commit_user_1"); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr commit_context, + commit_context_builder.Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr committer, + FileStoreCommit::Create(std::move(commit_context))); + PAIMON_RETURN_NOT_OK(committer->DropPartition(partitions, /*commit_identifier=*/10)); + + return Status::OK(); +} + +Status ExpireSnapshot(const std::string& table_path) { + CommitContextBuilder commit_context_builder(table_path, /*commit_user=*/"commit_user_1"); + std::map commit_options = { + {Options::SNAPSHOT_NUM_RETAINED_MAX, "2"}, + {Options::SNAPSHOT_NUM_RETAINED_MIN, "1"}, + {Options::SNAPSHOT_TIME_RETAINED, "1ms"}, + {Options::SNAPSHOT_CLEAN_EMPTY_DIRECTORIES, "true"}}; + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr commit_context, + commit_context_builder.SetOptions(commit_options).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr committer, + FileStoreCommit::Create(std::move(commit_context))); + PAIMON_RETURN_NOT_OK(committer->Expire()); + + return Status::OK(); +} + +} // namespace paimon + +bool CopyToTempDirectory(const fs::path& src, const fs::path& dst) { + try { + if (!fs::exists(dst)) { + fs::create_directories(dst); + } + for (const auto& entry : fs::recursive_directory_iterator(src)) { + const auto& relativePath = fs::relative(entry.path(), src); + const auto& targetPath = dst / relativePath; + if (entry.is_directory()) { + fs::create_directories(targetPath); + } else { + fs::copy_file(entry.path(), targetPath, fs::copy_options::overwrite_existing); + } + } + } catch (const fs::filesystem_error& e) { + std::cerr << "filesystem error: " << e.what() << std::endl; + return false; + } + return true; +} + +int main(int argc, char* argv[]) { + if (argc != 4) { + std::cout << "Usage: " << argv[0] << " " + << std::endl; + return -1; + } + std::string origin_table_path = std::string(argv[1]); + std::string temp_table_path = std::string(argv[2]); + std::string clean_mode = std::string(argv[3]); + + if (!CopyToTempDirectory(origin_table_path, temp_table_path)) { + return -1; + } + + std::map clean_options; + paimon::Status status; + if (clean_mode == "orphan_file") { + std::cout << "enter the timestamp (ms) before which orphan files will be deleted" + << std::endl; + int64_t older_than_ms; + std::cin >> older_than_ms; + status = paimon::CleanOrphanFiles(temp_table_path, older_than_ms); + } else if (clean_mode == "drop_partition") { + std::cout << "enter partition key-value pairs to drop. type 'EOF EOF' to finish" + << std::endl; + std::string partition_key, value; + std::vector> partitions; + while (std::cin >> partition_key >> value) { + if (partition_key == "EOF" && value == "EOF") break; + partitions.push_back({{partition_key, value}}); + } + status = paimon::DropPartition(temp_table_path, partitions); + } else if (clean_mode == "expire_snapshot") { + status = paimon::ExpireSnapshot(temp_table_path); + } + + if (!status.ok()) { + std::cout << status.ToString() << std::endl; + return -1; + } + + return 0; +} diff --git a/examples/read_write_demo.cpp b/examples/read_write_demo.cpp new file mode 100644 index 0000000..cb2a4f9 --- /dev/null +++ b/examples/read_write_demo.cpp @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#include "arrow/api.h" +#include "arrow/c/bridge.h" +#include "arrow/ipc/api.h" +#include "paimon/api.h" +#include "paimon/catalog/catalog.h" + +arrow::Result> PrepareData(const arrow::FieldVector& fields) { + arrow::StringBuilder f0_builder; + arrow::Int32Builder f1_builder; + arrow::Int32Builder f2_builder; + arrow::DoubleBuilder f3_builder; + + std::vector> data = { + {"Alice", 1, 0, 11.0}, {"Bob", 1, 1, 12.1}, {"Cathy", 1, 2, 13.2}}; + + for (const auto& row : data) { + ARROW_RETURN_NOT_OK(f0_builder.Append(std::get<0>(row))); + ARROW_RETURN_NOT_OK(f1_builder.Append(std::get<1>(row))); + ARROW_RETURN_NOT_OK(f2_builder.Append(std::get<2>(row))); + ARROW_RETURN_NOT_OK(f3_builder.Append(std::get<3>(row))); + } + + std::shared_ptr f0_array, f1_array, f2_array, f3_array; + ARROW_RETURN_NOT_OK(f0_builder.Finish(&f0_array)); + ARROW_RETURN_NOT_OK(f1_builder.Finish(&f1_array)); + ARROW_RETURN_NOT_OK(f2_builder.Finish(&f2_array)); + ARROW_RETURN_NOT_OK(f3_builder.Finish(&f3_array)); + + std::vector> children = {f0_array, f1_array, f2_array, f3_array}; + auto struct_type = arrow::struct_(fields); + return std::make_shared(struct_type, f0_array->length(), children); +} + +paimon::Status Run(const std::string& root_path, const std::string& db_name, + const std::string& table_name) { + std::map options = {{paimon::Options::MANIFEST_FORMAT, "orc"}, + {paimon::Options::FILE_FORMAT, "parquet"}, + {paimon::Options::FILE_SYSTEM, "local"}}; + + // create table + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr catalog, + paimon::Catalog::Create(root_path, options)); + PAIMON_RETURN_NOT_OK(catalog->CreateDatabase(db_name, options, /*ignore_if_exists=*/false)); + arrow::FieldVector fields = { + arrow::field("f0", arrow::utf8()), + arrow::field("f1", arrow::int32()), + arrow::field("f2", arrow::int32()), + arrow::field("f3", arrow::float64()), + }; + std::shared_ptr schema = arrow::schema(fields); + ::ArrowSchema arrow_schema; + arrow::Status arrow_status = arrow::ExportSchema(*schema, &arrow_schema); + if (!arrow_status.ok()) { + return paimon::Status::Invalid(arrow_status.message()); + } + PAIMON_RETURN_NOT_OK(catalog->CreateTable(paimon::Identifier(db_name, table_name), + &arrow_schema, + /*partition_keys=*/{}, + /*primary_keys=*/{}, options, + /*ignore_if_exists=*/false)); + + std::string table_path = root_path + "/" + db_name + ".db/" + table_name; + + std::string commit_user = "some_commit_user"; + // write + paimon::WriteContextBuilder context_builder(table_path, commit_user); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr write_context, + context_builder.SetOptions(options).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr writer, + paimon::FileStoreWrite::Create(std::move(write_context))); + + // prepare data + auto struct_array = PrepareData(fields); + if (!struct_array.ok()) { + return paimon::Status::Invalid(struct_array.status().ToString()); + } + ::ArrowArray arrow_array; + arrow_status = arrow::ExportArray(*struct_array.ValueUnsafe(), &arrow_array); + if (!arrow_status.ok()) { + return paimon::Status::Invalid(arrow_status.message()); + } + paimon::RecordBatchBuilder batch_builder(&arrow_array); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr record_batch, + batch_builder.Finish()); + PAIMON_RETURN_NOT_OK(writer->Write(std::move(record_batch))); + PAIMON_ASSIGN_OR_RAISE(std::vector> commit_message, + writer->PrepareCommit()); + + // commit + paimon::CommitContextBuilder commit_context_builder(table_path, commit_user); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr commit_context, + commit_context_builder.SetOptions(options).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr committer, + paimon::FileStoreCommit::Create(std::move(commit_context))); + PAIMON_RETURN_NOT_OK(committer->Commit(commit_message)); + + // scan + paimon::ScanContextBuilder scan_context_builder(table_path); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr scan_context, + scan_context_builder.SetOptions(options).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr scanner, + paimon::TableScan::Create(std::move(scan_context))); + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr plan, scanner->CreatePlan()); + + // read + paimon::ReadContextBuilder read_context_builder(table_path); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr read_context, + read_context_builder.SetOptions(options).Finish()); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr table_read, + paimon::TableRead::Create(std::move(read_context))); + PAIMON_ASSIGN_OR_RAISE(std::unique_ptr batch_reader, + table_read->CreateReader(plan->Splits())); + arrow::ArrayVector result_array_vector; + while (true) { + PAIMON_ASSIGN_OR_RAISE(paimon::BatchReader::ReadBatch batch, batch_reader->NextBatch()); + if (paimon::BatchReader::IsEofBatch(batch)) { + break; + } + auto& [c_array, c_schema] = batch; + auto arrow_result = arrow::ImportArray(c_array.get(), c_schema.get()); + if (!arrow_result.ok()) { + return paimon::Status::Invalid(arrow_result.status().ToString()); + } + auto result_array = arrow_result.ValueUnsafe(); + result_array_vector.push_back(result_array); + } + auto chunk_result = arrow::ChunkedArray::Make(result_array_vector); + if (!chunk_result.ok()) { + return paimon::Status::Invalid(chunk_result.status().ToString()); + } + std::cout << chunk_result.ValueUnsafe()->ToString() << std::endl; + return paimon::Status::OK(); +} + +int main(int argc, char** argv) { + if (argc != 4) { + std::cout << "Usage: " << argv[0] << " " + << std::endl; + return -1; + } + const std::string root_path = argv[1]; + const std::string db_name = argv[2]; + const std::string table_name = argv[3]; + paimon::Status status = Run(root_path, db_name, table_name); + if (!status.ok()) { + std::cerr << "Failed to run example:" << status.ToString() << std::endl; + return -1; + } + return 0; +} From 8a42489a64b0396911f3cb36604fc4669778617a Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Wed, 27 May 2026 18:35:44 +0800 Subject: [PATCH 3/4] fix(build): defer root cmake migration --- CMakeLists.txt | 462 ------------------------------------- ci/scripts/build_paimon.sh | 14 +- 2 files changed, 7 insertions(+), 469 deletions(-) delete mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 9a7a7e2..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,462 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -cmake_minimum_required(VERSION 3.16) -message(STATUS "Building using CMake version: ${CMAKE_VERSION}") - -# https://cmake.org/cmake/help/latest/policy/CMP0135.html -# -# CMP0135 is for solving re-building and re-downloading. -# We don't have a real problem with the OLD behavior for now -# but we use the NEW behavior explicitly to suppress CMP0135 -# warnings. -if(POLICY CMP0135) - cmake_policy(SET CMP0135 NEW) -endif() - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE - Release - CACHE STRING "Choose the type of build.") -endif() - -project(paimon - VERSION 0.2.0 - DESCRIPTION "Paimon C++ Project") - -string(TOUPPER "${CMAKE_BUILD_TYPE}" UPPERCASE_BUILD_TYPE) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -option(PAIMON_BUILD_STATIC "Build static library" ON) -option(PAIMON_BUILD_SHARED "Build shared library" ON) -option(PAIMON_BUILD_TESTS "Build tests" OFF) -option(PAIMON_USE_ASAN "Use Address Sanitizer" OFF) -option(PAIMON_USE_UBSAN "Use Undefined Behavior Sanitizer" OFF) -option(PAIMON_USE_CXX11_ABI "Use C++11 ABI" ON) -option(PAIMON_ENABLE_AVRO "Whether to enable avro file format" ON) -option(PAIMON_ENABLE_ORC "Whether to enable orc file format" ON) -option(PAIMON_ENABLE_LANCE "Whether to enable lance file format" OFF) -option(PAIMON_ENABLE_JINDO "Whether to enable jindo file system" OFF) -option(PAIMON_ENABLE_LUMINA "Whether to enable lumina vector index" OFF) -option(PAIMON_ENABLE_LUCENE "Whether to enable lucene index" OFF) - -if(PAIMON_ENABLE_ORC) - add_definitions(-DPAIMON_ENABLE_ORC) -endif() -if(PAIMON_ENABLE_AVRO) - add_definitions(-DPAIMON_ENABLE_AVRO) -endif() -if(PAIMON_ENABLE_JINDO) - add_definitions(-DPAIMON_ENABLE_JINDO) -endif() -if(PAIMON_USE_CXX11_ABI) - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) -else() - # lance and lumina are provided in so file, cannot be recompiled with C++11 ABI off. - if(PAIMON_ENABLE_LANCE) - message(FATAL_ERROR "Lance cannot be enabled with C++11 ABI off") - endif() - if(PAIMON_ENABLE_LUMINA) - message(FATAL_ERROR "Lumina cannot be enabled with C++11 ABI off") - endif() - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) -endif() -if(PAIMON_ENABLE_LANCE) - add_definitions(-DPAIMON_ENABLE_LANCE) -endif() -if(PAIMON_ENABLE_LUMINA) - add_definitions(-DPAIMON_ENABLE_LUMINA) -endif() - -if(PAIMON_ENABLE_LUCENE) - add_definitions(-DPAIMON_ENABLE_LUCENE) -endif() - -add_definitions(-DSNAPPY_CODEC_AVAILABLE) -add_definitions(-DZSTD_CODEC_AVAILABLE) -add_definitions(-DRAPIDJSON_HAS_STDSTRING) - -set(CLANG_TIDY_BIN "clang-tidy") - -set(PAIMON_SOURCE_DIR ${PROJECT_SOURCE_DIR}) -set(PAIMON_BINARY_DIR ${PROJECT_BINARY_DIR}) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules") -set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build_support") - -include(GNUInstallDirs) -set(PAIMON_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Paimon") -include(DefineOptions) - -if(PAIMON_OPTIONAL_INSTALL) - # Don't make the "install" target depend on the "all" target - set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true) - set(INSTALL_IS_OPTIONAL OPTIONAL) -endif() - -if(PAIMON_EXTRA_ERROR_CONTEXT) - add_definitions(-DPAIMON_EXTRA_ERROR_CONTEXT) -endif() - -if(NOT PAIMON_VERBOSE_LINT) - set(PAIMON_LINT_QUIET "--quiet") -endif() - -set(LINT_EXCLUSIONS_FILE ${BUILD_SUPPORT_DIR}/lint_exclusions.txt) - -set(LINT_SOURCE_DIRS ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src - ${CMAKE_SOURCE_DIR}/test ${CMAKE_SOURCE_DIR}/examples) - -set(CLANG_TIDY_OPTIONS - ${PAIMON_LINT_QUIET} - --compile_commands - ${CMAKE_BINARY_DIR}/compile_commands.json - --exclude_globs - ${LINT_EXCLUSIONS_FILE}) - -if(PAIMON_LINT_GIT_DIFF_MODE) - list(JOIN LINT_SOURCE_DIRS "|" _lint_dir_union) - set(LINT_DIR_REGEX "^(${_lint_dir_union}\/?)") - - set(GIT_DIFF_FILE_PATH ${CMAKE_BINARY_DIR}/git_diff_files.txt) - add_custom_target(update_diff_files - # get git-diff file list - COMMAND git diff-index --name-only --diff-filter=d --cached - ${PAIMON_LINT_GIT_TARGET_COMMIT} > ${GIT_DIFF_FILE_PATH} - # convert to absolute path - COMMAND sed -i \"s|^|${CMAKE_SOURCE_DIR}/|\" ${GIT_DIFF_FILE_PATH} - # filter with ${LINT_SOURCE_DIRS} dirs - COMMAND sed -nE -i '\\@${LINT_DIR_REGEX}@p' ${GIT_DIFF_FILE_PATH} - COMMENT "Generate git_diff_files.txt for git-diff lint" - BYPRODUCTS ${GIT_DIFF_FILE_PATH}) - - list(APPEND CLANG_TIDY_OPTIONS --source_file @${GIT_DIFF_FILE_PATH}) -else() - list(APPEND CLANG_TIDY_OPTIONS --source_dir ${LINT_SOURCE_DIRS}) -endif() - -# runs clang-tidy and attempts to fix any warning automatically -add_custom_target(clang-tidy - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_tidy.py - --clang_tidy_binary - ${CLANG_TIDY_BIN} - ${CLANG_TIDY_OPTIONS} - --fix) - -# runs clang-tidy and exits with a non-zero exit code if any errors are found. -add_custom_target(check-clang-tidy - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_tidy.py - --clang_tidy_binary - ${CLANG_TIDY_BIN} - ${CLANG_TIDY_OPTIONS}) - -if(PAIMON_LINT_GIT_DIFF_MODE) - add_dependencies(clang-tidy update_diff_files) - add_dependencies(check-clang-tidy update_diff_files) -endif() - -if(UNIX) - add_custom_target(iwyu - ${CMAKE_COMMAND} - -E - env - "PYTHON=${PYTHON_EXECUTABLE}" - ${BUILD_SUPPORT_DIR}/iwyu/iwyu.sh) - add_custom_target(iwyu-all - ${CMAKE_COMMAND} - -E - env - "PYTHON=${PYTHON_EXECUTABLE}" - ${BUILD_SUPPORT_DIR}/iwyu/iwyu.sh - all) -endif(UNIX) - -# Setup ccache to accelerate compilation if available -if(PAIMON_USE_CCACHE - AND NOT CMAKE_C_COMPILER_LAUNCHER - AND NOT CMAKE_CXX_COMPILER_LAUNCHER) - - find_program(CCACHE_PROGRAM ccache) - if(CCACHE_PROGRAM) - message(STATUS "Found ccache: ${CCACHE_PROGRAM}") - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - else() - message(STATUS "ccache not found, compiling without cache acceleration") - endif() -endif() - -include(SetupCxxFlags) - -# set compile output directory -string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_SUBDIR_NAME) - -# If build in-source, create the latest symlink. If build out-of-source, which -# is preferred, simply output the binaries in the build folder -if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) - set(BUILD_OUTPUT_ROOT_DIRECTORY - "${CMAKE_CURRENT_BINARY_DIR}/build/${BUILD_SUBDIR_NAME}/") - # Link build/latest to the current build directory, to avoid developers - # accidentally running the latest debug build when in fact they're building - # release builds. - file(MAKE_DIRECTORY ${BUILD_OUTPUT_ROOT_DIRECTORY}) - if(NOT APPLE) - set(MORE_ARGS "-T") - endif() - execute_process(COMMAND ln ${MORE_ARGS} -sf ${BUILD_OUTPUT_ROOT_DIRECTORY} - ${CMAKE_CURRENT_BINARY_DIR}/build/latest) -else() - set(BUILD_OUTPUT_ROOT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${BUILD_SUBDIR_NAME}/") -endif() - -# where to put generated archives (.a files) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") -set(ARCHIVE_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") - -# where to put generated libraries (.so files) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") -set(LIBRARY_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") - -# where to put generated binaries -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") -set(RUNTIME_OUTPUT_DIRECTORY "${BUILD_OUTPUT_ROOT_DIRECTORY}") -set(EXECUTABLE_OUTPUT_PATH "${BUILD_OUTPUT_ROOT_DIRECTORY}") - -include(BuildUtils) -enable_testing() - -# Add common flags -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_COMMON_FLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PAIMON_CXXFLAGS}") - -# For any C code, use the same flags. These flags don't contain C++ specific -# flags. -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CXX_COMMON_FLAGS} ${PAIMON_CXXFLAGS}") - -# Remove --std=c++17 to avoid errors from C compilers -string(REPLACE "-std=c++17" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) - -# Add C++-only flags, like -std=c++11 -set(CMAKE_CXX_FLAGS "${CXX_ONLY_FLAGS} ${CMAKE_CXX_FLAGS}") - -include(san-config) - -# Code coverage -if("${PAIMON_GENERATE_COVERAGE}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage -DCOVERAGE_BUILD") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage -DCOVERAGE_BUILD") -endif() - -# CMAKE_CXX_FLAGS now fully assembled -message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") -message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") - -include_directories(${CMAKE_CURRENT_BINARY_DIR}/src) -include_directories(src) - -# -# Visibility -# -# For generate_export_header() and add_compiler_export_flags(). -include(GenerateExportHeader) -include(ExternalProject) -include(ThirdpartyToolchain) - -add_custom_target(paimon_dependencies) - -if(PAIMON_STATIC_LINK_LIBS) - add_dependencies(paimon_dependencies ${PAIMON_STATIC_LINK_LIBS}) -endif() - -set(PAIMON_SHARED_PRIVATE_LINK_LIBS ${PAIMON_STATIC_LINK_LIBS}) - -add_subdirectory(third_party/roaring_bitmap EXCLUDE_FROM_ALL) -add_subdirectory(third_party/xxhash EXCLUDE_FROM_ALL) - -if(PAIMON_ENABLE_LANCE) - add_subdirectory(third_party/lance EXCLUDE_FROM_ALL) - link_directories(third_party/lance/lance_lib/target/release) - install(FILES ${PROJECT_SOURCE_DIR}/third_party/lance/lance_lib/target/release/liblance_lib_rc.so - DESTINATION ${CMAKE_INSTALL_LIBDIR}) -endif() - -list(APPEND PAIMON_LINK_LIBS ${CMAKE_DL_LIBS}) -list(APPEND PAIMON_SHARED_INSTALL_INTERFACE_LIBS ${CMAKE_DL_LIBS}) -if(PAIMON_ENABLE_LUMINA) - add_subdirectory(third_party/lumina EXCLUDE_FROM_ALL) - link_directories(third_party/lumina/lib) - install(FILES ${PROJECT_SOURCE_DIR}/third_party/lumina/lib/liblumina.so - DESTINATION ${CMAKE_INSTALL_LIBDIR}) -endif() - -if(PAIMON_ENABLE_LUCENE) - set(PAIMON_DICT_DEST "share/paimon/dict") - - install(DIRECTORY ${JIEBA_DICT_DIR}/ - DESTINATION ${PAIMON_DICT_DEST} - FILES_MATCHING - PATTERN "jieba.dict.utf8" - PATTERN "hmm_model.utf8" - PATTERN "idf.utf8" - PATTERN "stop_words.utf8" - PATTERN "user.dict.utf8" - PATTERN "pos_dict" - PATTERN ".git*" EXCLUDE - PATTERN "*.md" EXCLUDE) -endif() - -install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ - DESTINATION "include" - FILES_MATCHING - PATTERN "*.h") - -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_C_VISIBILITY_PRESET hidden) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories("${CMAKE_SOURCE_DIR}/third_party/roaring_bitmap") -include_directories("${CMAKE_SOURCE_DIR}/third_party/xxhash") - -if(PAIMON_ENABLE_LANCE) - include_directories("${CMAKE_SOURCE_DIR}/third_party/lance") -endif() - -if(PAIMON_ENABLE_LUMINA) - include_directories("${CMAKE_SOURCE_DIR}/third_party/lumina/include") -endif() - -include_directories(SYSTEM ${ARROW_INCLUDE_DIR}) -include_directories(SYSTEM ${TBB_INCLUDE_DIR}) - -include_directories(SYSTEM ${GLOG_INCLUDE_DIR}) -add_compile_definitions("GLOG_USE_GLOG_EXPORT") - -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) -set(PAIMON_VERSION_SCRIPT_FLAGS - "-Wl,--version-script=${CMAKE_SOURCE_DIR}/src/paimon/symbols.map") - -set(ENV{PAIMON_TEST_DATA} "${CMAKE_SOURCE_DIR}/test/test_data") - -if(PAIMON_BUILD_TESTS) - if(NOT PAIMON_ENABLE_ORC) - message(FATAL_ERROR "PAIMON_ENABLE_ORC must be enabled if PAIMON_BUILD_TESTS is enable" - ) - endif() - if(NOT PAIMON_ENABLE_AVRO) - message(FATAL_ERROR "PAIMON_ENABLE_AVRO must be enabled if PAIMON_BUILD_TESTS is enable" - ) - endif() - # Adding unit tests part of the "paimon" portion of the test suite - add_custom_target(paimon-tests) - resolve_dependency(GTest) - - add_custom_target(unittest - ctest - -j4 - -L - unittest - --output-on-failure) - add_dependencies(unittest paimon-tests) - - include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) - include_directories("${CMAKE_SOURCE_DIR}/test/") - - set(TEST_STATIC_LINK_LIBS - "-Wl,--whole-archive" - paimon_file_index_static - paimon_global_index_static - paimon_local_file_system_static - paimon_mock_file_format_static - "-Wl,--no-whole-archive" - "-Wl,--no-as-needed" - paimon_parquet_file_format_shared - paimon_blob_file_format_shared - "-Wl,--as-needed") - - if(PAIMON_ENABLE_LANCE) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_lance_file_format_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() - if(PAIMON_ENABLE_ORC) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_orc_file_format_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() - if(PAIMON_ENABLE_AVRO) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_avro_file_format_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() - if(PAIMON_ENABLE_JINDO) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_jindo_file_system_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() - if(PAIMON_ENABLE_LUMINA) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_lumina_index_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() - if(PAIMON_ENABLE_LUCENE) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed") - list(APPEND TEST_STATIC_LINK_LIBS paimon_lucene_index_shared) - list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed") - endif() -endif() - -paimon_print_dependency_resolution_summary() - -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion) - -configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/PaimonConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake" - INSTALL_DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfigVersion.cmake" - DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) - -config_summary_message() - -add_subdirectory(src/paimon) -add_subdirectory(src/paimon/fs/local) -add_subdirectory(src/paimon/fs/jindo) -add_subdirectory(src/paimon/format/blob) -add_subdirectory(src/paimon/format/orc) -add_subdirectory(src/paimon/format/parquet) -add_subdirectory(src/paimon/format/avro) -add_subdirectory(src/paimon/format/lance) -add_subdirectory(src/paimon/global_index/lumina) -add_subdirectory(src/paimon/global_index/lucene) -add_subdirectory(src/paimon/testing/mock) -add_subdirectory(src/paimon/testing/utils) -add_subdirectory(test/inte) - -install(EXPORT PaimonTargets - NAMESPACE Paimon:: - DESTINATION ${PAIMON_CMAKE_INSTALL_DIR}) diff --git a/ci/scripts/build_paimon.sh b/ci/scripts/build_paimon.sh index c412689..b64bd8d 100755 --- a/ci/scripts/build_paimon.sh +++ b/ci/scripts/build_paimon.sh @@ -23,7 +23,7 @@ source_dir=${1} enable_sanitizer=${2:-false} check_clang_tidy=${3:-false} build_type=${4:-Debug} -build_dir=${1}/build +build_dir="${source_dir}/build" # Display ccache status if available if command -v ccache &> /dev/null; then @@ -34,8 +34,8 @@ else echo "=== ccache not found, compiling without cache acceleration ===" fi -mkdir ${build_dir} -pushd ${build_dir} +mkdir -p "${build_dir}" +pushd "${build_dir}" ENABLE_LUMINA="ON" ENABLE_LANCE="ON" @@ -65,9 +65,9 @@ if [[ "${enable_sanitizer}" == "true" ]]; then ) fi -cmake "${CMAKE_ARGS[@]}" ${source_dir} -cmake --build . -- -j$(nproc) -ctest --output-on-failure -j $(nproc) +cmake "${CMAKE_ARGS[@]}" "${source_dir}" +cmake --build . -- -j "$(nproc)" +ctest --output-on-failure -j "$(nproc)" if [[ "${check_clang_tidy}" == "true" ]]; then cmake --build . --target check-clang-tidy @@ -81,4 +81,4 @@ fi popd -rm -rf ${build_dir} +rm -rf "${build_dir}" From 16ca1ae33fe84d604eb303a7b08f1bb0541bc519 Mon Sep 17 00:00:00 2001 From: "jinli.zjw" Date: Wed, 27 May 2026 18:46:51 +0800 Subject: [PATCH 4/4] fix(build): defer build script migration --- ci/scripts/build_paimon.sh | 84 -------------------------------------- 1 file changed, 84 deletions(-) delete mode 100755 ci/scripts/build_paimon.sh diff --git a/ci/scripts/build_paimon.sh b/ci/scripts/build_paimon.sh deleted file mode 100755 index b64bd8d..0000000 --- a/ci/scripts/build_paimon.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bash -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -set -eux - -source_dir=${1} -enable_sanitizer=${2:-false} -check_clang_tidy=${3:-false} -build_type=${4:-Debug} -build_dir="${source_dir}/build" - -# Display ccache status if available -if command -v ccache &> /dev/null; then - echo "=== ccache found: $(ccache --version | head -1) ===" - ccache -p | grep -E "cache_dir|max_size|compression" || true - ccache -z # Reset statistics for this build -else - echo "=== ccache not found, compiling without cache acceleration ===" -fi - -mkdir -p "${build_dir}" -pushd "${build_dir}" - -ENABLE_LUMINA="ON" -ENABLE_LANCE="ON" -if [[ "${CC:-}" == *"gcc-8"* ]] || [[ "${CXX:-}" == *"g++-8"* ]]; then - ENABLE_LUMINA="OFF" # Lumina is only supported on GCC 9 or higher. - ENABLE_LANCE="OFF" - # Lance's prebuilt binaries can only be compiled on Ubuntu 22.04 and above - # which requires a higher version of glibc, - # but Ubuntu 22.04 and above no longer ships with gcc-8 by default. - # Consider supporting Lance from source compilation in the future -fi - -CMAKE_ARGS=( - "-G Ninja" - "-DCMAKE_BUILD_TYPE=${build_type}" - "-DPAIMON_BUILD_TESTS=ON" - "-DPAIMON_ENABLE_LANCE=${ENABLE_LANCE}" - "-DPAIMON_ENABLE_JINDO=ON" - "-DPAIMON_ENABLE_LUMINA=${ENABLE_LUMINA}" - "-DPAIMON_ENABLE_LUCENE=ON" -) - -if [[ "${enable_sanitizer}" == "true" ]]; then - CMAKE_ARGS+=( - "-DPAIMON_USE_ASAN=ON" - "-DPAIMON_USE_UBSAN=ON" - ) -fi - -cmake "${CMAKE_ARGS[@]}" "${source_dir}" -cmake --build . -- -j "$(nproc)" -ctest --output-on-failure -j "$(nproc)" - -if [[ "${check_clang_tidy}" == "true" ]]; then - cmake --build . --target check-clang-tidy -fi - -# Print ccache statistics after build -if command -v ccache &> /dev/null; then - echo "=== ccache statistics after build ===" - ccache -s -fi - -popd - -rm -rf "${build_dir}"