{"thread":{"id":"58458","subject":"[PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","startedAt":"2022-09-19T17:47:48Z","lastAt":"2022-11-01T06:59:23Z","messageCount":25,"participants":["Abhradeep Chakraborty via GitGitGadget","Derrick Stolee","Junio C Hamano","Abhradeep Chakraborty","Taylor Blau","Ævar Arnfjörð Bjarmason","rsbecker@nexbridge.com"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"463205","messageId":"pull.1357.git.1663609659.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":null,"subject":"[PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:34Z","receivedAt":"2022-09-19T17:47:48Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"Git currently uses ewah bitmaps ( which are based on run-length encoding) to\ncompress bitmaps. Ewah bitmaps stores bitmaps in the form of run-length\nwords i.e. instead of storing each and every bit, it tries to find\nconsecutive bits (having same value) and replace them with the value bit and\nthe range upto which the bit is present. It is simple and efficient. But one\ndownside of this approach is that we have to decompress the whole bitmap in\norder to find the bit of a certain position.\n\nFor small (or medium sized) bitmaps, this is not an issue. But it can be an\nissue for large (or extra large) bitmaps. In that case roaring bitmaps are\ngenerally more efficient[1] than ewah itself. Some benchmarks suggests that\nroaring bitmaps give more performance benefits than ewah or any other\nsimilar compression technique.\n\nThis patch series is currently in RFC state and it aims to let Git use\nroaring bitmaps. As this is an RFC patch series (for now), the code are not\nfully accurate (i.e. some tests are failing). But it is backward-compatible\n(tests related to ewah bitmaps are passing). Some commit messages might need\nmore explanation and some commits may need a split (specially the one that\nimplement writing roaring bitmaps). Overall, the structure and code are near\nto ready to make the series a formal patch series.\n\nI am submitting it as an RFC (after discussions with mentors) because the\nGSoC coding period is about to end. I will continue to work on the patch\nseries.\n\nAbhradeep Chakraborty (5):\n  reachability-bitmaps: add CRoaring library to Git\n  roaring.[ch]: apply Git specific changes to the roaring API\n  roaring: teach Git to write roaring bitmaps\n  roaring: introduce a new config option for roaring bitmaps\n  roaring: teach Git to read roaring bitmaps\n\n Makefile                   |     3 +\n bitmap.c                   |   225 +\n bitmap.h                   |    33 +\n builtin/diff.c             |    10 +-\n builtin/multi-pack-index.c |     5 +\n builtin/pack-objects.c     |    81 +-\n ewah/bitmap.c              |    61 +-\n ewah/ewok.h                |    37 +-\n midx.c                     |     7 +\n midx.h                     |     1 +\n pack-bitmap-write.c        |   326 +-\n pack-bitmap.c              |   969 +-\n pack-bitmap.h              |    27 +-\n roaring/roaring.c          | 20047 +++++++++++++++++++++++++++++++++++\n roaring/roaring.h          |  1028 ++\n t/t5310-pack-bitmaps.sh    |    79 +-\n 16 files changed, 22490 insertions(+), 449 deletions(-)\n create mode 100644 bitmap.c\n create mode 100644 bitmap.h\n create mode 100644 roaring/roaring.c\n create mode 100644 roaring/roaring.h\n\n\nbase-commit: d3fa443f97e3a8d75b51341e2d5bac380b7422df\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1357%2FAbhra303%2Froaring-bitmap-exp-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1357/Abhra303/roaring-bitmap-exp-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1357\n-- \ngitgitgadget\n"},{"id":"463924","messageId":"3c7521f13dd61a216a4a2e70d8cf349bc8902806.1663609659.git.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"[PATCH 1/5] reachability-bitmaps: add CRoaring library to Git","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:35Z","receivedAt":"2022-09-19T17:48:01Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n\nAccording to Roaring bitmap's paper[1], it gives better performance\n(in most cases) than EWAH bitmaps. Its compression ratio is also good.\nMoreover, unlike EWAH, it doesn't have to parse the whole bitmap to\nknow about one object. So, It may be good for Git to use Roaring\nbitmaps for its work.\n\nCRoaring is a well tested library for Roaring bitmaps and is mainly\nwritten by the author of the mentioned paper.\n\nAdd CRoaring library to use roaring bitmap related functions.\n\n[1] https://arxiv.org/pdf/1603.06549.pdf\n\nMentored-by: Taylor Blau <me@ttaylorr.com>\nCo-Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\nSigned-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n---\n Makefile          |     2 +\n roaring/roaring.c | 19522 ++++++++++++++++++++++++++++++++++++++++++++\n roaring/roaring.h |  1011 +++\n 3 files changed, 20535 insertions(+)\n create mode 100644 roaring/roaring.c\n create mode 100644 roaring/roaring.h\n\ndiff --git a/Makefile b/Makefile\nindex d9247ead45b..e9537951105 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1060,6 +1060,7 @@ LIB_OBJS += rerere.o\n LIB_OBJS += reset.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n+LIB_OBJS += roaring/roaring.o\n LIB_OBJS += run-command.o\n LIB_OBJS += send-pack.o\n LIB_OBJS += sequencer.o\n@@ -1258,6 +1259,7 @@ THIRD_PARTY_SOURCES += compat/nedmalloc/%\n THIRD_PARTY_SOURCES += compat/obstack.%\n THIRD_PARTY_SOURCES += compat/poll/%\n THIRD_PARTY_SOURCES += compat/regex/%\n+THIRD_PARTY_SOURCES += roaring/roaring.c\n THIRD_PARTY_SOURCES += sha1collisiondetection/%\n THIRD_PARTY_SOURCES += sha1dc/%\n \ndiff --git a/roaring/roaring.c b/roaring/roaring.c\nnew file mode 100644\nindex 00000000000..df2d90544cd\n--- /dev/null\n+++ b/roaring/roaring.c\n@@ -0,0 +1,19522 @@\n+/*\n+ * The CRoaring project is under a dual license (Apache/MIT).\n+ * Users of the library may choose one or the other license.\n+ */\n+/*\n+ * MIT License\n+ *\n+ * Copyright 2016-2022 The CRoaring authors\n+ *\n+ * Permission is hereby granted, free of charge, to any\n+ * person obtaining a copy of this software and associated\n+ * documentation files (the \"Software\"), to deal in the\n+ * Software without restriction, including without\n+ * limitation the rights to use, copy, modify, merge,\n+ * publish, distribute, sublicense, and/or sell copies of\n+ * the Software, and to permit persons to whom the Software\n+ * is furnished to do so, subject to the following\n+ * conditions:\n+ *\n+ * The above copyright notice and this permission notice\n+ * shall be included in all copies or substantial portions\n+ * of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\n+ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\n+ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\n+ * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\n+ * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n+ * DEALINGS IN THE SOFTWARE\n+ *\n+ * SPDX-License-Identifier: MIT\n+ */\n+\n+#include \"roaring.h\"\n+\n+/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */\n+#ifdef DMALLOC\n+#include \"dmalloc.h\"\n+#endif\n+\n+#include \"roaring.h\"  /* include public API definitions */\n+/* begin file include/roaring/isadetection.h */\n+/* From\n+https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h\n+Highly modified.\n+\n+Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)\n+Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)\n+Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)\n+Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)\n+Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)\n+Copyright (c) 2011-2013 NYU                      (Clement Farabet)\n+Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou,\n+Iain Melvin, Jason Weston) Copyright (c) 2006      Idiap Research Institute\n+(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,\n+Samy Bengio, Johnny Mariethoz)\n+\n+All rights reserved.\n+\n+Redistribution and use in source and binary forms, with or without\n+modification, are permitted provided that the following conditions are met:\n+\n+1. Redistributions of source code must retain the above copyright\n+   notice, this list of conditions and the following disclaimer.\n+\n+2. Redistributions in binary form must reproduce the above copyright\n+   notice, this list of conditions and the following disclaimer in the\n+   documentation and/or other materials provided with the distribution.\n+\n+3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories\n+America and IDIAP Research Institute nor the names of its contributors may be\n+   used to endorse or promote products derived from this software without\n+   specific prior written permission.\n+\n+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n+POSSIBILITY OF SUCH DAMAGE.\n+*/\n+\n+#ifndef ROARING_ISADETECTION_H\n+#define ROARING_ISADETECTION_H\n+\n+#include <stdint.h>\n+#include <stdbool.h>\n+#include <stdlib.h>\n+#if defined(_MSC_VER)\n+#include <intrin.h>\n+#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)\n+#include <cpuid.h>\n+#endif // defined(_MSC_VER)\n+\n+\n+enum croaring_instruction_set {\n+  CROARING_DEFAULT = 0x0,\n+  CROARING_NEON = 0x1,\n+  CROARING_AVX2 = 0x4,\n+  CROARING_SSE42 = 0x8,\n+  CROARING_PCLMULQDQ = 0x10,\n+  CROARING_BMI1 = 0x20,\n+  CROARING_BMI2 = 0x40,\n+  CROARING_ALTIVEC = 0x80,\n+  CROARING_UNINITIALIZED = 0x8000\n+};\n+\n+#if defined(__PPC64__)\n+\n+static inline uint32_t dynamic_croaring_detect_supported_architectures() {\n+  return CROARING_ALTIVEC;\n+}\n+\n+#elif defined(__arm__) || defined(__aarch64__) // incl. armel, armhf, arm64\n+\n+#if defined(__ARM_NEON)\n+\n+static inline uint32_t dynamic_croaring_detect_supported_architectures() {\n+  return CROARING_NEON;\n+}\n+\n+#else // ARM without NEON\n+\n+static inline uint32_t dynamic_croaring_detect_supported_architectures() {\n+  return CROARING_DEFAULT;\n+}\n+\n+#endif\n+\n+#elif defined(__x86_64__) || defined(_M_AMD64) // x64\n+\n+\n+\n+\n+static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,\n+                         uint32_t *edx) {\n+\n+#if defined(_MSC_VER)\n+  int cpu_info[4];\n+  __cpuid(cpu_info, *eax);\n+  *eax = cpu_info[0];\n+  *ebx = cpu_info[1];\n+  *ecx = cpu_info[2];\n+  *edx = cpu_info[3];\n+#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)\n+  uint32_t level = *eax;\n+  __get_cpuid(level, eax, ebx, ecx, edx);\n+#else\n+  uint32_t a = *eax, b, c = *ecx, d;\n+  __asm__(\"cpuid\\n\\t\" : \"+a\"(a), \"=b\"(b), \"+c\"(c), \"=d\"(d));\n+  *eax = a;\n+  *ebx = b;\n+  *ecx = c;\n+  *edx = d;\n+#endif\n+}\n+\n+static inline uint32_t dynamic_croaring_detect_supported_architectures() {\n+  uint32_t eax, ebx, ecx, edx;\n+  uint32_t host_isa = 0x0;\n+  // Can be found on Intel ISA Reference for CPUID\n+  static uint32_t cpuid_avx2_bit = 1 << 5;      ///< @private Bit 5 of EBX for EAX=0x7\n+  static uint32_t cpuid_bmi1_bit = 1 << 3;      ///< @private bit 3 of EBX for EAX=0x7\n+  static uint32_t cpuid_bmi2_bit = 1 << 8;      ///< @private bit 8 of EBX for EAX=0x7\n+  static uint32_t cpuid_sse42_bit = 1 << 20;    ///< @private bit 20 of ECX for EAX=0x1\n+  static uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit  1 of ECX for EAX=0x1\n+  // ECX for EAX=0x7\n+  eax = 0x7;\n+  ecx = 0x0;\n+  cpuid(&eax, &ebx, &ecx, &edx);\n+  if (ebx & cpuid_avx2_bit) {\n+    host_isa |= CROARING_AVX2;\n+  }\n+  if (ebx & cpuid_bmi1_bit) {\n+    host_isa |= CROARING_BMI1;\n+  }\n+\n+  if (ebx & cpuid_bmi2_bit) {\n+    host_isa |= CROARING_BMI2;\n+  }\n+\n+  // EBX for EAX=0x1\n+  eax = 0x1;\n+  cpuid(&eax, &ebx, &ecx, &edx);\n+\n+  if (ecx & cpuid_sse42_bit) {\n+    host_isa |= CROARING_SSE42;\n+  }\n+\n+  if (ecx & cpuid_pclmulqdq_bit) {\n+    host_isa |= CROARING_PCLMULQDQ;\n+  }\n+\n+  return host_isa;\n+}\n+#else // fallback\n+\n+\n+static inline uint32_t dynamic_croaring_detect_supported_architectures() {\n+  return CROARING_DEFAULT;\n+}\n+\n+\n+#endif // end SIMD extension detection code\n+\n+\n+#if defined(__x86_64__) || defined(_M_AMD64) // x64\n+\n+#if defined(__cplusplus)\n+#include <atomic>\n+static inline uint32_t croaring_detect_supported_architectures() {\n+    static std::atomic<int> buffer{CROARING_UNINITIALIZED};\n+    if(buffer == CROARING_UNINITIALIZED) {\n+      buffer = dynamic_croaring_detect_supported_architectures();\n+    }\n+    return buffer;\n+}\n+#elif defined(_MSC_VER) && !defined(__clang__)\n+// Visual Studio does not support C11 atomics.\n+static inline uint32_t croaring_detect_supported_architectures() {\n+    static int buffer = CROARING_UNINITIALIZED;\n+    if(buffer == CROARING_UNINITIALIZED) {\n+      buffer = dynamic_croaring_detect_supported_architectures();\n+    }\n+    return buffer;\n+}\n+#else // defined(__cplusplus) and defined(_MSC_VER) && !defined(__clang__)\n+#include <stdatomic.h>\n+static inline uint32_t croaring_detect_supported_architectures() {\n+    static _Atomic int buffer = CROARING_UNINITIALIZED;\n+    if(buffer == CROARING_UNINITIALIZED) {\n+      buffer = dynamic_croaring_detect_supported_architectures();\n+    }\n+    return buffer;\n+}\n+#endif // defined(_MSC_VER) && !defined(__clang__)\n+\n+#ifdef ROARING_DISABLE_AVX\n+static inline bool croaring_avx2() {\n+  return false;\n+}\n+#elif defined(__AVX2__)\n+static inline bool croaring_avx2() {\n+  return true;\n+}\n+#else\n+static inline bool croaring_avx2() {\n+  return  (croaring_detect_supported_architectures() & CROARING_AVX2) == CROARING_AVX2;\n+}\n+#endif\n+\n+\n+#else // defined(__x86_64__) || defined(_M_AMD64) // x64\n+\n+static inline bool croaring_avx2() {\n+  return false;\n+}\n+\n+static inline uint32_t croaring_detect_supported_architectures() {\n+    // no runtime dispatch\n+    return dynamic_croaring_detect_supported_architectures();\n+}\n+#endif // defined(__x86_64__) || defined(_M_AMD64) // x64\n+\n+#endif // ROARING_ISADETECTION_H\n+/* end file include/roaring/isadetection.h */\n+/* begin file include/roaring/portability.h */\n+/*\n+ * portability.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_PORTABILITY_H_\n+#define INCLUDE_PORTABILITY_H_\n+\n+#ifndef _GNU_SOURCE\n+#define _GNU_SOURCE 1\n+#endif // _GNU_SOURCE\n+#ifndef __STDC_FORMAT_MACROS\n+#define __STDC_FORMAT_MACROS 1\n+#endif // __STDC_FORMAT_MACROS\n+\n+#if !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L)\n+#define _POSIX_C_SOURCE 200809L\n+#endif // !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L)\n+#if !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700)\n+#define _XOPEN_SOURCE 700\n+#endif // !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700)\n+\n+#include <stdbool.h>\n+#include <stdint.h>\n+#include <stdlib.h>  // will provide posix_memalign with _POSIX_C_SOURCE as defined above\n+#if !(defined(__APPLE__)) && !(defined(__FreeBSD__))\n+#include <malloc.h>  // this should never be needed but there are some reports that it is needed.\n+#endif\n+\n+#ifdef __cplusplus\n+extern \"C\" {  // portability definitions are in global scope, not a namespace\n+#endif\n+\n+#if defined(_MSC_VER) && !defined(__clang__) && !defined(_WIN64) && !defined(ROARING_ACK_32BIT)\n+#pragma message( \\\n+    \"You appear to be attempting a 32-bit build under Visual Studio. We recommend a 64-bit build instead.\")\n+#endif\n+\n+#if defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ != 8\n+#error This code assumes  64-bit long longs (by use of the GCC intrinsics). Your system is not currently supported.\n+#endif\n+\n+#if defined(_MSC_VER)\n+#define __restrict__ __restrict\n+#endif // defined(_MSC_VER\n+\n+\n+\n+#if defined(__x86_64__) || defined(_M_X64)\n+// we have an x64 processor\n+#define CROARING_IS_X64\n+\n+#if defined(_MSC_VER) && (_MSC_VER < 1910)\n+// Old visual studio systems won't support AVX2 well.\n+#undef CROARING_IS_X64\n+#endif\n+\n+#if defined(__clang_major__) && (__clang_major__<= 8) && !defined(__AVX2__)\n+// Older versions of clang have a bug affecting us\n+// https://stackoverflow.com/questions/57228537/how-does-one-use-pragma-clang-attribute-push-with-c-namespaces\n+#undef CROARING_IS_X64\n+#endif\n+\n+#ifdef ROARING_DISABLE_X64\n+#undef CROARING_IS_X64\n+#endif\n+// we include the intrinsic header\n+#ifndef _MSC_VER\n+/* Non-Microsoft C/C++-compatible compiler */\n+#include <x86intrin.h>  // on some recent GCC, this will declare posix_memalign\n+#endif // _MSC_VER\n+#endif // defined(__x86_64__) || defined(_M_X64)\n+\n+#if !defined(USENEON) && !defined(DISABLENEON) && defined(__ARM_NEON)\n+#  define USENEON\n+#endif\n+#if defined(USENEON)\n+#  include <arm_neon.h>\n+#endif\n+\n+#ifndef _MSC_VER\n+/* Non-Microsoft C/C++-compatible compiler, assumes that it supports inline\n+ * assembly */\n+#define ROARING_INLINE_ASM\n+#endif  // _MSC_VER\n+\n+\n+#ifdef _MSC_VER\n+/* Microsoft C/C++-compatible compiler */\n+#include <intrin.h>\n+\n+#ifndef __clang__  // if one compiles with MSVC *with* clang, then these\n+                   // intrinsics are defined!!!\n+// sadly there is no way to check whether we are missing these intrinsics\n+// specifically.\n+\n+/* wrappers for Visual Studio built-ins that look like gcc built-ins */\n+/* result might be undefined when input_num is zero */\n+inline int __builtin_ctzll(unsigned long long input_num) {\n+    unsigned long index;\n+#ifdef _WIN64  // highly recommended!!!\n+    _BitScanForward64(&index, input_num);\n+#else  // if we must support 32-bit Windows\n+    if ((uint32_t)input_num != 0) {\n+        _BitScanForward(&index, (uint32_t)input_num);\n+    } else {\n+        _BitScanForward(&index, (uint32_t)(input_num >> 32));\n+        index += 32;\n+    }\n+#endif\n+    return index;\n+}\n+\n+/* result might be undefined when input_num is zero */\n+inline int __builtin_clzll(unsigned long long input_num) {\n+    unsigned long index;\n+#ifdef _WIN64  // highly recommended!!!\n+    _BitScanReverse64(&index, input_num);\n+#else  // if we must support 32-bit Windows\n+    if (input_num > 0xFFFFFFFF) {\n+        _BitScanReverse(&index, (uint32_t)(input_num >> 32));\n+        index += 32;\n+    } else {\n+        _BitScanReverse(&index, (uint32_t)(input_num));\n+    }\n+#endif\n+    return 63 - index;\n+}\n+\n+\n+/* software implementation avoids POPCNT */\n+/*static inline int __builtin_popcountll(unsigned long long input_num) {\n+  const uint64_t m1 = 0x5555555555555555; //binary: 0101...\n+  const uint64_t m2 = 0x3333333333333333; //binary: 00110011..\n+  const uint64_t m4 = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...\n+  const uint64_t h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...\n+\n+  input_num -= (input_num >> 1) & m1;\n+  input_num = (input_num & m2) + ((input_num >> 2) & m2);\n+  input_num = (input_num + (input_num >> 4)) & m4;\n+  return (input_num * h01) >> 56;\n+}*/\n+\n+/* Use #define so this is effective even under /Ob0 (no inline) */\n+#define __builtin_unreachable() __assume(0)\n+#endif\n+\n+#endif\n+\n+#if defined(_MSC_VER)\n+#define ALIGNED(x) __declspec(align(x))\n+#else\n+#if defined(__GNUC__)\n+#define ALIGNED(x) __attribute__((aligned(x)))\n+#endif\n+#endif\n+\n+#ifdef __GNUC__\n+#define WARN_UNUSED __attribute__((warn_unused_result))\n+#else\n+#define WARN_UNUSED\n+#endif\n+\n+#define IS_BIG_ENDIAN (*(uint16_t *)\"\\0\\xff\" < 0x100)\n+\n+static inline int hammingbackup(uint64_t x) {\n+  uint64_t c1 = UINT64_C(0x5555555555555555);\n+  uint64_t c2 = UINT64_C(0x3333333333333333);\n+  uint64_t c4 = UINT64_C(0x0F0F0F0F0F0F0F0F);\n+  x -= (x >> 1) & c1;\n+  x = (( x >> 2) & c2) + (x & c2); x=(x +(x>>4))&c4;\n+  x *= UINT64_C(0x0101010101010101);\n+  return x >> 56;\n+}\n+\n+static inline int hamming(uint64_t x) {\n+#if defined(_WIN64) && defined(_MSC_VER) && !defined(__clang__)\n+#ifdef _M_ARM64\n+  return hammingbackup(x);\n+  // (int) _CountOneBits64(x); is unavailable\n+#else  // _M_ARM64\n+  return (int) __popcnt64(x);\n+#endif // _M_ARM64\n+#elif defined(_WIN32) && defined(_MSC_VER) && !defined(__clang__)\n+#ifdef _M_ARM\n+  return hammingbackup(x);\n+  // _CountOneBits is unavailable\n+#else // _M_ARM\n+    return (int) __popcnt(( unsigned int)x) + (int)  __popcnt(( unsigned int)(x>>32));\n+#endif // _M_ARM\n+#else\n+    return __builtin_popcountll(x);\n+#endif\n+}\n+\n+#ifndef UINT64_C\n+#define UINT64_C(c) (c##ULL)\n+#endif // UINT64_C\n+\n+#ifndef UINT32_C\n+#define UINT32_C(c) (c##UL)\n+#endif // UINT32_C\n+\n+#ifdef __cplusplus\n+}  // extern \"C\" {\n+#endif // __cplusplus\n+\n+\n+// this is almost standard?\n+#undef STRINGIFY_IMPLEMENTATION_\n+#undef STRINGIFY\n+#define STRINGIFY_IMPLEMENTATION_(a) #a\n+#define STRINGIFY(a) STRINGIFY_IMPLEMENTATION_(a)\n+\n+// Our fast kernels require 64-bit systems.\n+//\n+// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions.\n+// Furthermore, the number of SIMD registers is reduced.\n+//\n+// On 32-bit ARM, we would have smaller registers.\n+//\n+// The library should still have the fallback kernel. It is\n+// slower, but it should run everywhere.\n+\n+//\n+// Enable valid runtime implementations, and select CROARING_BUILTIN_IMPLEMENTATION\n+//\n+\n+// We are going to use runtime dispatch.\n+#ifdef CROARING_IS_X64\n+#ifdef __clang__\n+// clang does not have GCC push pop\n+// warning: clang attribute push can't be used within a namespace in clang up\n+// til 8.0 so CROARING_TARGET_REGION and CROARING_UNTARGET_REGION must be *outside* of a\n+// namespace.\n+#define CROARING_TARGET_REGION(T)                                                       \\\n+  _Pragma(STRINGIFY(                                                           \\\n+      clang attribute push(__attribute__((target(T))), apply_to = function)))\n+#define CROARING_UNTARGET_REGION _Pragma(\"clang attribute pop\")\n+#elif defined(__GNUC__)\n+// GCC is easier\n+#define CROARING_TARGET_REGION(T)                                                       \\\n+  _Pragma(\"GCC push_options\") _Pragma(STRINGIFY(GCC target(T)))\n+#define CROARING_UNTARGET_REGION _Pragma(\"GCC pop_options\")\n+#endif // clang then gcc\n+\n+#endif // CROARING_IS_X64\n+\n+// Default target region macros don't do anything.\n+#ifndef CROARING_TARGET_REGION\n+#define CROARING_TARGET_REGION(T)\n+#define CROARING_UNTARGET_REGION\n+#endif\n+\n+#define CROARING_TARGET_AVX2 CROARING_TARGET_REGION(\"avx2,bmi,pclmul,lzcnt\")\n+\n+#ifdef __AVX2__\n+// No need for runtime dispatching.\n+// It is unnecessary and harmful to old clang to tag regions.\n+#undef CROARING_TARGET_AVX2\n+#define CROARING_TARGET_AVX2\n+#undef CROARING_UNTARGET_REGION\n+#define CROARING_UNTARGET_REGION\n+#endif\n+\n+#endif /* INCLUDE_PORTABILITY_H_ */\n+/* end file include/roaring/portability.h */\n+/* begin file include/roaring/containers/perfparameters.h */\n+#ifndef PERFPARAMETERS_H_\n+#define PERFPARAMETERS_H_\n+\n+#include <stdbool.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/**\n+During lazy computations, we can transform array containers into bitset\n+containers as\n+long as we can expect them to have  ARRAY_LAZY_LOWERBOUND values.\n+*/\n+enum { ARRAY_LAZY_LOWERBOUND = 1024 };\n+\n+/* default initial size of a run container\n+   setting it to zero delays the malloc.*/\n+enum { RUN_DEFAULT_INIT_SIZE = 0 };\n+\n+/* default initial size of an array container\n+   setting it to zero delays the malloc */\n+enum { ARRAY_DEFAULT_INIT_SIZE = 0 };\n+\n+/* automatic bitset conversion during lazy or */\n+#ifndef LAZY_OR_BITSET_CONVERSION\n+#define LAZY_OR_BITSET_CONVERSION true\n+#endif\n+\n+/* automatically attempt to convert a bitset to a full run during lazy\n+ * evaluation */\n+#ifndef LAZY_OR_BITSET_CONVERSION_TO_FULL\n+#define LAZY_OR_BITSET_CONVERSION_TO_FULL true\n+#endif\n+\n+/* automatically attempt to convert a bitset to a full run */\n+#ifndef OR_BITSET_CONVERSION_TO_FULL\n+#define OR_BITSET_CONVERSION_TO_FULL true\n+#endif\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif\n+/* end file include/roaring/containers/perfparameters.h */\n+/* begin file include/roaring/containers/container_defs.h */\n+/*\n+ * container_defs.h\n+ *\n+ * Unlike containers.h (which is a file aggregating all the container includes,\n+ * like array.h, bitset.h, and run.h) this is a file included BY those headers\n+ * to do things like define the container base class `container_t`.\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_CONTAINER_DEFS_H_\n+#define INCLUDE_CONTAINERS_CONTAINER_DEFS_H_\n+\n+#ifdef __cplusplus\n+    #include <type_traits>  // used by casting helper for compile-time check\n+#endif\n+\n+// The preferences are a separate file to separate out tweakable parameters\n+\n+#ifdef __cplusplus\n+namespace roaring { namespace internal {  // No extern \"C\" (contains template)\n+#endif\n+\n+\n+/*\n+ * Since roaring_array_t's definition is not opaque, the container type is\n+ * part of the API.  If it's not going to be `void*` then it needs a name, and\n+ * expectations are to prefix C library-exported names with `roaring_` etc.\n+ *\n+ * Rather than force the whole codebase to use the name `roaring_container_t`,\n+ * the few API appearances use the macro ROARING_CONTAINER_T.  Those includes\n+ * are prior to containers.h, so make a short private alias of `container_t`.\n+ * Then undefine the awkward macro so it's not used any more than it has to be.\n+ */\n+typedef ROARING_CONTAINER_T container_t;\n+#undef ROARING_CONTAINER_T\n+\n+\n+/*\n+ * See ROARING_CONTAINER_T for notes on using container_t as a base class.\n+ * This macro helps make the following pattern look nicer:\n+ *\n+ *     #ifdef __cplusplus\n+ *     struct roaring_array_s : public container_t {\n+ *     #else\n+ *     struct roaring_array_s {\n+ *     #endif\n+ *         int32_t cardinality;\n+ *         int32_t capacity;\n+ *         uint16_t *array;\n+ *     }\n+ */\n+#if defined(__cplusplus)\n+    #define STRUCT_CONTAINER(name) \\\n+        struct name : public container_t  /* { ... } */\n+#else\n+    #define STRUCT_CONTAINER(name) \\\n+        struct name  /* { ... } */\n+#endif\n+\n+\n+/**\n+ * Since container_t* is not void* in C++, \"dangerous\" casts are not needed to\n+ * downcast; only a static_cast<> is needed.  Define a macro for static casting\n+ * which helps make casts more visible, and catches problems at compile-time\n+ * when building the C sources in C++ mode:\n+ *\n+ *     void some_func(container_t **c, ...) {  // double pointer, not single\n+ *         array_container_t *ac1 = (array_container_t *)(c);  // uncaught!!\n+ *\n+ *         array_container_t *ac2 = CAST(array_container_t *, c)  // C++ errors\n+ *         array_container_t *ac3 = CAST_array(c);  // shorthand for #2, errors\n+ *     }\n+ *\n+ * Trickier to do is a cast from `container**` to `array_container_t**`.  This\n+ * needs a reinterpret_cast<>, which sacrifices safety...so a template is used\n+ * leveraging <type_traits> to make sure it's legal in the C++ build.\n+ */\n+#ifdef __cplusplus\n+    #define CAST(type,value)            static_cast<type>(value)\n+    #define movable_CAST(type,value)    movable_CAST_HELPER<type>(value)\n+\n+    template<typename PPDerived, typename Base>\n+    PPDerived movable_CAST_HELPER(Base **ptr_to_ptr) {\n+        typedef typename std::remove_pointer<PPDerived>::type PDerived;\n+        typedef typename std::remove_pointer<PDerived>::type Derived;\n+        static_assert(\n+            std::is_base_of<Base, Derived>::value,\n+            \"use movable_CAST() for container_t** => xxx_container_t**\"\n+        );\n+        return reinterpret_cast<Derived**>(ptr_to_ptr);\n+    }\n+#else\n+    #define CAST(type,value)            ((type)value)\n+    #define movable_CAST(type, value)   ((type)value)\n+#endif\n+\n+// Use for converting e.g. an `array_container_t**` to a `container_t**`\n+//\n+#define movable_CAST_base(c)   movable_CAST(container_t **, c)\n+\n+\n+#ifdef __cplusplus\n+} }  // namespace roaring { namespace internal {\n+#endif\n+\n+#endif  /* INCLUDE_CONTAINERS_CONTAINER_DEFS_H_ */\n+/* end file include/roaring/containers/container_defs.h */\n+/* begin file include/roaring/array_util.h */\n+#ifndef ARRAY_UTIL_H\n+#define ARRAY_UTIL_H\n+\n+#include <stddef.h>  // for size_t\n+#include <stdint.h>\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/*\n+ *  Good old binary search.\n+ *  Assumes that array is sorted, has logarithmic complexity.\n+ *  if the result is x, then:\n+ *     if ( x>0 )  you have array[x] = ikey\n+ *     if ( x<0 ) then inserting ikey at position -x-1 in array (insuring that array[-x-1]=ikey)\n+ *                   keys the array sorted.\n+ */\n+inline int32_t binarySearch(const uint16_t *array, int32_t lenarray,\n+                            uint16_t ikey) {\n+    int32_t low = 0;\n+    int32_t high = lenarray - 1;\n+    while (low <= high) {\n+        int32_t middleIndex = (low + high) >> 1;\n+        uint16_t middleValue = array[middleIndex];\n+        if (middleValue < ikey) {\n+            low = middleIndex + 1;\n+        } else if (middleValue > ikey) {\n+            high = middleIndex - 1;\n+        } else {\n+            return middleIndex;\n+        }\n+    }\n+    return -(low + 1);\n+}\n+\n+/**\n+ * Galloping search\n+ * Assumes that array is sorted, has logarithmic complexity.\n+ * if the result is x, then if x = length, you have that all values in array between pos and length\n+ *    are smaller than min.\n+ * otherwise returns the first index x such that array[x] >= min.\n+ */\n+static inline int32_t advanceUntil(const uint16_t *array, int32_t pos,\n+                                   int32_t length, uint16_t min) {\n+    int32_t lower = pos + 1;\n+\n+    if ((lower >= length) || (array[lower] >= min)) {\n+        return lower;\n+    }\n+\n+    int32_t spansize = 1;\n+\n+    while ((lower + spansize < length) && (array[lower + spansize] < min)) {\n+        spansize <<= 1;\n+    }\n+    int32_t upper = (lower + spansize < length) ? lower + spansize : length - 1;\n+\n+    if (array[upper] == min) {\n+        return upper;\n+    }\n+    if (array[upper] < min) {\n+        // means\n+        // array\n+        // has no\n+        // item\n+        // >= min\n+        // pos = array.length;\n+        return length;\n+    }\n+\n+    // we know that the next-smallest span was too small\n+    lower += (spansize >> 1);\n+\n+    int32_t mid = 0;\n+    while (lower + 1 != upper) {\n+        mid = (lower + upper) >> 1;\n+        if (array[mid] == min) {\n+            return mid;\n+        } else if (array[mid] < min) {\n+            lower = mid;\n+        } else {\n+            upper = mid;\n+        }\n+    }\n+    return upper;\n+}\n+\n+/**\n+ * Returns number of elements which are less then $ikey.\n+ * Array elements must be unique and sorted.\n+ */\n+static inline int32_t count_less(const uint16_t *array, int32_t lenarray,\n+                                 uint16_t ikey) {\n+    if (lenarray == 0) return 0;\n+    int32_t pos = binarySearch(array, lenarray, ikey);\n+    return pos >= 0 ? pos : -(pos+1);\n+}\n+\n+/**\n+ * Returns number of elements which are greater then $ikey.\n+ * Array elements must be unique and sorted.\n+ */\n+static inline int32_t count_greater(const uint16_t *array, int32_t lenarray,\n+                                    uint16_t ikey) {\n+    if (lenarray == 0) return 0;\n+    int32_t pos = binarySearch(array, lenarray, ikey);\n+    if (pos >= 0) {\n+        return lenarray - (pos+1);\n+    } else {\n+        return lenarray - (-pos-1);\n+    }\n+}\n+\n+/**\n+ * From Schlegel et al., Fast Sorted-Set Intersection using SIMD Instructions\n+ * Optimized by D. Lemire on May 3rd 2013\n+ *\n+ * C should have capacity greater than the minimum of s_1 and s_b + 8\n+ * where 8 is sizeof(__m128i)/sizeof(uint16_t).\n+ */\n+int32_t intersect_vector16(const uint16_t *__restrict__ A, size_t s_a,\n+                           const uint16_t *__restrict__ B, size_t s_b,\n+                           uint16_t *C);\n+\n+/**\n+ * Compute the cardinality of the intersection using SSE4 instructions\n+ */\n+int32_t intersect_vector16_cardinality(const uint16_t *__restrict__ A,\n+                                       size_t s_a,\n+                                       const uint16_t *__restrict__ B,\n+                                       size_t s_b);\n+\n+/* Computes the intersection between one small and one large set of uint16_t.\n+ * Stores the result into buffer and return the number of elements. */\n+int32_t intersect_skewed_uint16(const uint16_t *smallarray, size_t size_s,\n+                                const uint16_t *largearray, size_t size_l,\n+                                uint16_t *buffer);\n+\n+/* Computes the size of the intersection between one small and one large set of\n+ * uint16_t. */\n+int32_t intersect_skewed_uint16_cardinality(const uint16_t *smallarray,\n+                                            size_t size_s,\n+                                            const uint16_t *largearray,\n+                                            size_t size_l);\n+\n+\n+/* Check whether the size of the intersection between one small and one large set of uint16_t is non-zero. */\n+bool intersect_skewed_uint16_nonempty(const uint16_t *smallarray, size_t size_s,\n+                                const uint16_t *largearray, size_t size_l);\n+/**\n+ * Generic intersection function.\n+ */\n+int32_t intersect_uint16(const uint16_t *A, const size_t lenA,\n+                         const uint16_t *B, const size_t lenB, uint16_t *out);\n+/**\n+ * Compute the size of the intersection (generic).\n+ */\n+int32_t intersect_uint16_cardinality(const uint16_t *A, const size_t lenA,\n+                                     const uint16_t *B, const size_t lenB);\n+\n+/**\n+ * Checking whether the size of the intersection  is non-zero.\n+ */\n+bool intersect_uint16_nonempty(const uint16_t *A, const size_t lenA,\n+                         const uint16_t *B, const size_t lenB);\n+/**\n+ * Generic union function.\n+ */\n+size_t union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2,\n+                    size_t size_2, uint16_t *buffer);\n+\n+/**\n+ * Generic XOR function.\n+ */\n+int32_t xor_uint16(const uint16_t *array_1, int32_t card_1,\n+                   const uint16_t *array_2, int32_t card_2, uint16_t *out);\n+\n+/**\n+ * Generic difference function (ANDNOT).\n+ */\n+int difference_uint16(const uint16_t *a1, int length1, const uint16_t *a2,\n+                      int length2, uint16_t *a_out);\n+\n+/**\n+ * Generic intersection function.\n+ */\n+size_t intersection_uint32(const uint32_t *A, const size_t lenA,\n+                           const uint32_t *B, const size_t lenB, uint32_t *out);\n+\n+/**\n+ * Generic intersection function, returns just the cardinality.\n+ */\n+size_t intersection_uint32_card(const uint32_t *A, const size_t lenA,\n+                                const uint32_t *B, const size_t lenB);\n+\n+/**\n+ * Generic union function.\n+ */\n+size_t union_uint32(const uint32_t *set_1, size_t size_1, const uint32_t *set_2,\n+                    size_t size_2, uint32_t *buffer);\n+\n+/**\n+ * A fast SSE-based union function.\n+ */\n+uint32_t union_vector16(const uint16_t *__restrict__ set_1, uint32_t size_1,\n+                        const uint16_t *__restrict__ set_2, uint32_t size_2,\n+                        uint16_t *__restrict__ buffer);\n+/**\n+ * A fast SSE-based XOR function.\n+ */\n+uint32_t xor_vector16(const uint16_t *__restrict__ array1, uint32_t length1,\n+                      const uint16_t *__restrict__ array2, uint32_t length2,\n+                      uint16_t *__restrict__ output);\n+\n+/**\n+ * A fast SSE-based difference function.\n+ */\n+int32_t difference_vector16(const uint16_t *__restrict__ A, size_t s_a,\n+                            const uint16_t *__restrict__ B, size_t s_b,\n+                            uint16_t *C);\n+\n+/**\n+ * Generic union function, returns just the cardinality.\n+ */\n+size_t union_uint32_card(const uint32_t *set_1, size_t size_1,\n+                         const uint32_t *set_2, size_t size_2);\n+\n+/**\n+* combines union_uint16 and  union_vector16 optimally\n+*/\n+size_t fast_union_uint16(const uint16_t *set_1, size_t size_1, const uint16_t *set_2,\n+                    size_t size_2, uint16_t *buffer);\n+\n+\n+bool memequals(const void *s1, const void *s2, size_t n);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif\n+/* end file include/roaring/array_util.h */\n+/* begin file include/roaring/utilasm.h */\n+/*\n+ * utilasm.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_UTILASM_H_\n+#define INCLUDE_UTILASM_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring {\n+#endif\n+\n+#if defined(ROARING_INLINE_ASM)\n+#define CROARING_ASMBITMANIPOPTIMIZATION  // optimization flag\n+\n+#define ASM_SHIFT_RIGHT(srcReg, bitsReg, destReg) \\\n+    __asm volatile(\"shrx %1, %2, %0\"              \\\n+                   : \"=r\"(destReg)                \\\n+                   :             /* write */      \\\n+                   \"r\"(bitsReg), /* read only */  \\\n+                   \"r\"(srcReg)   /* read only */  \\\n+                   )\n+\n+#define ASM_INPLACESHIFT_RIGHT(srcReg, bitsReg)  \\\n+    __asm volatile(\"shrx %1, %0, %0\"             \\\n+                   : \"+r\"(srcReg)                \\\n+                   :            /* read/write */ \\\n+                   \"r\"(bitsReg) /* read only */  \\\n+                   )\n+\n+#define ASM_SHIFT_LEFT(srcReg, bitsReg, destReg) \\\n+    __asm volatile(\"shlx %1, %2, %0\"             \\\n+                   : \"=r\"(destReg)               \\\n+                   :             /* write */     \\\n+                   \"r\"(bitsReg), /* read only */ \\\n+                   \"r\"(srcReg)   /* read only */ \\\n+                   )\n+// set bit at position testBit within testByte to 1 and\n+// copy cmovDst to cmovSrc if that bit was previously clear\n+#define ASM_SET_BIT_INC_WAS_CLEAR(testByte, testBit, count) \\\n+    __asm volatile(                                         \\\n+        \"bts %2, %0\\n\"                                      \\\n+        \"sbb $-1, %1\\n\"                                     \\\n+        : \"+r\"(testByte), /* read/write */                  \\\n+          \"+r\"(count)                                       \\\n+        :            /* read/write */                       \\\n+        \"r\"(testBit) /* read only */                        \\\n+        )\n+\n+#define ASM_CLEAR_BIT_DEC_WAS_SET(testByte, testBit, count) \\\n+    __asm volatile(                                         \\\n+        \"btr %2, %0\\n\"                                      \\\n+        \"sbb $0, %1\\n\"                                      \\\n+        : \"+r\"(testByte), /* read/write */                  \\\n+          \"+r\"(count)                                       \\\n+        :            /* read/write */                       \\\n+        \"r\"(testBit) /* read only */                        \\\n+        )\n+\n+#define ASM_BT64(testByte, testBit, count) \\\n+    __asm volatile(                        \\\n+        \"bt %2,%1\\n\"                       \\\n+        \"sbb %0,%0\" /*could use setb */    \\\n+        : \"=r\"(count)                      \\\n+        :              /* write */         \\\n+        \"r\"(testByte), /* read only */     \\\n+        \"r\"(testBit)   /* read only */     \\\n+        )\n+\n+#endif\n+\n+#ifdef __cplusplus\n+} }  // extern \"C\" { namespace roaring {\n+#endif\n+\n+#endif  /* INCLUDE_UTILASM_H_ */\n+/* end file include/roaring/utilasm.h */\n+/* begin file include/roaring/bitset_util.h */\n+#ifndef BITSET_UTIL_H\n+#define BITSET_UTIL_H\n+\n+#include <stdint.h>\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/*\n+ * Set all bits in indexes [begin,end) to true.\n+ */\n+static inline void bitset_set_range(uint64_t *words, uint32_t start,\n+                                    uint32_t end) {\n+    if (start == end) return;\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (end - 1) / 64;\n+    if (firstword == endword) {\n+        words[firstword] |= ((~UINT64_C(0)) << (start % 64)) &\n+                             ((~UINT64_C(0)) >> ((~end + 1) % 64));\n+        return;\n+    }\n+    words[firstword] |= (~UINT64_C(0)) << (start % 64);\n+    for (uint32_t i = firstword + 1; i < endword; i++) {\n+        words[i] = ~UINT64_C(0);\n+    }\n+    words[endword] |= (~UINT64_C(0)) >> ((~end + 1) % 64);\n+}\n+\n+\n+/*\n+ * Find the cardinality of the bitset in [begin,begin+lenminusone]\n+ */\n+static inline int bitset_lenrange_cardinality(const uint64_t *words,\n+                                              uint32_t start,\n+                                              uint32_t lenminusone) {\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (start + lenminusone) / 64;\n+    if (firstword == endword) {\n+        return hamming(words[firstword] &\n+                       ((~UINT64_C(0)) >> ((63 - lenminusone) % 64))\n+                           << (start % 64));\n+    }\n+    int answer = hamming(words[firstword] & ((~UINT64_C(0)) << (start % 64)));\n+    for (uint32_t i = firstword + 1; i < endword; i++) {\n+        answer += hamming(words[i]);\n+    }\n+    answer +=\n+        hamming(words[endword] &\n+                (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64));\n+    return answer;\n+}\n+\n+/*\n+ * Check whether the cardinality of the bitset in [begin,begin+lenminusone] is 0\n+ */\n+static inline bool bitset_lenrange_empty(const uint64_t *words, uint32_t start,\n+                                         uint32_t lenminusone) {\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (start + lenminusone) / 64;\n+    if (firstword == endword) {\n+        return (words[firstword] & ((~UINT64_C(0)) >> ((63 - lenminusone) % 64))\n+              << (start % 64)) == 0;\n+    }\n+    if (((words[firstword] & ((~UINT64_C(0)) << (start%64)))) != 0) {\n+        return false;\n+    }\n+    for (uint32_t i = firstword + 1; i < endword; i++) {\n+        if (words[i] != 0) {\n+            return false;\n+        }\n+    }\n+    if ((words[endword] & (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64)) != 0) {\n+        return false;\n+    }\n+    return true;\n+}\n+\n+\n+/*\n+ * Set all bits in indexes [begin,begin+lenminusone] to true.\n+ */\n+static inline void bitset_set_lenrange(uint64_t *words, uint32_t start,\n+                                       uint32_t lenminusone) {\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (start + lenminusone) / 64;\n+    if (firstword == endword) {\n+        words[firstword] |= ((~UINT64_C(0)) >> ((63 - lenminusone) % 64))\n+                             << (start % 64);\n+        return;\n+    }\n+    uint64_t temp = words[endword];\n+    words[firstword] |= (~UINT64_C(0)) << (start % 64);\n+    for (uint32_t i = firstword + 1; i < endword; i += 2)\n+        words[i] = words[i + 1] = ~UINT64_C(0);\n+    words[endword] =\n+        temp | (~UINT64_C(0)) >> (((~start + 1) - lenminusone - 1) % 64);\n+}\n+\n+/*\n+ * Flip all the bits in indexes [begin,end).\n+ */\n+static inline void bitset_flip_range(uint64_t *words, uint32_t start,\n+                                     uint32_t end) {\n+    if (start == end) return;\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (end - 1) / 64;\n+    words[firstword] ^= ~((~UINT64_C(0)) << (start % 64));\n+    for (uint32_t i = firstword; i < endword; i++) {\n+        words[i] = ~words[i];\n+    }\n+    words[endword] ^= ((~UINT64_C(0)) >> ((~end + 1) % 64));\n+}\n+\n+/*\n+ * Set all bits in indexes [begin,end) to false.\n+ */\n+static inline void bitset_reset_range(uint64_t *words, uint32_t start,\n+                                      uint32_t end) {\n+    if (start == end) return;\n+    uint32_t firstword = start / 64;\n+    uint32_t endword = (end - 1) / 64;\n+    if (firstword == endword) {\n+        words[firstword] &= ~(((~UINT64_C(0)) << (start % 64)) &\n+                               ((~UINT64_C(0)) >> ((~end + 1) % 64)));\n+        return;\n+    }\n+    words[firstword] &= ~((~UINT64_C(0)) << (start % 64));\n+    for (uint32_t i = firstword + 1; i < endword; i++) {\n+        words[i] = UINT64_C(0);\n+    }\n+    words[endword] &= ~((~UINT64_C(0)) >> ((~end + 1) % 64));\n+}\n+\n+/*\n+ * Given a bitset containing \"length\" 64-bit words, write out the position\n+ * of all the set bits to \"out\", values start at \"base\".\n+ *\n+ * The \"out\" pointer should be sufficient to store the actual number of bits\n+ * set.\n+ *\n+ * Returns how many values were actually decoded.\n+ *\n+ * This function should only be expected to be faster than\n+ * bitset_extract_setbits\n+ * when the density of the bitset is high.\n+ *\n+ * This function uses AVX2 decoding.\n+ */\n+size_t bitset_extract_setbits_avx2(const uint64_t *words, size_t length,\n+                                   uint32_t *out, size_t outcapacity,\n+                                   uint32_t base);\n+\n+/*\n+ * Given a bitset containing \"length\" 64-bit words, write out the position\n+ * of all the set bits to \"out\", values start at \"base\".\n+ *\n+ * The \"out\" pointer should be sufficient to store the actual number of bits\n+ *set.\n+ *\n+ * Returns how many values were actually decoded.\n+ */\n+size_t bitset_extract_setbits(const uint64_t *words, size_t length,\n+                              uint32_t *out, uint32_t base);\n+\n+/*\n+ * Given a bitset containing \"length\" 64-bit words, write out the position\n+ * of all the set bits to \"out\" as 16-bit integers, values start at \"base\" (can\n+ *be set to zero)\n+ *\n+ * The \"out\" pointer should be sufficient to store the actual number of bits\n+ *set.\n+ *\n+ * Returns how many values were actually decoded.\n+ *\n+ * This function should only be expected to be faster than\n+ *bitset_extract_setbits_uint16\n+ * when the density of the bitset is high.\n+ *\n+ * This function uses SSE decoding.\n+ */\n+size_t bitset_extract_setbits_sse_uint16(const uint64_t *words, size_t length,\n+                                         uint16_t *out, size_t outcapacity,\n+                                         uint16_t base);\n+\n+/*\n+ * Given a bitset containing \"length\" 64-bit words, write out the position\n+ * of all the set bits to \"out\",  values start at \"base\"\n+ * (can be set to zero)\n+ *\n+ * The \"out\" pointer should be sufficient to store the actual number of bits\n+ *set.\n+ *\n+ * Returns how many values were actually decoded.\n+ */\n+size_t bitset_extract_setbits_uint16(const uint64_t *words, size_t length,\n+                                     uint16_t *out, uint16_t base);\n+\n+/*\n+ * Given two bitsets containing \"length\" 64-bit words, write out the position\n+ * of all the common set bits to \"out\", values start at \"base\"\n+ * (can be set to zero)\n+ *\n+ * The \"out\" pointer should be sufficient to store the actual number of bits\n+ * set.\n+ *\n+ * Returns how many values were actually decoded.\n+ */\n+size_t bitset_extract_intersection_setbits_uint16(const uint64_t * __restrict__ words1,\n+                                                  const uint64_t * __restrict__ words2,\n+                                                  size_t length, uint16_t *out,\n+                                                  uint16_t base);\n+\n+/*\n+ * Given a bitset having cardinality card, set all bit values in the list (there\n+ * are length of them)\n+ * and return the updated cardinality. This evidently assumes that the bitset\n+ * already contained data.\n+ */\n+uint64_t bitset_set_list_withcard(uint64_t *words, uint64_t card,\n+                                  const uint16_t *list, uint64_t length);\n+/*\n+ * Given a bitset, set all bit values in the list (there\n+ * are length of them).\n+ */\n+void bitset_set_list(uint64_t *words, const uint16_t *list, uint64_t length);\n+\n+/*\n+ * Given a bitset having cardinality card, unset all bit values in the list\n+ * (there are length of them)\n+ * and return the updated cardinality. This evidently assumes that the bitset\n+ * already contained data.\n+ */\n+uint64_t bitset_clear_list(uint64_t *words, uint64_t card, const uint16_t *list,\n+                           uint64_t length);\n+\n+/*\n+ * Given a bitset having cardinality card, toggle all bit values in the list\n+ * (there are length of them)\n+ * and return the updated cardinality. This evidently assumes that the bitset\n+ * already contained data.\n+ */\n+\n+uint64_t bitset_flip_list_withcard(uint64_t *words, uint64_t card,\n+                                   const uint16_t *list, uint64_t length);\n+\n+void bitset_flip_list(uint64_t *words, const uint16_t *list, uint64_t length);\n+\n+#ifdef CROARING_IS_X64\n+/***\n+ * BEGIN Harley-Seal popcount functions.\n+ */\n+CROARING_TARGET_AVX2\n+/**\n+ * Compute the population count of a 256-bit word\n+ * This is not especially fast, but it is convenient as part of other functions.\n+ */\n+static inline __m256i popcount256(__m256i v) {\n+    const __m256i lookuppos = _mm256_setr_epi8(\n+        /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2,\n+        /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3,\n+        /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3,\n+        /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4,\n+\n+        /* 0 */ 4 + 0, /* 1 */ 4 + 1, /* 2 */ 4 + 1, /* 3 */ 4 + 2,\n+        /* 4 */ 4 + 1, /* 5 */ 4 + 2, /* 6 */ 4 + 2, /* 7 */ 4 + 3,\n+        /* 8 */ 4 + 1, /* 9 */ 4 + 2, /* a */ 4 + 2, /* b */ 4 + 3,\n+        /* c */ 4 + 2, /* d */ 4 + 3, /* e */ 4 + 3, /* f */ 4 + 4);\n+    const __m256i lookupneg = _mm256_setr_epi8(\n+        /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2,\n+        /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3,\n+        /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3,\n+        /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4,\n+\n+        /* 0 */ 4 - 0, /* 1 */ 4 - 1, /* 2 */ 4 - 1, /* 3 */ 4 - 2,\n+        /* 4 */ 4 - 1, /* 5 */ 4 - 2, /* 6 */ 4 - 2, /* 7 */ 4 - 3,\n+        /* 8 */ 4 - 1, /* 9 */ 4 - 2, /* a */ 4 - 2, /* b */ 4 - 3,\n+        /* c */ 4 - 2, /* d */ 4 - 3, /* e */ 4 - 3, /* f */ 4 - 4);\n+    const __m256i low_mask = _mm256_set1_epi8(0x0f);\n+\n+    const __m256i lo = _mm256_and_si256(v, low_mask);\n+    const __m256i hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), low_mask);\n+    const __m256i popcnt1 = _mm256_shuffle_epi8(lookuppos, lo);\n+    const __m256i popcnt2 = _mm256_shuffle_epi8(lookupneg, hi);\n+    return _mm256_sad_epu8(popcnt1, popcnt2);\n+}\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+/**\n+ * Simple CSA over 256 bits\n+ */\n+static inline void CSA(__m256i *h, __m256i *l, __m256i a, __m256i b,\n+                       __m256i c) {\n+    const __m256i u = _mm256_xor_si256(a, b);\n+    *h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c));\n+    *l = _mm256_xor_si256(u, c);\n+}\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+/**\n+ * Fast Harley-Seal AVX population count function\n+ */\n+inline static uint64_t avx2_harley_seal_popcount256(const __m256i *data,\n+                                                    const uint64_t size) {\n+    __m256i total = _mm256_setzero_si256();\n+    __m256i ones = _mm256_setzero_si256();\n+    __m256i twos = _mm256_setzero_si256();\n+    __m256i fours = _mm256_setzero_si256();\n+    __m256i eights = _mm256_setzero_si256();\n+    __m256i sixteens = _mm256_setzero_si256();\n+    __m256i twosA, twosB, foursA, foursB, eightsA, eightsB;\n+\n+    const uint64_t limit = size - size % 16;\n+    uint64_t i = 0;\n+\n+    for (; i < limit; i += 16) {\n+        CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i),\n+            _mm256_lddqu_si256(data + i + 1));\n+        CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 2),\n+            _mm256_lddqu_si256(data + i + 3));\n+        CSA(&foursA, &twos, twos, twosA, twosB);\n+        CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 4),\n+            _mm256_lddqu_si256(data + i + 5));\n+        CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 6),\n+            _mm256_lddqu_si256(data + i + 7));\n+        CSA(&foursB, &twos, twos, twosA, twosB);\n+        CSA(&eightsA, &fours, fours, foursA, foursB);\n+        CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 8),\n+            _mm256_lddqu_si256(data + i + 9));\n+        CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 10),\n+            _mm256_lddqu_si256(data + i + 11));\n+        CSA(&foursA, &twos, twos, twosA, twosB);\n+        CSA(&twosA, &ones, ones, _mm256_lddqu_si256(data + i + 12),\n+            _mm256_lddqu_si256(data + i + 13));\n+        CSA(&twosB, &ones, ones, _mm256_lddqu_si256(data + i + 14),\n+            _mm256_lddqu_si256(data + i + 15));\n+        CSA(&foursB, &twos, twos, twosA, twosB);\n+        CSA(&eightsB, &fours, fours, foursA, foursB);\n+        CSA(&sixteens, &eights, eights, eightsA, eightsB);\n+\n+        total = _mm256_add_epi64(total, popcount256(sixteens));\n+    }\n+\n+    total = _mm256_slli_epi64(total, 4);  // * 16\n+    total = _mm256_add_epi64(\n+        total, _mm256_slli_epi64(popcount256(eights), 3));  // += 8 * ...\n+    total = _mm256_add_epi64(\n+        total, _mm256_slli_epi64(popcount256(fours), 2));  // += 4 * ...\n+    total = _mm256_add_epi64(\n+        total, _mm256_slli_epi64(popcount256(twos), 1));  // += 2 * ...\n+    total = _mm256_add_epi64(total, popcount256(ones));\n+    for (; i < size; i++)\n+        total =\n+            _mm256_add_epi64(total, popcount256(_mm256_lddqu_si256(data + i)));\n+\n+    return (uint64_t)(_mm256_extract_epi64(total, 0)) +\n+           (uint64_t)(_mm256_extract_epi64(total, 1)) +\n+           (uint64_t)(_mm256_extract_epi64(total, 2)) +\n+           (uint64_t)(_mm256_extract_epi64(total, 3));\n+}\n+CROARING_UNTARGET_REGION\n+\n+#define AVXPOPCNTFNC(opname, avx_intrinsic)                                    \\\n+    static inline uint64_t avx2_harley_seal_popcount256_##opname(              \\\n+        const __m256i *data1, const __m256i *data2, const uint64_t size) {     \\\n+        __m256i total = _mm256_setzero_si256();                                \\\n+        __m256i ones = _mm256_setzero_si256();                                 \\\n+        __m256i twos = _mm256_setzero_si256();                                 \\\n+        __m256i fours = _mm256_setzero_si256();                                \\\n+        __m256i eights = _mm256_setzero_si256();                               \\\n+        __m256i sixteens = _mm256_setzero_si256();                             \\\n+        __m256i twosA, twosB, foursA, foursB, eightsA, eightsB;                \\\n+        __m256i A1, A2;                                                        \\\n+        const uint64_t limit = size - size % 16;                               \\\n+        uint64_t i = 0;                                                        \\\n+        for (; i < limit; i += 16) {                                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i),                  \\\n+                               _mm256_lddqu_si256(data2 + i));                 \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1),              \\\n+                               _mm256_lddqu_si256(data2 + i + 1));             \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2),              \\\n+                               _mm256_lddqu_si256(data2 + i + 2));             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3),              \\\n+                               _mm256_lddqu_si256(data2 + i + 3));             \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursA, &twos, twos, twosA, twosB);                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4),              \\\n+                               _mm256_lddqu_si256(data2 + i + 4));             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5),              \\\n+                               _mm256_lddqu_si256(data2 + i + 5));             \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6),              \\\n+                               _mm256_lddqu_si256(data2 + i + 6));             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7),              \\\n+                               _mm256_lddqu_si256(data2 + i + 7));             \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursB, &twos, twos, twosA, twosB);                           \\\n+            CSA(&eightsA, &fours, fours, foursA, foursB);                      \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8),              \\\n+                               _mm256_lddqu_si256(data2 + i + 8));             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9),              \\\n+                               _mm256_lddqu_si256(data2 + i + 9));             \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10),             \\\n+                               _mm256_lddqu_si256(data2 + i + 10));            \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11),             \\\n+                               _mm256_lddqu_si256(data2 + i + 11));            \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursA, &twos, twos, twosA, twosB);                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12),             \\\n+                               _mm256_lddqu_si256(data2 + i + 12));            \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13),             \\\n+                               _mm256_lddqu_si256(data2 + i + 13));            \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14),             \\\n+                               _mm256_lddqu_si256(data2 + i + 14));            \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15),             \\\n+                               _mm256_lddqu_si256(data2 + i + 15));            \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursB, &twos, twos, twosA, twosB);                           \\\n+            CSA(&eightsB, &fours, fours, foursA, foursB);                      \\\n+            CSA(&sixteens, &eights, eights, eightsA, eightsB);                 \\\n+            total = _mm256_add_epi64(total, popcount256(sixteens));            \\\n+        }                                                                      \\\n+        total = _mm256_slli_epi64(total, 4);                                   \\\n+        total = _mm256_add_epi64(total,                                        \\\n+                                 _mm256_slli_epi64(popcount256(eights), 3));   \\\n+        total =                                                                \\\n+            _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \\\n+        total =                                                                \\\n+            _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1));  \\\n+        total = _mm256_add_epi64(total, popcount256(ones));                    \\\n+        for (; i < size; i++) {                                                \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i),                  \\\n+                               _mm256_lddqu_si256(data2 + i));                 \\\n+            total = _mm256_add_epi64(total, popcount256(A1));                  \\\n+        }                                                                      \\\n+        return (uint64_t)(_mm256_extract_epi64(total, 0)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 1)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 2)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 3));                     \\\n+    }                                                                          \\\n+    static inline uint64_t avx2_harley_seal_popcount256andstore_##opname(      \\\n+        const __m256i *__restrict__ data1, const __m256i *__restrict__ data2,  \\\n+        __m256i *__restrict__ out, const uint64_t size) {                      \\\n+        __m256i total = _mm256_setzero_si256();                                \\\n+        __m256i ones = _mm256_setzero_si256();                                 \\\n+        __m256i twos = _mm256_setzero_si256();                                 \\\n+        __m256i fours = _mm256_setzero_si256();                                \\\n+        __m256i eights = _mm256_setzero_si256();                               \\\n+        __m256i sixteens = _mm256_setzero_si256();                             \\\n+        __m256i twosA, twosB, foursA, foursB, eightsA, eightsB;                \\\n+        __m256i A1, A2;                                                        \\\n+        const uint64_t limit = size - size % 16;                               \\\n+        uint64_t i = 0;                                                        \\\n+        for (; i < limit; i += 16) {                                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i),                  \\\n+                               _mm256_lddqu_si256(data2 + i));                 \\\n+            _mm256_storeu_si256(out + i, A1);                                  \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 1),              \\\n+                               _mm256_lddqu_si256(data2 + i + 1));             \\\n+            _mm256_storeu_si256(out + i + 1, A2);                              \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 2),              \\\n+                               _mm256_lddqu_si256(data2 + i + 2));             \\\n+            _mm256_storeu_si256(out + i + 2, A1);                              \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 3),              \\\n+                               _mm256_lddqu_si256(data2 + i + 3));             \\\n+            _mm256_storeu_si256(out + i + 3, A2);                              \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursA, &twos, twos, twosA, twosB);                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 4),              \\\n+                               _mm256_lddqu_si256(data2 + i + 4));             \\\n+            _mm256_storeu_si256(out + i + 4, A1);                              \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 5),              \\\n+                               _mm256_lddqu_si256(data2 + i + 5));             \\\n+            _mm256_storeu_si256(out + i + 5, A2);                              \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 6),              \\\n+                               _mm256_lddqu_si256(data2 + i + 6));             \\\n+            _mm256_storeu_si256(out + i + 6, A1);                              \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 7),              \\\n+                               _mm256_lddqu_si256(data2 + i + 7));             \\\n+            _mm256_storeu_si256(out + i + 7, A2);                              \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursB, &twos, twos, twosA, twosB);                           \\\n+            CSA(&eightsA, &fours, fours, foursA, foursB);                      \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 8),              \\\n+                               _mm256_lddqu_si256(data2 + i + 8));             \\\n+            _mm256_storeu_si256(out + i + 8, A1);                              \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 9),              \\\n+                               _mm256_lddqu_si256(data2 + i + 9));             \\\n+            _mm256_storeu_si256(out + i + 9, A2);                              \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 10),             \\\n+                               _mm256_lddqu_si256(data2 + i + 10));            \\\n+            _mm256_storeu_si256(out + i + 10, A1);                             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 11),             \\\n+                               _mm256_lddqu_si256(data2 + i + 11));            \\\n+            _mm256_storeu_si256(out + i + 11, A2);                             \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursA, &twos, twos, twosA, twosB);                           \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 12),             \\\n+                               _mm256_lddqu_si256(data2 + i + 12));            \\\n+            _mm256_storeu_si256(out + i + 12, A1);                             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 13),             \\\n+                               _mm256_lddqu_si256(data2 + i + 13));            \\\n+            _mm256_storeu_si256(out + i + 13, A2);                             \\\n+            CSA(&twosA, &ones, ones, A1, A2);                                  \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 14),             \\\n+                               _mm256_lddqu_si256(data2 + i + 14));            \\\n+            _mm256_storeu_si256(out + i + 14, A1);                             \\\n+            A2 = avx_intrinsic(_mm256_lddqu_si256(data1 + i + 15),             \\\n+                               _mm256_lddqu_si256(data2 + i + 15));            \\\n+            _mm256_storeu_si256(out + i + 15, A2);                             \\\n+            CSA(&twosB, &ones, ones, A1, A2);                                  \\\n+            CSA(&foursB, &twos, twos, twosA, twosB);                           \\\n+            CSA(&eightsB, &fours, fours, foursA, foursB);                      \\\n+            CSA(&sixteens, &eights, eights, eightsA, eightsB);                 \\\n+            total = _mm256_add_epi64(total, popcount256(sixteens));            \\\n+        }                                                                      \\\n+        total = _mm256_slli_epi64(total, 4);                                   \\\n+        total = _mm256_add_epi64(total,                                        \\\n+                                 _mm256_slli_epi64(popcount256(eights), 3));   \\\n+        total =                                                                \\\n+            _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(fours), 2)); \\\n+        total =                                                                \\\n+            _mm256_add_epi64(total, _mm256_slli_epi64(popcount256(twos), 1));  \\\n+        total = _mm256_add_epi64(total, popcount256(ones));                    \\\n+        for (; i < size; i++) {                                                \\\n+            A1 = avx_intrinsic(_mm256_lddqu_si256(data1 + i),                  \\\n+                               _mm256_lddqu_si256(data2 + i));                 \\\n+            _mm256_storeu_si256(out + i, A1);                                  \\\n+            total = _mm256_add_epi64(total, popcount256(A1));                  \\\n+        }                                                                      \\\n+        return (uint64_t)(_mm256_extract_epi64(total, 0)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 1)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 2)) +                    \\\n+               (uint64_t)(_mm256_extract_epi64(total, 3));                     \\\n+    }\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC(or, _mm256_or_si256)\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC(union, _mm256_or_si256)\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC(and, _mm256_and_si256)\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC(intersection, _mm256_and_si256)\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC (xor, _mm256_xor_si256)\n+CROARING_UNTARGET_REGION\n+\n+CROARING_TARGET_AVX2\n+AVXPOPCNTFNC(andnot, _mm256_andnot_si256)\n+CROARING_UNTARGET_REGION\n+\n+/***\n+ * END Harley-Seal popcount functions.\n+ */\n+\n+#endif  // CROARING_IS_X64\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal\n+#endif\n+\n+#endif\n+/* end file include/roaring/bitset_util.h */\n+/* begin file include/roaring/containers/array.h */\n+/*\n+ * array.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_ARRAY_H_\n+#define INCLUDE_CONTAINERS_ARRAY_H_\n+\n+#include <string.h>\n+\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring {\n+\n+// Note: in pure C++ code, you should avoid putting `using` in header files\n+using api::roaring_iterator;\n+using api::roaring_iterator64;\n+\n+namespace internal {\n+#endif\n+\n+/* Containers with DEFAULT_MAX_SIZE or less integers should be arrays */\n+enum { DEFAULT_MAX_SIZE = 4096 };\n+\n+/* struct array_container - sparse representation of a bitmap\n+ *\n+ * @cardinality: number of indices in `array` (and the bitmap)\n+ * @capacity:    allocated size of `array`\n+ * @array:       sorted list of integers\n+ */\n+STRUCT_CONTAINER(array_container_s) {\n+    int32_t cardinality;\n+    int32_t capacity;\n+    uint16_t *array;\n+};\n+\n+typedef struct array_container_s array_container_t;\n+\n+#define CAST_array(c)         CAST(array_container_t *, c)  // safer downcast\n+#define const_CAST_array(c)   CAST(const array_container_t *, c)\n+#define movable_CAST_array(c) movable_CAST(array_container_t **, c)\n+\n+/* Create a new array with default. Return NULL in case of failure. See also\n+ * array_container_create_given_capacity. */\n+array_container_t *array_container_create(void);\n+\n+/* Create a new array with a specified capacity size. Return NULL in case of\n+ * failure. */\n+array_container_t *array_container_create_given_capacity(int32_t size);\n+\n+/* Create a new array containing all values in [min,max). */\n+array_container_t * array_container_create_range(uint32_t min, uint32_t max);\n+\n+/*\n+ * Shrink the capacity to the actual size, return the number of bytes saved.\n+ */\n+int array_container_shrink_to_fit(array_container_t *src);\n+\n+/* Free memory owned by `array'. */\n+void array_container_free(array_container_t *array);\n+\n+/* Duplicate container */\n+array_container_t *array_container_clone(const array_container_t *src);\n+\n+/* Get the cardinality of `array'. */\n+static inline int array_container_cardinality(const array_container_t *array) {\n+    return array->cardinality;\n+}\n+\n+static inline bool array_container_nonzero_cardinality(\n+    const array_container_t *array) {\n+    return array->cardinality > 0;\n+}\n+\n+/* Copy one container into another. We assume that they are distinct. */\n+void array_container_copy(const array_container_t *src, array_container_t *dst);\n+\n+/*  Add all the values in [min,max) (included) at a distance k*step from min.\n+    The container must have a size less or equal to DEFAULT_MAX_SIZE after this\n+   addition. */\n+void array_container_add_from_range(array_container_t *arr, uint32_t min,\n+                                    uint32_t max, uint16_t step);\n+\n+/* Set the cardinality to zero (does not release memory). */\n+static inline void array_container_clear(array_container_t *array) {\n+    array->cardinality = 0;\n+}\n+\n+static inline bool array_container_empty(const array_container_t *array) {\n+    return array->cardinality == 0;\n+}\n+\n+/* check whether the cardinality is equal to the capacity (this does not mean\n+* that it contains 1<<16 elements) */\n+static inline bool array_container_full(const array_container_t *array) {\n+    return array->cardinality == array->capacity;\n+}\n+\n+\n+/* Compute the union of `src_1' and `src_2' and write the result to `dst'\n+ * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */\n+void array_container_union(const array_container_t *src_1,\n+                           const array_container_t *src_2,\n+                           array_container_t *dst);\n+\n+/* symmetric difference, see array_container_union */\n+void array_container_xor(const array_container_t *array_1,\n+                         const array_container_t *array_2,\n+                         array_container_t *out);\n+\n+/* Computes the intersection of src_1 and src_2 and write the result to\n+ * dst. It is assumed that dst is distinct from both src_1 and src_2. */\n+void array_container_intersection(const array_container_t *src_1,\n+                                  const array_container_t *src_2,\n+                                  array_container_t *dst);\n+\n+/* Check whether src_1 and src_2 intersect. */\n+bool array_container_intersect(const array_container_t *src_1,\n+                                  const array_container_t *src_2);\n+\n+\n+/* computers the size of the intersection between two arrays.\n+ */\n+int array_container_intersection_cardinality(const array_container_t *src_1,\n+                                             const array_container_t *src_2);\n+\n+/* computes the intersection of array1 and array2 and write the result to\n+ * array1.\n+ * */\n+void array_container_intersection_inplace(array_container_t *src_1,\n+                                          const array_container_t *src_2);\n+\n+/*\n+ * Write out the 16-bit integers contained in this container as a list of 32-bit\n+ * integers using base\n+ * as the starting value (it might be expected that base has zeros in its 16\n+ * least significant bits).\n+ * The function returns the number of values written.\n+ * The caller is responsible for allocating enough memory in out.\n+ */\n+int array_container_to_uint32_array(void *vout, const array_container_t *cont,\n+                                    uint32_t base);\n+\n+/* Compute the number of runs */\n+int32_t array_container_number_of_runs(const array_container_t *ac);\n+\n+/*\n+ * Print this container using printf (useful for debugging).\n+ */\n+void array_container_printf(const array_container_t *v);\n+\n+/*\n+ * Print this container using printf as a comma-separated list of 32-bit\n+ * integers starting at base.\n+ */\n+void array_container_printf_as_uint32_array(const array_container_t *v,\n+                                            uint32_t base);\n+\n+/**\n+ * Return the serialized size in bytes of a container having cardinality \"card\".\n+ */\n+static inline int32_t array_container_serialized_size_in_bytes(int32_t card) {\n+    return card * 2 + 2;\n+}\n+\n+/**\n+ * Increase capacity to at least min.\n+ * Whether the existing data needs to be copied over depends on the \"preserve\"\n+ * parameter. If preserve is false, then the new content will be uninitialized,\n+ * otherwise the old content is copied.\n+ */\n+void array_container_grow(array_container_t *container, int32_t min,\n+                          bool preserve);\n+\n+bool array_container_iterate(const array_container_t *cont, uint32_t base,\n+                             roaring_iterator iterator, void *ptr);\n+bool array_container_iterate64(const array_container_t *cont, uint32_t base,\n+                               roaring_iterator64 iterator, uint64_t high_bits,\n+                               void *ptr);\n+\n+/**\n+ * Writes the underlying array to buf, outputs how many bytes were written.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes written should be\n+ * array_container_size_in_bytes(container).\n+ *\n+ */\n+int32_t array_container_write(const array_container_t *container, char *buf);\n+/**\n+ * Reads the instance from buf, outputs how many bytes were read.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes read should be array_container_size_in_bytes(container).\n+ * You need to provide the (known) cardinality.\n+ */\n+int32_t array_container_read(int32_t cardinality, array_container_t *container,\n+                             const char *buf);\n+\n+/**\n+ * Return the serialized size in bytes of a container (see\n+ * bitset_container_write)\n+ * This is meant to be compatible with the Java and Go versions of Roaring and\n+ * assumes\n+ * that the cardinality of the container is already known.\n+ *\n+ */\n+static inline int32_t array_container_size_in_bytes(\n+    const array_container_t *container) {\n+    return container->cardinality * sizeof(uint16_t);\n+}\n+\n+/**\n+ * Return true if the two arrays have the same content.\n+ */\n+static inline bool array_container_equals(\n+    const array_container_t *container1,\n+    const array_container_t *container2) {\n+\n+    if (container1->cardinality != container2->cardinality) {\n+        return false;\n+    }\n+    return memequals(container1->array, container2->array, container1->cardinality*2);\n+}\n+\n+/**\n+ * Return true if container1 is a subset of container2.\n+ */\n+bool array_container_is_subset(const array_container_t *container1,\n+                               const array_container_t *container2);\n+\n+/**\n+ * If the element of given rank is in this container, supposing that the first\n+ * element has rank start_rank, then the function returns true and sets element\n+ * accordingly.\n+ * Otherwise, it returns false and update start_rank.\n+ */\n+static inline bool array_container_select(const array_container_t *container,\n+                                          uint32_t *start_rank, uint32_t rank,\n+                                          uint32_t *element) {\n+    int card = array_container_cardinality(container);\n+    if (*start_rank + card <= rank) {\n+        *start_rank += card;\n+        return false;\n+    } else {\n+        *element = container->array[rank - *start_rank];\n+        return true;\n+    }\n+}\n+\n+/* Computes the  difference of array1 and array2 and write the result\n+ * to array out.\n+ * Array out does not need to be distinct from array_1\n+ */\n+void array_container_andnot(const array_container_t *array_1,\n+                            const array_container_t *array_2,\n+                            array_container_t *out);\n+\n+/* Append x to the set. Assumes that the value is larger than any preceding\n+ * values.  */\n+static inline void array_container_append(array_container_t *arr,\n+                                          uint16_t pos) {\n+    const int32_t capacity = arr->capacity;\n+\n+    if (array_container_full(arr)) {\n+        array_container_grow(arr, capacity + 1, true);\n+    }\n+\n+    arr->array[arr->cardinality++] = pos;\n+}\n+\n+/**\n+ * Add value to the set if final cardinality doesn't exceed max_cardinality.\n+ * Return code:\n+ * 1  -- value was added\n+ * 0  -- value was already present\n+ * -1 -- value was not added because cardinality would exceed max_cardinality\n+ */\n+static inline int array_container_try_add(array_container_t *arr, uint16_t value,\n+                                          int32_t max_cardinality) {\n+    const int32_t cardinality = arr->cardinality;\n+\n+    // best case, we can append.\n+    if ((array_container_empty(arr) || arr->array[cardinality - 1] < value) &&\n+        cardinality < max_cardinality) {\n+        array_container_append(arr, value);\n+        return 1;\n+    }\n+\n+    const int32_t loc = binarySearch(arr->array, cardinality, value);\n+\n+    if (loc >= 0) {\n+        return 0;\n+    } else if (cardinality < max_cardinality) {\n+        if (array_container_full(arr)) {\n+            array_container_grow(arr, arr->capacity + 1, true);\n+        }\n+        const int32_t insert_idx = -loc - 1;\n+        memmove(arr->array + insert_idx + 1, arr->array + insert_idx,\n+                (cardinality - insert_idx) * sizeof(uint16_t));\n+        arr->array[insert_idx] = value;\n+        arr->cardinality++;\n+        return 1;\n+    } else {\n+        return -1;\n+    }\n+}\n+\n+/* Add value to the set. Returns true if x was not already present.  */\n+static inline bool array_container_add(array_container_t *arr, uint16_t value) {\n+    return array_container_try_add(arr, value, INT32_MAX) == 1;\n+}\n+\n+/* Remove x from the set. Returns true if x was present.  */\n+static inline bool array_container_remove(array_container_t *arr,\n+                                          uint16_t pos) {\n+    const int32_t idx = binarySearch(arr->array, arr->cardinality, pos);\n+    const bool is_present = idx >= 0;\n+    if (is_present) {\n+        memmove(arr->array + idx, arr->array + idx + 1,\n+                (arr->cardinality - idx - 1) * sizeof(uint16_t));\n+        arr->cardinality--;\n+    }\n+\n+    return is_present;\n+}\n+\n+/* Check whether x is present.  */\n+inline bool array_container_contains(const array_container_t *arr,\n+                                     uint16_t pos) {\n+    //    return binarySearch(arr->array, arr->cardinality, pos) >= 0;\n+    // binary search with fallback to linear search for short ranges\n+    int32_t low = 0;\n+    const uint16_t * carr = (const uint16_t *) arr->array;\n+    int32_t high = arr->cardinality - 1;\n+    //    while (high - low >= 0) {\n+    while(high >= low + 16) {\n+        int32_t middleIndex = (low + high)>>1;\n+        uint16_t middleValue = carr[middleIndex];\n+        if (middleValue < pos) {\n+            low = middleIndex + 1;\n+        } else if (middleValue > pos) {\n+            high = middleIndex - 1;\n+        } else {\n+            return true;\n+        }\n+    }\n+\n+    for (int i=low; i <= high; i++) {\n+        uint16_t v = carr[i];\n+        if (v == pos) {\n+            return true;\n+        }\n+        if ( v > pos ) return false;\n+    }\n+    return false;\n+\n+}\n+\n+void array_container_offset(const array_container_t *c,\n+                            container_t **loc, container_t **hic,\n+                            uint16_t offset);\n+\n+//* Check whether a range of values from range_start (included) to range_end (excluded) is present. */\n+static inline bool array_container_contains_range(const array_container_t *arr,\n+                                                    uint32_t range_start, uint32_t range_end) {\n+\n+    const uint16_t rs_included = range_start;\n+    const uint16_t re_included = range_end - 1;\n+\n+    const uint16_t *carr = (const uint16_t *) arr->array;\n+\n+    const int32_t start = advanceUntil(carr, -1, arr->cardinality, rs_included);\n+    const int32_t end = advanceUntil(carr, start - 1, arr->cardinality, re_included);\n+\n+    return (start < arr->cardinality) && (end < arr->cardinality)\n+            && (((uint16_t)(end - start)) == re_included - rs_included)\n+            && (carr[start] == rs_included) && (carr[end] == re_included);\n+}\n+\n+/* Returns the smallest value (assumes not empty) */\n+inline uint16_t array_container_minimum(const array_container_t *arr) {\n+    if (arr->cardinality == 0) return 0;\n+    return arr->array[0];\n+}\n+\n+/* Returns the largest value (assumes not empty) */\n+inline uint16_t array_container_maximum(const array_container_t *arr) {\n+    if (arr->cardinality == 0) return 0;\n+    return arr->array[arr->cardinality - 1];\n+}\n+\n+/* Returns the number of values equal or smaller than x */\n+inline int array_container_rank(const array_container_t *arr, uint16_t x) {\n+    const int32_t idx = binarySearch(arr->array, arr->cardinality, x);\n+    const bool is_present = idx >= 0;\n+    if (is_present) {\n+        return idx + 1;\n+    } else {\n+        return -idx - 1;\n+    }\n+}\n+\n+/* Returns the index of the first value equal or smaller than x, or -1 */\n+inline int array_container_index_equalorlarger(const array_container_t *arr, uint16_t x) {\n+    const int32_t idx = binarySearch(arr->array, arr->cardinality, x);\n+    const bool is_present = idx >= 0;\n+    if (is_present) {\n+        return idx;\n+    } else {\n+        int32_t candidate = - idx - 1;\n+        if(candidate < arr->cardinality) return candidate;\n+        return -1;\n+    }\n+}\n+\n+/*\n+ * Adds all values in range [min,max] using hint:\n+ *   nvals_less is the number of array values less than $min\n+ *   nvals_greater is the number of array values greater than $max\n+ */\n+static inline void array_container_add_range_nvals(array_container_t *array,\n+                                                   uint32_t min, uint32_t max,\n+                                                   int32_t nvals_less,\n+                                                   int32_t nvals_greater) {\n+    int32_t union_cardinality = nvals_less + (max - min + 1) + nvals_greater;\n+    if (union_cardinality > array->capacity) {\n+        array_container_grow(array, union_cardinality, true);\n+    }\n+    memmove(&(array->array[union_cardinality - nvals_greater]),\n+            &(array->array[array->cardinality - nvals_greater]),\n+            nvals_greater * sizeof(uint16_t));\n+    for (uint32_t i = 0; i <= max - min; i++) {\n+        array->array[nvals_less + i] = min + i;\n+    }\n+    array->cardinality = union_cardinality;\n+}\n+\n+/**\n+ * Adds all values in range [min,max].\n+ */\n+static inline void array_container_add_range(array_container_t *array,\n+                                             uint32_t min, uint32_t max) {\n+    int32_t nvals_greater = count_greater(array->array, array->cardinality, max);\n+    int32_t nvals_less = count_less(array->array, array->cardinality - nvals_greater, min);\n+    array_container_add_range_nvals(array, min, max, nvals_less, nvals_greater);\n+}\n+\n+/*\n+ * Removes all elements array[pos] .. array[pos+count-1]\n+ */\n+static inline void array_container_remove_range(array_container_t *array,\n+                                                uint32_t pos, uint32_t count) {\n+  if (count != 0) {\n+      memmove(&(array->array[pos]), &(array->array[pos+count]),\n+              (array->cardinality - pos - count) * sizeof(uint16_t));\n+      array->cardinality -= count;\n+  }\n+}\n+\n+#ifdef __cplusplus\n+} } } // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_ARRAY_H_ */\n+/* end file include/roaring/containers/array.h */\n+/* begin file include/roaring/containers/bitset.h */\n+/*\n+ * bitset.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_BITSET_H_\n+#define INCLUDE_CONTAINERS_BITSET_H_\n+\n+#include <stdbool.h>\n+#include <stdint.h>\n+\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring {\n+\n+// Note: in pure C++ code, you should avoid putting `using` in header files\n+using api::roaring_iterator;\n+using api::roaring_iterator64;\n+\n+namespace internal {\n+#endif\n+\n+\n+\n+enum {\n+    BITSET_CONTAINER_SIZE_IN_WORDS = (1 << 16) / 64,\n+    BITSET_UNKNOWN_CARDINALITY = -1\n+};\n+\n+STRUCT_CONTAINER(bitset_container_s) {\n+    int32_t cardinality;\n+    uint64_t *words;\n+};\n+\n+typedef struct bitset_container_s bitset_container_t;\n+\n+#define CAST_bitset(c)         CAST(bitset_container_t *, c)  // safer downcast\n+#define const_CAST_bitset(c)   CAST(const bitset_container_t *, c)\n+#define movable_CAST_bitset(c) movable_CAST(bitset_container_t **, c)\n+\n+/* Create a new bitset. Return NULL in case of failure. */\n+bitset_container_t *bitset_container_create(void);\n+\n+/* Free memory. */\n+void bitset_container_free(bitset_container_t *bitset);\n+\n+/* Clear bitset (sets bits to 0). */\n+void bitset_container_clear(bitset_container_t *bitset);\n+\n+/* Set all bits to 1. */\n+void bitset_container_set_all(bitset_container_t *bitset);\n+\n+/* Duplicate bitset */\n+bitset_container_t *bitset_container_clone(const bitset_container_t *src);\n+\n+/* Set the bit in [begin,end). WARNING: as of April 2016, this method is slow\n+ * and\n+ * should not be used in performance-sensitive code. Ever.  */\n+void bitset_container_set_range(bitset_container_t *bitset, uint32_t begin,\n+                                uint32_t end);\n+\n+#if defined(CROARING_ASMBITMANIPOPTIMIZATION) && defined(__AVX2__)\n+/* Set the ith bit.  */\n+static inline void bitset_container_set(bitset_container_t *bitset,\n+                                        uint16_t pos) {\n+    uint64_t shift = 6;\n+    uint64_t offset;\n+    uint64_t p = pos;\n+    ASM_SHIFT_RIGHT(p, shift, offset);\n+    uint64_t load = bitset->words[offset];\n+    ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality);\n+    bitset->words[offset] = load;\n+}\n+\n+/* Unset the ith bit.  */\n+static inline void bitset_container_unset(bitset_container_t *bitset,\n+                                          uint16_t pos) {\n+    uint64_t shift = 6;\n+    uint64_t offset;\n+    uint64_t p = pos;\n+    ASM_SHIFT_RIGHT(p, shift, offset);\n+    uint64_t load = bitset->words[offset];\n+    ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality);\n+    bitset->words[offset] = load;\n+}\n+\n+/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower\n+ * than bitset_container_set.  */\n+static inline bool bitset_container_add(bitset_container_t *bitset,\n+                                        uint16_t pos) {\n+    uint64_t shift = 6;\n+    uint64_t offset;\n+    uint64_t p = pos;\n+    ASM_SHIFT_RIGHT(p, shift, offset);\n+    uint64_t load = bitset->words[offset];\n+    // could be possibly slightly further optimized\n+    const int32_t oldcard = bitset->cardinality;\n+    ASM_SET_BIT_INC_WAS_CLEAR(load, p, bitset->cardinality);\n+    bitset->words[offset] = load;\n+    return bitset->cardinality - oldcard;\n+}\n+\n+/* Remove `pos' from `bitset'. Returns true if `pos' was present.  Might be\n+ * slower than bitset_container_unset.  */\n+static inline bool bitset_container_remove(bitset_container_t *bitset,\n+                                           uint16_t pos) {\n+    uint64_t shift = 6;\n+    uint64_t offset;\n+    uint64_t p = pos;\n+    ASM_SHIFT_RIGHT(p, shift, offset);\n+    uint64_t load = bitset->words[offset];\n+    // could be possibly slightly further optimized\n+    const int32_t oldcard = bitset->cardinality;\n+    ASM_CLEAR_BIT_DEC_WAS_SET(load, p, bitset->cardinality);\n+    bitset->words[offset] = load;\n+    return oldcard - bitset->cardinality;\n+}\n+\n+/* Get the value of the ith bit.  */\n+inline bool bitset_container_get(const bitset_container_t *bitset,\n+                                 uint16_t pos) {\n+    uint64_t word = bitset->words[pos >> 6];\n+    const uint64_t p = pos;\n+    ASM_INPLACESHIFT_RIGHT(word, p);\n+    return word & 1;\n+}\n+\n+#else\n+\n+/* Set the ith bit.  */\n+static inline void bitset_container_set(bitset_container_t *bitset,\n+                                        uint16_t pos) {\n+    const uint64_t old_word = bitset->words[pos >> 6];\n+    const int index = pos & 63;\n+    const uint64_t new_word = old_word | (UINT64_C(1) << index);\n+    bitset->cardinality += (uint32_t)((old_word ^ new_word) >> index);\n+    bitset->words[pos >> 6] = new_word;\n+}\n+\n+/* Unset the ith bit.  */\n+static inline void bitset_container_unset(bitset_container_t *bitset,\n+                                          uint16_t pos) {\n+    const uint64_t old_word = bitset->words[pos >> 6];\n+    const int index = pos & 63;\n+    const uint64_t new_word = old_word & (~(UINT64_C(1) << index));\n+    bitset->cardinality -= (uint32_t)((old_word ^ new_word) >> index);\n+    bitset->words[pos >> 6] = new_word;\n+}\n+\n+/* Add `pos' to `bitset'. Returns true if `pos' was not present. Might be slower\n+ * than bitset_container_set.  */\n+static inline bool bitset_container_add(bitset_container_t *bitset,\n+                                        uint16_t pos) {\n+    const uint64_t old_word = bitset->words[pos >> 6];\n+    const int index = pos & 63;\n+    const uint64_t new_word = old_word | (UINT64_C(1) << index);\n+    const uint64_t increment = (old_word ^ new_word) >> index;\n+    bitset->cardinality += (uint32_t)increment;\n+    bitset->words[pos >> 6] = new_word;\n+    return increment > 0;\n+}\n+\n+/* Remove `pos' from `bitset'. Returns true if `pos' was present.  Might be\n+ * slower than bitset_container_unset.  */\n+static inline bool bitset_container_remove(bitset_container_t *bitset,\n+                                           uint16_t pos) {\n+    const uint64_t old_word = bitset->words[pos >> 6];\n+    const int index = pos & 63;\n+    const uint64_t new_word = old_word & (~(UINT64_C(1) << index));\n+    const uint64_t increment = (old_word ^ new_word) >> index;\n+    bitset->cardinality -= (uint32_t)increment;\n+    bitset->words[pos >> 6] = new_word;\n+    return increment > 0;\n+}\n+\n+/* Get the value of the ith bit.  */\n+inline bool bitset_container_get(const bitset_container_t *bitset,\n+                                 uint16_t pos) {\n+    const uint64_t word = bitset->words[pos >> 6];\n+    return (word >> (pos & 63)) & 1;\n+}\n+\n+#endif\n+\n+/*\n+* Check if all bits are set in a range of positions from pos_start (included) to\n+* pos_end (excluded).\n+*/\n+static inline bool bitset_container_get_range(const bitset_container_t *bitset,\n+                                                uint32_t pos_start, uint32_t pos_end) {\n+\n+    const uint32_t start = pos_start >> 6;\n+    const uint32_t end = pos_end >> 6;\n+\n+    const uint64_t first = ~((1ULL << (pos_start & 0x3F)) - 1);\n+    const uint64_t last = (1ULL << (pos_end & 0x3F)) - 1;\n+\n+    if (start == end) return ((bitset->words[end] & first & last) == (first & last));\n+    if ((bitset->words[start] & first) != first) return false;\n+\n+    if ((end < BITSET_CONTAINER_SIZE_IN_WORDS) && ((bitset->words[end] & last) != last)){\n+\n+        return false;\n+    }\n+\n+    for (uint16_t i = start + 1; (i < BITSET_CONTAINER_SIZE_IN_WORDS) && (i < end); ++i){\n+\n+        if (bitset->words[i] != UINT64_C(0xFFFFFFFFFFFFFFFF)) return false;\n+    }\n+\n+    return true;\n+}\n+\n+/* Check whether `bitset' is present in `array'.  Calls bitset_container_get. */\n+inline bool bitset_container_contains(const bitset_container_t *bitset,\n+                                      uint16_t pos) {\n+    return bitset_container_get(bitset, pos);\n+}\n+\n+/*\n+* Check whether a range of bits from position `pos_start' (included) to `pos_end' (excluded)\n+* is present in `bitset'.  Calls bitset_container_get_all.\n+*/\n+static inline bool bitset_container_contains_range(const bitset_container_t *bitset,\n+          uint32_t pos_start, uint32_t pos_end) {\n+    return bitset_container_get_range(bitset, pos_start, pos_end);\n+}\n+\n+/* Get the number of bits set */\n+static inline int bitset_container_cardinality(\n+    const bitset_container_t *bitset) {\n+    return bitset->cardinality;\n+}\n+\n+\n+\n+\n+/* Copy one container into another. We assume that they are distinct. */\n+void bitset_container_copy(const bitset_container_t *source,\n+                           bitset_container_t *dest);\n+\n+/*  Add all the values [min,max) at a distance k*step from min: min,\n+ * min+step,.... */\n+void bitset_container_add_from_range(bitset_container_t *bitset, uint32_t min,\n+                                     uint32_t max, uint16_t step);\n+\n+/* Get the number of bits set (force computation). This does not modify bitset.\n+ * To update the cardinality, you should do\n+ * bitset->cardinality =  bitset_container_compute_cardinality(bitset).*/\n+int bitset_container_compute_cardinality(const bitset_container_t *bitset);\n+\n+/* Get whether there is at least one bit set  (see bitset_container_empty for the reverse),\n+   when the cardinality is unknown, it is computed and stored in the struct */\n+static inline bool bitset_container_nonzero_cardinality(\n+    bitset_container_t *bitset) {\n+    // account for laziness\n+    if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) {\n+        // could bail early instead with a nonzero result\n+        bitset->cardinality = bitset_container_compute_cardinality(bitset);\n+    }\n+    return bitset->cardinality > 0;\n+}\n+\n+/* Check whether this bitset is empty (see bitset_container_nonzero_cardinality for the reverse),\n+ *  it never modifies the bitset struct. */\n+static inline bool bitset_container_empty(\n+    const bitset_container_t *bitset) {\n+  if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) {\n+      for (int i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i ++) {\n+          if((bitset->words[i]) != 0) return false;\n+      }\n+      return true;\n+  }\n+  return bitset->cardinality == 0;\n+}\n+\n+\n+/* Get whether there is at least one bit set  (see bitset_container_empty for the reverse),\n+   the bitset is never modified */\n+static inline bool bitset_container_const_nonzero_cardinality(\n+    const bitset_container_t *bitset) {\n+    return !bitset_container_empty(bitset);\n+}\n+\n+/*\n+ * Check whether the two bitsets intersect\n+ */\n+bool bitset_container_intersect(const bitset_container_t *src_1,\n+                                  const bitset_container_t *src_2);\n+\n+/* Computes the union of bitsets `src_1' and `src_2' into `dst'  and return the\n+ * cardinality. */\n+int bitset_container_or(const bitset_container_t *src_1,\n+                        const bitset_container_t *src_2,\n+                        bitset_container_t *dst);\n+\n+/* Computes the union of bitsets `src_1' and `src_2' and return the cardinality.\n+ */\n+int bitset_container_or_justcard(const bitset_container_t *src_1,\n+                                 const bitset_container_t *src_2);\n+\n+/* Computes the union of bitsets `src_1' and `src_2' into `dst' and return the\n+ * cardinality. Same as bitset_container_or. */\n+int bitset_container_union(const bitset_container_t *src_1,\n+                           const bitset_container_t *src_2,\n+                           bitset_container_t *dst);\n+\n+/* Computes the union of bitsets `src_1' and `src_2'  and return the\n+ * cardinality. Same as bitset_container_or_justcard. */\n+int bitset_container_union_justcard(const bitset_container_t *src_1,\n+                                    const bitset_container_t *src_2);\n+\n+/* Computes the union of bitsets `src_1' and `src_2' into `dst', but does not\n+ * update the cardinality. Provided to optimize chained operations. */\n+int bitset_container_or_nocard(const bitset_container_t *src_1,\n+                               const bitset_container_t *src_2,\n+                               bitset_container_t *dst);\n+\n+/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and\n+ * return the cardinality. */\n+int bitset_container_and(const bitset_container_t *src_1,\n+                         const bitset_container_t *src_2,\n+                         bitset_container_t *dst);\n+\n+/* Computes the intersection of bitsets `src_1' and `src_2'  and return the\n+ * cardinality. */\n+int bitset_container_and_justcard(const bitset_container_t *src_1,\n+                                  const bitset_container_t *src_2);\n+\n+/* Computes the intersection of bitsets `src_1' and `src_2' into `dst' and\n+ * return the cardinality. Same as bitset_container_and. */\n+int bitset_container_intersection(const bitset_container_t *src_1,\n+                                  const bitset_container_t *src_2,\n+                                  bitset_container_t *dst);\n+\n+/* Computes the intersection of bitsets `src_1' and `src_2' and return the\n+ * cardinality. Same as bitset_container_and_justcard. */\n+int bitset_container_intersection_justcard(const bitset_container_t *src_1,\n+                                           const bitset_container_t *src_2);\n+\n+/* Computes the intersection of bitsets `src_1' and `src_2' into `dst', but does\n+ * not update the cardinality. Provided to optimize chained operations. */\n+int bitset_container_and_nocard(const bitset_container_t *src_1,\n+                                const bitset_container_t *src_2,\n+                                bitset_container_t *dst);\n+\n+/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst' and\n+ * return the cardinality. */\n+int bitset_container_xor(const bitset_container_t *src_1,\n+                         const bitset_container_t *src_2,\n+                         bitset_container_t *dst);\n+\n+/* Computes the exclusive or of bitsets `src_1' and `src_2' and return the\n+ * cardinality. */\n+int bitset_container_xor_justcard(const bitset_container_t *src_1,\n+                                  const bitset_container_t *src_2);\n+\n+/* Computes the exclusive or of bitsets `src_1' and `src_2' into `dst', but does\n+ * not update the cardinality. Provided to optimize chained operations. */\n+int bitset_container_xor_nocard(const bitset_container_t *src_1,\n+                                const bitset_container_t *src_2,\n+                                bitset_container_t *dst);\n+\n+/* Computes the and not of bitsets `src_1' and `src_2' into `dst' and return the\n+ * cardinality. */\n+int bitset_container_andnot(const bitset_container_t *src_1,\n+                            const bitset_container_t *src_2,\n+                            bitset_container_t *dst);\n+\n+/* Computes the and not of bitsets `src_1' and `src_2'  and return the\n+ * cardinality. */\n+int bitset_container_andnot_justcard(const bitset_container_t *src_1,\n+                                     const bitset_container_t *src_2);\n+\n+/* Computes the and not or of bitsets `src_1' and `src_2' into `dst', but does\n+ * not update the cardinality. Provided to optimize chained operations. */\n+int bitset_container_andnot_nocard(const bitset_container_t *src_1,\n+                                   const bitset_container_t *src_2,\n+                                   bitset_container_t *dst);\n+\n+void bitset_container_offset(const bitset_container_t *c,\n+                             container_t **loc, container_t **hic,\n+                             uint16_t offset);\n+/*\n+ * Write out the 16-bit integers contained in this container as a list of 32-bit\n+ * integers using base\n+ * as the starting value (it might be expected that base has zeros in its 16\n+ * least significant bits).\n+ * The function returns the number of values written.\n+ * The caller is responsible for allocating enough memory in out.\n+ * The out pointer should point to enough memory (the cardinality times 32\n+ * bits).\n+ */\n+int bitset_container_to_uint32_array(uint32_t *out,\n+                                     const bitset_container_t *bc,\n+                                     uint32_t base);\n+\n+/*\n+ * Print this container using printf (useful for debugging).\n+ */\n+void bitset_container_printf(const bitset_container_t *v);\n+\n+/*\n+ * Print this container using printf as a comma-separated list of 32-bit\n+ * integers starting at base.\n+ */\n+void bitset_container_printf_as_uint32_array(const bitset_container_t *v,\n+                                             uint32_t base);\n+\n+/**\n+ * Return the serialized size in bytes of a container.\n+ */\n+static inline int32_t bitset_container_serialized_size_in_bytes(void) {\n+    return BITSET_CONTAINER_SIZE_IN_WORDS * 8;\n+}\n+\n+/**\n+ * Return the the number of runs.\n+ */\n+int bitset_container_number_of_runs(bitset_container_t *bc);\n+\n+bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base,\n+                              roaring_iterator iterator, void *ptr);\n+bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base,\n+                                roaring_iterator64 iterator, uint64_t high_bits,\n+                                void *ptr);\n+\n+/**\n+ * Writes the underlying array to buf, outputs how many bytes were written.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes written should be\n+ * bitset_container_size_in_bytes(container).\n+ */\n+int32_t bitset_container_write(const bitset_container_t *container, char *buf);\n+\n+/**\n+ * Reads the instance from buf, outputs how many bytes were read.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes read should be bitset_container_size_in_bytes(container).\n+ * You need to provide the (known) cardinality.\n+ */\n+int32_t bitset_container_read(int32_t cardinality,\n+                              bitset_container_t *container, const char *buf);\n+/**\n+ * Return the serialized size in bytes of a container (see\n+ * bitset_container_write).\n+ * This is meant to be compatible with the Java and Go versions of Roaring and\n+ * assumes\n+ * that the cardinality of the container is already known or can be computed.\n+ */\n+static inline int32_t bitset_container_size_in_bytes(\n+    const bitset_container_t *container) {\n+    (void)container;\n+    return BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t);\n+}\n+\n+/**\n+ * Return true if the two containers have the same content.\n+ */\n+bool bitset_container_equals(const bitset_container_t *container1,\n+                             const bitset_container_t *container2);\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+*/\n+bool bitset_container_is_subset(const bitset_container_t *container1,\n+                                const bitset_container_t *container2);\n+\n+/**\n+ * If the element of given rank is in this container, supposing that the first\n+ * element has rank start_rank, then the function returns true and sets element\n+ * accordingly.\n+ * Otherwise, it returns false and update start_rank.\n+ */\n+bool bitset_container_select(const bitset_container_t *container,\n+                             uint32_t *start_rank, uint32_t rank,\n+                             uint32_t *element);\n+\n+/* Returns the smallest value (assumes not empty) */\n+uint16_t bitset_container_minimum(const bitset_container_t *container);\n+\n+/* Returns the largest value (assumes not empty) */\n+uint16_t bitset_container_maximum(const bitset_container_t *container);\n+\n+/* Returns the number of values equal or smaller than x */\n+int bitset_container_rank(const bitset_container_t *container, uint16_t x);\n+\n+/* Returns the index of the first value equal or larger than x, or -1 */\n+int bitset_container_index_equalorlarger(const bitset_container_t *container, uint16_t x);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_BITSET_H_ */\n+/* end file include/roaring/containers/bitset.h */\n+/* begin file include/roaring/containers/run.h */\n+/*\n+ * run.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_RUN_H_\n+#define INCLUDE_CONTAINERS_RUN_H_\n+\n+#include <assert.h>\n+#include <stdbool.h>\n+#include <stdint.h>\n+#include <string.h>\n+\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring {\n+\n+// Note: in pure C++ code, you should avoid putting `using` in header files\n+using api::roaring_iterator;\n+using api::roaring_iterator64;\n+\n+namespace internal {\n+#endif\n+\n+/* struct rle16_s - run length pair\n+ *\n+ * @value:  start position of the run\n+ * @length: length of the run is `length + 1`\n+ *\n+ * An RLE pair {v, l} would represent the integers between the interval\n+ * [v, v+l+1], e.g. {3, 2} = [3, 4, 5].\n+ */\n+struct rle16_s {\n+    uint16_t value;\n+    uint16_t length;\n+};\n+\n+typedef struct rle16_s rle16_t;\n+\n+#ifdef __cplusplus\n+    #define MAKE_RLE16(val,len) \\\n+        {(uint16_t)(val), (uint16_t)(len)}  // no tagged structs until c++20\n+#else\n+    #define MAKE_RLE16(val,len) \\\n+        (rle16_t){.value = (uint16_t)(val), .length = (uint16_t)(len)}\n+#endif\n+\n+/* struct run_container_s - run container bitmap\n+ *\n+ * @n_runs:   number of rle_t pairs in `runs`.\n+ * @capacity: capacity in rle_t pairs `runs` can hold.\n+ * @runs:     pairs of rle_t.\n+ */\n+STRUCT_CONTAINER(run_container_s) {\n+    int32_t n_runs;\n+    int32_t capacity;\n+    rle16_t *runs;\n+};\n+\n+typedef struct run_container_s run_container_t;\n+\n+#define CAST_run(c)         CAST(run_container_t *, c)  // safer downcast\n+#define const_CAST_run(c)   CAST(const run_container_t *, c)\n+#define movable_CAST_run(c) movable_CAST(run_container_t **, c)\n+\n+/* Create a new run container. Return NULL in case of failure. */\n+run_container_t *run_container_create(void);\n+\n+/* Create a new run container with given capacity. Return NULL in case of\n+ * failure. */\n+run_container_t *run_container_create_given_capacity(int32_t size);\n+\n+/*\n+ * Shrink the capacity to the actual size, return the number of bytes saved.\n+ */\n+int run_container_shrink_to_fit(run_container_t *src);\n+\n+/* Free memory owned by `run'. */\n+void run_container_free(run_container_t *run);\n+\n+/* Duplicate container */\n+run_container_t *run_container_clone(const run_container_t *src);\n+\n+/*\n+ * Effectively deletes the value at index index, repacking data.\n+ */\n+static inline void recoverRoomAtIndex(run_container_t *run, uint16_t index) {\n+    memmove(run->runs + index, run->runs + (1 + index),\n+            (run->n_runs - index - 1) * sizeof(rle16_t));\n+    run->n_runs--;\n+}\n+\n+/**\n+ * Good old binary search through rle data\n+ */\n+inline int32_t interleavedBinarySearch(const rle16_t *array, int32_t lenarray,\n+                                       uint16_t ikey) {\n+    int32_t low = 0;\n+    int32_t high = lenarray - 1;\n+    while (low <= high) {\n+        int32_t middleIndex = (low + high) >> 1;\n+        uint16_t middleValue = array[middleIndex].value;\n+        if (middleValue < ikey) {\n+            low = middleIndex + 1;\n+        } else if (middleValue > ikey) {\n+            high = middleIndex - 1;\n+        } else {\n+            return middleIndex;\n+        }\n+    }\n+    return -(low + 1);\n+}\n+\n+/*\n+ * Returns index of the run which contains $ikey\n+ */\n+static inline int32_t rle16_find_run(const rle16_t *array, int32_t lenarray,\n+                                     uint16_t ikey) {\n+    int32_t low = 0;\n+    int32_t high = lenarray - 1;\n+    while (low <= high) {\n+        int32_t middleIndex = (low + high) >> 1;\n+        uint16_t min = array[middleIndex].value;\n+        uint16_t max = array[middleIndex].value + array[middleIndex].length;\n+        if (ikey > max) {\n+            low = middleIndex + 1;\n+        } else if (ikey < min) {\n+            high = middleIndex - 1;\n+        } else {\n+            return middleIndex;\n+        }\n+    }\n+    return -(low + 1);\n+}\n+\n+\n+/**\n+ * Returns number of runs which can'be be merged with the key because they\n+ * are less than the key.\n+ * Note that [5,6,7,8] can be merged with the key 9 and won't be counted.\n+ */\n+static inline int32_t rle16_count_less(const rle16_t* array, int32_t lenarray,\n+                                       uint16_t key) {\n+    if (lenarray == 0) return 0;\n+    int32_t low = 0;\n+    int32_t high = lenarray - 1;\n+    while (low <= high) {\n+        int32_t middleIndex = (low + high) >> 1;\n+        uint16_t min_value = array[middleIndex].value;\n+        uint16_t max_value = array[middleIndex].value + array[middleIndex].length;\n+        if (max_value + UINT32_C(1) < key) { // uint32 arithmetic\n+            low = middleIndex + 1;\n+        } else if (key < min_value) {\n+            high = middleIndex - 1;\n+        } else {\n+            return middleIndex;\n+        }\n+    }\n+    return low;\n+}\n+\n+static inline int32_t rle16_count_greater(const rle16_t* array, int32_t lenarray,\n+                                          uint16_t key) {\n+    if (lenarray == 0) return 0;\n+    int32_t low = 0;\n+    int32_t high = lenarray - 1;\n+    while (low <= high) {\n+        int32_t middleIndex = (low + high) >> 1;\n+        uint16_t min_value = array[middleIndex].value;\n+        uint16_t max_value = array[middleIndex].value + array[middleIndex].length;\n+        if (max_value < key) {\n+            low = middleIndex + 1;\n+        } else if (key + UINT32_C(1) < min_value) { // uint32 arithmetic\n+            high = middleIndex - 1;\n+        } else {\n+            return lenarray - (middleIndex + 1);\n+        }\n+    }\n+    return lenarray - low;\n+}\n+\n+/**\n+ * increase capacity to at least min. Whether the\n+ * existing data needs to be copied over depends on copy. If \"copy\" is false,\n+ * then the new content will be uninitialized, otherwise a copy is made.\n+ */\n+void run_container_grow(run_container_t *run, int32_t min, bool copy);\n+\n+/**\n+ * Moves the data so that we can write data at index\n+ */\n+static inline void makeRoomAtIndex(run_container_t *run, uint16_t index) {\n+    /* This function calls realloc + memmove sequentially to move by one index.\n+     * Potentially copying twice the array.\n+     */\n+    if (run->n_runs + 1 > run->capacity)\n+        run_container_grow(run, run->n_runs + 1, true);\n+    memmove(run->runs + 1 + index, run->runs + index,\n+            (run->n_runs - index) * sizeof(rle16_t));\n+    run->n_runs++;\n+}\n+\n+/* Add `pos' to `run'. Returns true if `pos' was not present. */\n+bool run_container_add(run_container_t *run, uint16_t pos);\n+\n+/* Remove `pos' from `run'. Returns true if `pos' was present. */\n+static inline bool run_container_remove(run_container_t *run, uint16_t pos) {\n+    int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos);\n+    if (index >= 0) {\n+        int32_t le = run->runs[index].length;\n+        if (le == 0) {\n+            recoverRoomAtIndex(run, (uint16_t)index);\n+        } else {\n+            run->runs[index].value++;\n+            run->runs[index].length--;\n+        }\n+        return true;\n+    }\n+    index = -index - 2;  // points to preceding value, possibly -1\n+    if (index >= 0) {    // possible match\n+        int32_t offset = pos - run->runs[index].value;\n+        int32_t le = run->runs[index].length;\n+        if (offset < le) {\n+            // need to break in two\n+            run->runs[index].length = (uint16_t)(offset - 1);\n+            // need to insert\n+            uint16_t newvalue = pos + 1;\n+            int32_t newlength = le - offset - 1;\n+            makeRoomAtIndex(run, (uint16_t)(index + 1));\n+            run->runs[index + 1].value = newvalue;\n+            run->runs[index + 1].length = (uint16_t)newlength;\n+            return true;\n+\n+        } else if (offset == le) {\n+            run->runs[index].length--;\n+            return true;\n+        }\n+    }\n+    // no match\n+    return false;\n+}\n+\n+/* Check whether `pos' is present in `run'.  */\n+inline bool run_container_contains(const run_container_t *run, uint16_t pos) {\n+    int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos);\n+    if (index >= 0) return true;\n+    index = -index - 2;  // points to preceding value, possibly -1\n+    if (index != -1) {   // possible match\n+        int32_t offset = pos - run->runs[index].value;\n+        int32_t le = run->runs[index].length;\n+        if (offset <= le) return true;\n+    }\n+    return false;\n+}\n+\n+/*\n+* Check whether all positions in a range of positions from pos_start (included)\n+* to pos_end (excluded) is present in `run'.\n+*/\n+static inline bool run_container_contains_range(const run_container_t *run,\n+                                                uint32_t pos_start, uint32_t pos_end) {\n+    uint32_t count = 0;\n+    int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos_start);\n+    if (index < 0) {\n+        index = -index - 2;\n+        if ((index == -1) || ((pos_start - run->runs[index].value) > run->runs[index].length)){\n+            return false;\n+        }\n+    }\n+    for (int32_t i = index; i < run->n_runs; ++i) {\n+        const uint32_t stop = run->runs[i].value + run->runs[i].length;\n+        if (run->runs[i].value >= pos_end) break;\n+        if (stop >= pos_end) {\n+            count += (((pos_end - run->runs[i].value) > 0) ? (pos_end - run->runs[i].value) : 0);\n+            break;\n+        }\n+        const uint32_t min = (stop - pos_start) > 0 ? (stop - pos_start) : 0;\n+        count += (min < run->runs[i].length) ? min : run->runs[i].length;\n+    }\n+    return count >= (pos_end - pos_start - 1);\n+}\n+\n+/* Get the cardinality of `run'. Requires an actual computation. */\n+int run_container_cardinality(const run_container_t *run);\n+\n+/* Card > 0?, see run_container_empty for the reverse */\n+static inline bool run_container_nonzero_cardinality(\n+    const run_container_t *run) {\n+    return run->n_runs > 0;  // runs never empty\n+}\n+\n+/* Card == 0?, see run_container_nonzero_cardinality for the reverse */\n+static inline bool run_container_empty(\n+    const run_container_t *run) {\n+    return run->n_runs == 0;  // runs never empty\n+}\n+\n+\n+\n+/* Copy one container into another. We assume that they are distinct. */\n+void run_container_copy(const run_container_t *src, run_container_t *dst);\n+\n+/* Set the cardinality to zero (does not release memory). */\n+static inline void run_container_clear(run_container_t *run) {\n+    run->n_runs = 0;\n+}\n+\n+/**\n+ * Append run described by vl to the run container, possibly merging.\n+ * It is assumed that the run would be inserted at the end of the container, no\n+ * check is made.\n+ * It is assumed that the run container has the necessary capacity: caller is\n+ * responsible for checking memory capacity.\n+ *\n+ *\n+ * This is not a safe function, it is meant for performance: use with care.\n+ */\n+static inline void run_container_append(run_container_t *run, rle16_t vl,\n+                                        rle16_t *previousrl) {\n+    const uint32_t previousend = previousrl->value + previousrl->length;\n+    if (vl.value > previousend + 1) {  // we add a new one\n+        run->runs[run->n_runs] = vl;\n+        run->n_runs++;\n+        *previousrl = vl;\n+    } else {\n+        uint32_t newend = vl.value + vl.length + UINT32_C(1);\n+        if (newend > previousend) {  // we merge\n+            previousrl->length = (uint16_t)(newend - 1 - previousrl->value);\n+            run->runs[run->n_runs - 1] = *previousrl;\n+        }\n+    }\n+}\n+\n+/**\n+ * Like run_container_append but it is assumed that the content of run is empty.\n+ */\n+static inline rle16_t run_container_append_first(run_container_t *run,\n+                                                 rle16_t vl) {\n+    run->runs[run->n_runs] = vl;\n+    run->n_runs++;\n+    return vl;\n+}\n+\n+/**\n+ * append a single value  given by val to the run container, possibly merging.\n+ * It is assumed that the value would be inserted at the end of the container,\n+ * no check is made.\n+ * It is assumed that the run container has the necessary capacity: caller is\n+ * responsible for checking memory capacity.\n+ *\n+ * This is not a safe function, it is meant for performance: use with care.\n+ */\n+static inline void run_container_append_value(run_container_t *run,\n+                                              uint16_t val,\n+                                              rle16_t *previousrl) {\n+    const uint32_t previousend = previousrl->value + previousrl->length;\n+    if (val > previousend + 1) {  // we add a new one\n+        *previousrl = MAKE_RLE16(val, 0);\n+        run->runs[run->n_runs] = *previousrl;\n+        run->n_runs++;\n+    } else if (val == previousend + 1) {  // we merge\n+        previousrl->length++;\n+        run->runs[run->n_runs - 1] = *previousrl;\n+    }\n+}\n+\n+/**\n+ * Like run_container_append_value but it is assumed that the content of run is\n+ * empty.\n+ */\n+static inline rle16_t run_container_append_value_first(run_container_t *run,\n+                                                       uint16_t val) {\n+    rle16_t newrle = MAKE_RLE16(val, 0);\n+    run->runs[run->n_runs] = newrle;\n+    run->n_runs++;\n+    return newrle;\n+}\n+\n+/* Check whether the container spans the whole chunk (cardinality = 1<<16).\n+ * This check can be done in constant time (inexpensive). */\n+static inline bool run_container_is_full(const run_container_t *run) {\n+    rle16_t vl = run->runs[0];\n+    return (run->n_runs == 1) && (vl.value == 0) && (vl.length == 0xFFFF);\n+}\n+\n+/* Compute the union of `src_1' and `src_2' and write the result to `dst'\n+ * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */\n+void run_container_union(const run_container_t *src_1,\n+                         const run_container_t *src_2, run_container_t *dst);\n+\n+/* Compute the union of `src_1' and `src_2' and write the result to `src_1' */\n+void run_container_union_inplace(run_container_t *src_1,\n+                                 const run_container_t *src_2);\n+\n+/* Compute the intersection of src_1 and src_2 and write the result to\n+ * dst. It is assumed that dst is distinct from both src_1 and src_2. */\n+void run_container_intersection(const run_container_t *src_1,\n+                                const run_container_t *src_2,\n+                                run_container_t *dst);\n+\n+/* Compute the size of the intersection of src_1 and src_2 . */\n+int run_container_intersection_cardinality(const run_container_t *src_1,\n+                                           const run_container_t *src_2);\n+\n+/* Check whether src_1 and src_2 intersect. */\n+bool run_container_intersect(const run_container_t *src_1,\n+                                const run_container_t *src_2);\n+\n+/* Compute the symmetric difference of `src_1' and `src_2' and write the result\n+ * to `dst'\n+ * It is assumed that `dst' is distinct from both `src_1' and `src_2'. */\n+void run_container_xor(const run_container_t *src_1,\n+                       const run_container_t *src_2, run_container_t *dst);\n+\n+/*\n+ * Write out the 16-bit integers contained in this container as a list of 32-bit\n+ * integers using base\n+ * as the starting value (it might be expected that base has zeros in its 16\n+ * least significant bits).\n+ * The function returns the number of values written.\n+ * The caller is responsible for allocating enough memory in out.\n+ */\n+int run_container_to_uint32_array(void *vout, const run_container_t *cont,\n+                                  uint32_t base);\n+\n+/*\n+ * Print this container using printf (useful for debugging).\n+ */\n+void run_container_printf(const run_container_t *v);\n+\n+/*\n+ * Print this container using printf as a comma-separated list of 32-bit\n+ * integers starting at base.\n+ */\n+void run_container_printf_as_uint32_array(const run_container_t *v,\n+                                          uint32_t base);\n+\n+/**\n+ * Return the serialized size in bytes of a container having \"num_runs\" runs.\n+ */\n+static inline int32_t run_container_serialized_size_in_bytes(int32_t num_runs) {\n+    return sizeof(uint16_t) +\n+           sizeof(rle16_t) * num_runs;  // each run requires 2 2-byte entries.\n+}\n+\n+bool run_container_iterate(const run_container_t *cont, uint32_t base,\n+                           roaring_iterator iterator, void *ptr);\n+bool run_container_iterate64(const run_container_t *cont, uint32_t base,\n+                             roaring_iterator64 iterator, uint64_t high_bits,\n+                             void *ptr);\n+\n+/**\n+ * Writes the underlying array to buf, outputs how many bytes were written.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes written should be run_container_size_in_bytes(container).\n+ */\n+int32_t run_container_write(const run_container_t *container, char *buf);\n+\n+/**\n+ * Reads the instance from buf, outputs how many bytes were read.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes read should be bitset_container_size_in_bytes(container).\n+ * The cardinality parameter is provided for consistency with other containers,\n+ * but\n+ * it might be effectively ignored..\n+ */\n+int32_t run_container_read(int32_t cardinality, run_container_t *container,\n+                           const char *buf);\n+\n+/**\n+ * Return the serialized size in bytes of a container (see run_container_write).\n+ * This is meant to be compatible with the Java and Go versions of Roaring.\n+ */\n+static inline int32_t run_container_size_in_bytes(\n+    const run_container_t *container) {\n+    return run_container_serialized_size_in_bytes(container->n_runs);\n+}\n+\n+/**\n+ * Return true if the two containers have the same content.\n+ */\n+static inline bool run_container_equals(const run_container_t *container1,\n+                          const run_container_t *container2) {\n+    if (container1->n_runs != container2->n_runs) {\n+        return false;\n+    }\n+    return memequals(container1->runs, container2->runs,\n+                     container1->n_runs * sizeof(rle16_t));\n+}\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+*/\n+bool run_container_is_subset(const run_container_t *container1,\n+                             const run_container_t *container2);\n+\n+/**\n+ * Used in a start-finish scan that appends segments, for XOR and NOT\n+ */\n+\n+void run_container_smart_append_exclusive(run_container_t *src,\n+                                          const uint16_t start,\n+                                          const uint16_t length);\n+\n+/**\n+* The new container consists of a single run [start,stop).\n+* It is required that stop>start, the caller is responsability for this check.\n+* It is required that stop <= (1<<16), the caller is responsability for this check.\n+* The cardinality of the created container is stop - start.\n+* Returns NULL on failure\n+*/\n+static inline run_container_t *run_container_create_range(uint32_t start,\n+                                                          uint32_t stop) {\n+    run_container_t *rc = run_container_create_given_capacity(1);\n+    if (rc) {\n+        rle16_t r;\n+        r.value = (uint16_t)start;\n+        r.length = (uint16_t)(stop - start - 1);\n+        run_container_append_first(rc, r);\n+    }\n+    return rc;\n+}\n+\n+/**\n+ * If the element of given rank is in this container, supposing that the first\n+ * element has rank start_rank, then the function returns true and sets element\n+ * accordingly.\n+ * Otherwise, it returns false and update start_rank.\n+ */\n+bool run_container_select(const run_container_t *container,\n+                          uint32_t *start_rank, uint32_t rank,\n+                          uint32_t *element);\n+\n+/* Compute the difference of src_1 and src_2 and write the result to\n+ * dst. It is assumed that dst is distinct from both src_1 and src_2. */\n+\n+void run_container_andnot(const run_container_t *src_1,\n+                          const run_container_t *src_2, run_container_t *dst);\n+\n+void run_container_offset(const run_container_t *c,\n+                         container_t **loc, container_t **hic,\n+                         uint16_t offset);\n+\n+/* Returns the smallest value (assumes not empty) */\n+inline uint16_t run_container_minimum(const run_container_t *run) {\n+    if (run->n_runs == 0) return 0;\n+    return run->runs[0].value;\n+}\n+\n+/* Returns the largest value (assumes not empty) */\n+inline uint16_t run_container_maximum(const run_container_t *run) {\n+    if (run->n_runs == 0) return 0;\n+    return run->runs[run->n_runs - 1].value + run->runs[run->n_runs - 1].length;\n+}\n+\n+/* Returns the number of values equal or smaller than x */\n+int run_container_rank(const run_container_t *arr, uint16_t x);\n+\n+/* Returns the index of the first run containing a value at least as large as x, or -1 */\n+inline int run_container_index_equalorlarger(const run_container_t *arr, uint16_t x) {\n+    int32_t index = interleavedBinarySearch(arr->runs, arr->n_runs, x);\n+    if (index >= 0) return index;\n+    index = -index - 2;  // points to preceding run, possibly -1\n+    if (index != -1) {   // possible match\n+        int32_t offset = x - arr->runs[index].value;\n+        int32_t le = arr->runs[index].length;\n+        if (offset <= le) return index;\n+    }\n+    index += 1;\n+    if(index  < arr->n_runs) {\n+      return index;\n+    }\n+    return -1;\n+}\n+\n+/*\n+ * Add all values in range [min, max] using hint.\n+ */\n+static inline void run_container_add_range_nruns(run_container_t* run,\n+                                                 uint32_t min, uint32_t max,\n+                                                 int32_t nruns_less,\n+                                                 int32_t nruns_greater) {\n+    int32_t nruns_common = run->n_runs - nruns_less - nruns_greater;\n+    if (nruns_common == 0) {\n+        makeRoomAtIndex(run, nruns_less);\n+        run->runs[nruns_less].value = min;\n+        run->runs[nruns_less].length = max - min;\n+    } else {\n+        uint32_t common_min = run->runs[nruns_less].value;\n+        uint32_t common_max = run->runs[nruns_less + nruns_common - 1].value +\n+                              run->runs[nruns_less + nruns_common - 1].length;\n+        uint32_t result_min = (common_min < min) ? common_min : min;\n+        uint32_t result_max = (common_max > max) ? common_max : max;\n+\n+        run->runs[nruns_less].value = result_min;\n+        run->runs[nruns_less].length = result_max - result_min;\n+\n+        memmove(&(run->runs[nruns_less + 1]),\n+                &(run->runs[run->n_runs - nruns_greater]),\n+                nruns_greater*sizeof(rle16_t));\n+        run->n_runs = nruns_less + 1 + nruns_greater;\n+    }\n+}\n+\n+/**\n+ * Add all values in range [min, max]\n+ */\n+static inline void run_container_add_range(run_container_t* run,\n+                                           uint32_t min, uint32_t max) {\n+    int32_t nruns_greater = rle16_count_greater(run->runs, run->n_runs, max);\n+    int32_t nruns_less = rle16_count_less(run->runs, run->n_runs - nruns_greater, min);\n+    run_container_add_range_nruns(run, min, max, nruns_less, nruns_greater);\n+}\n+\n+/**\n+ * Shifts last $count elements either left (distance < 0) or right (distance > 0)\n+ */\n+static inline void run_container_shift_tail(run_container_t* run,\n+                                            int32_t count, int32_t distance) {\n+    if (distance > 0) {\n+        if (run->capacity < count+distance) {\n+            run_container_grow(run, count+distance, true);\n+        }\n+    }\n+    int32_t srcpos = run->n_runs - count;\n+    int32_t dstpos = srcpos + distance;\n+    memmove(&(run->runs[dstpos]), &(run->runs[srcpos]), sizeof(rle16_t) * count);\n+    run->n_runs += distance;\n+}\n+\n+/**\n+ * Remove all elements in range [min, max]\n+ */\n+static inline void run_container_remove_range(run_container_t *run, uint32_t min, uint32_t max) {\n+    int32_t first = rle16_find_run(run->runs, run->n_runs, min);\n+    int32_t last = rle16_find_run(run->runs, run->n_runs, max);\n+\n+    if (first >= 0 && min > run->runs[first].value &&\n+        max < ((uint32_t)run->runs[first].value + (uint32_t)run->runs[first].length)) {\n+        // split this run into two adjacent runs\n+\n+        // right subinterval\n+        makeRoomAtIndex(run, first+1);\n+        run->runs[first+1].value = max + 1;\n+        run->runs[first+1].length = (run->runs[first].value + run->runs[first].length) - (max + 1);\n+\n+        // left subinterval\n+        run->runs[first].length = (min - 1) - run->runs[first].value;\n+\n+        return;\n+    }\n+\n+    // update left-most partial run\n+    if (first >= 0) {\n+        if (min > run->runs[first].value) {\n+            run->runs[first].length = (min - 1) - run->runs[first].value;\n+            first++;\n+        }\n+    } else {\n+        first = -first-1;\n+    }\n+\n+    // update right-most run\n+    if (last >= 0) {\n+        uint16_t run_max = run->runs[last].value + run->runs[last].length;\n+        if (run_max > max) {\n+            run->runs[last].value = max + 1;\n+            run->runs[last].length = run_max - (max + 1);\n+            last--;\n+        }\n+    } else {\n+        last = (-last-1) - 1;\n+    }\n+\n+    // remove intermediate runs\n+    if (first <= last) {\n+        run_container_shift_tail(run, run->n_runs - (last+1), -(last-first+1));\n+    }\n+}\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_RUN_H_ */\n+/* end file include/roaring/containers/run.h */\n+/* begin file include/roaring/containers/convert.h */\n+/*\n+ * convert.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_CONVERT_H_\n+#define INCLUDE_CONTAINERS_CONVERT_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Convert an array into a bitset. The input container is not freed or modified.\n+ */\n+bitset_container_t *bitset_container_from_array(const array_container_t *arr);\n+\n+/* Convert a run into a bitset. The input container is not freed or modified. */\n+bitset_container_t *bitset_container_from_run(const run_container_t *arr);\n+\n+/* Convert a run into an array. The input container is not freed or modified. */\n+array_container_t *array_container_from_run(const run_container_t *arr);\n+\n+/* Convert a bitset into an array. The input container is not freed or modified.\n+ */\n+array_container_t *array_container_from_bitset(const bitset_container_t *bits);\n+\n+/* Convert an array into a run. The input container is not freed or modified.\n+ */\n+run_container_t *run_container_from_array(const array_container_t *c);\n+\n+/* convert a run into either an array or a bitset\n+ * might free the container. This does not free the input run container. */\n+container_t *convert_to_bitset_or_array_container(\n+        run_container_t *rc, int32_t card,\n+        uint8_t *resulttype);\n+\n+/* convert containers to and from runcontainers, as is most space efficient.\n+ * The container might be freed. */\n+container_t *convert_run_optimize(\n+        container_t *c, uint8_t typecode_original,\n+        uint8_t *typecode_after);\n+\n+/* converts a run container to either an array or a bitset, IF it saves space.\n+ */\n+/* If a conversion occurs, the caller is responsible to free the original\n+ * container and\n+ * he becomes reponsible to free the new one. */\n+container_t *convert_run_to_efficient_container(\n+        run_container_t *c, uint8_t *typecode_after);\n+\n+// like convert_run_to_efficient_container but frees the old result if needed\n+container_t *convert_run_to_efficient_container_and_free(\n+        run_container_t *c, uint8_t *typecode_after);\n+\n+/**\n+ * Create new container which is a union of run container and\n+ * range [min, max]. Caller is responsible for freeing run container.\n+ */\n+container_t *container_from_run_range(\n+        const run_container_t *run,\n+        uint32_t min, uint32_t max,\n+        uint8_t *typecode_after);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_CONVERT_H_ */\n+/* end file include/roaring/containers/convert.h */\n+/* begin file include/roaring/containers/mixed_equal.h */\n+/*\n+ * mixed_equal.h\n+ *\n+ */\n+\n+#ifndef CONTAINERS_MIXED_EQUAL_H_\n+#define CONTAINERS_MIXED_EQUAL_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/**\n+ * Return true if the two containers have the same content.\n+ */\n+bool array_container_equal_bitset(const array_container_t* container1,\n+                                  const bitset_container_t* container2);\n+\n+/**\n+ * Return true if the two containers have the same content.\n+ */\n+bool run_container_equals_array(const run_container_t* container1,\n+                                const array_container_t* container2);\n+/**\n+ * Return true if the two containers have the same content.\n+ */\n+bool run_container_equals_bitset(const run_container_t* container1,\n+                                 const bitset_container_t* container2);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* CONTAINERS_MIXED_EQUAL_H_ */\n+/* end file include/roaring/containers/mixed_equal.h */\n+/* begin file include/roaring/containers/mixed_subset.h */\n+/*\n+ * mixed_subset.h\n+ *\n+ */\n+\n+#ifndef CONTAINERS_MIXED_SUBSET_H_\n+#define CONTAINERS_MIXED_SUBSET_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/**\n+ * Return true if container1 is a subset of container2.\n+ */\n+bool array_container_is_subset_bitset(const array_container_t* container1,\n+                                      const bitset_container_t* container2);\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+ */\n+bool run_container_is_subset_array(const run_container_t* container1,\n+                                   const array_container_t* container2);\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+ */\n+bool array_container_is_subset_run(const array_container_t* container1,\n+                                   const run_container_t* container2);\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+ */\n+bool run_container_is_subset_bitset(const run_container_t* container1,\n+                                    const bitset_container_t* container2);\n+\n+/**\n+* Return true if container1 is a subset of container2.\n+*/\n+bool bitset_container_is_subset_run(const bitset_container_t* container1,\n+                                    const run_container_t* container2);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* CONTAINERS_MIXED_SUBSET_H_ */\n+/* end file include/roaring/containers/mixed_subset.h */\n+/* begin file include/roaring/containers/mixed_andnot.h */\n+/*\n+ * mixed_andnot.h\n+ */\n+#ifndef INCLUDE_CONTAINERS_MIXED_ANDNOT_H_\n+#define INCLUDE_CONTAINERS_MIXED_ANDNOT_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst, a valid array container that could be the same as dst.*/\n+void array_bitset_container_andnot(const array_container_t *src_1,\n+                                   const bitset_container_t *src_2,\n+                                   array_container_t *dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * src_1 */\n+\n+void array_bitset_container_iandnot(array_container_t *src_1,\n+                                    const bitset_container_t *src_2);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst, which does not initially have a valid container.\n+ * Return true for a bitset result; false for array\n+ */\n+\n+bool bitset_array_container_andnot(\n+        const bitset_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+bool bitset_array_container_iandnot(\n+        bitset_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst. Result may be either a bitset or an array container\n+ * (returns \"result is bitset\"). dst does not initially have\n+ * any container, but becomes either a bitset container (return\n+ * result true) or an array container.\n+ */\n+\n+bool run_bitset_container_andnot(\n+        const run_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst. Result may be either a bitset or an array container\n+ * (returns \"result is bitset\"). dst does not initially have\n+ * any container, but becomes either a bitset container (return\n+ * result true) or an array container.\n+ */\n+\n+bool run_bitset_container_iandnot(\n+        run_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst. Result may be either a bitset or an array container\n+ * (returns \"result is bitset\").  dst does not initially have\n+ * any container, but becomes either a bitset container (return\n+ * result true) or an array container.\n+ */\n+\n+bool bitset_run_container_andnot(\n+        const bitset_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+bool bitset_run_container_iandnot(\n+        bitset_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any type of container.\n+ */\n+\n+int run_array_container_andnot(\n+        const run_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+int run_array_container_iandnot(\n+        run_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* dst must be a valid array container, allowed to be src_1 */\n+\n+void array_run_container_andnot(const array_container_t *src_1,\n+                                const run_container_t *src_2,\n+                                array_container_t *dst);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any kind of container.\n+ */\n+\n+void array_run_container_iandnot(array_container_t *src_1,\n+                                 const run_container_t *src_2);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any kind of container.\n+ */\n+\n+int run_run_container_andnot(\n+        const run_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+int run_run_container_iandnot(\n+        run_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/*\n+ * dst is a valid array container and may be the same as src_1\n+ */\n+\n+void array_array_container_andnot(const array_container_t *src_1,\n+                                  const array_container_t *src_2,\n+                                  array_container_t *dst);\n+\n+/* inplace array-array andnot will always be able to reuse the space of\n+ * src_1 */\n+void array_array_container_iandnot(array_container_t *src_1,\n+                                   const array_container_t *src_2);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially). Return value is\n+ * \"dst is a bitset\"\n+ */\n+\n+bool bitset_bitset_container_andnot(\n+        const bitset_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the andnot of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+bool bitset_bitset_container_iandnot(\n+        bitset_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif\n+/* end file include/roaring/containers/mixed_andnot.h */\n+/* begin file include/roaring/containers/mixed_intersection.h */\n+/*\n+ * mixed_intersection.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_\n+#define INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_\n+\n+/* These functions appear to exclude cases where the\n+ * inputs have the same type and the output is guaranteed\n+ * to have the same type as the inputs.  Eg, array intersection\n+ */\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Compute the intersection of src_1 and src_2 and write the result to\n+ * dst. It is allowed for dst to be equal to src_1. We assume that dst is a\n+ * valid container. */\n+void array_bitset_container_intersection(const array_container_t *src_1,\n+                                         const bitset_container_t *src_2,\n+                                         array_container_t *dst);\n+\n+/* Compute the size of the intersection of src_1 and src_2. */\n+int array_bitset_container_intersection_cardinality(\n+    const array_container_t *src_1, const bitset_container_t *src_2);\n+\n+\n+\n+/* Checking whether src_1 and src_2 intersect. */\n+bool array_bitset_container_intersect(const array_container_t *src_1,\n+                                         const bitset_container_t *src_2);\n+\n+/*\n+ * Compute the intersection between src_1 and src_2 and write the result\n+ * to *dst. If the return function is true, the result is a bitset_container_t\n+ * otherwise is a array_container_t. We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+bool bitset_bitset_container_intersection(const bitset_container_t *src_1,\n+                                          const bitset_container_t *src_2,\n+                                          container_t **dst);\n+\n+/* Compute the intersection between src_1 and src_2 and write the result to\n+ * dst. It is allowed for dst to be equal to src_1. We assume that dst is a\n+ * valid container. */\n+void array_run_container_intersection(const array_container_t *src_1,\n+                                      const run_container_t *src_2,\n+                                      array_container_t *dst);\n+\n+/* Compute the intersection between src_1 and src_2 and write the result to\n+ * *dst. If the result is true then the result is a bitset_container_t\n+ * otherwise is a array_container_t.\n+ * If *dst == src_2, then an in-place intersection is attempted\n+ **/\n+bool run_bitset_container_intersection(const run_container_t *src_1,\n+                                       const bitset_container_t *src_2,\n+                                       container_t **dst);\n+\n+/* Compute the size of the intersection between src_1 and src_2 . */\n+int array_run_container_intersection_cardinality(const array_container_t *src_1,\n+                                                 const run_container_t *src_2);\n+\n+/* Compute the size of the intersection  between src_1 and src_2\n+ **/\n+int run_bitset_container_intersection_cardinality(const run_container_t *src_1,\n+                                       const bitset_container_t *src_2);\n+\n+\n+/* Check that src_1 and src_2 intersect. */\n+bool array_run_container_intersect(const array_container_t *src_1,\n+                                      const run_container_t *src_2);\n+\n+/* Check that src_1 and src_2 intersect.\n+ **/\n+bool run_bitset_container_intersect(const run_container_t *src_1,\n+                                       const bitset_container_t *src_2);\n+\n+/*\n+ * Same as bitset_bitset_container_intersection except that if the output is to\n+ * be a\n+ * bitset_container_t, then src_1 is modified and no allocation is made.\n+ * If the output is to be an array_container_t, then caller is responsible\n+ * to free the container.\n+ * In all cases, the result is in *dst.\n+ */\n+bool bitset_bitset_container_intersection_inplace(\n+    bitset_container_t *src_1, const bitset_container_t *src_2,\n+    container_t **dst);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_MIXED_INTERSECTION_H_ */\n+/* end file include/roaring/containers/mixed_intersection.h */\n+/* begin file include/roaring/containers/mixed_negation.h */\n+/*\n+ * mixed_negation.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_MIXED_NEGATION_H_\n+#define INCLUDE_CONTAINERS_MIXED_NEGATION_H_\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Negation across the entire range of the container.\n+ * Compute the  negation of src  and write the result\n+ * to *dst. The complement of a\n+ * sufficiently sparse set will always be dense and a hence a bitmap\n+ * We assume that dst is pre-allocated and a valid bitset container\n+ * There can be no in-place version.\n+ */\n+void array_container_negation(const array_container_t *src,\n+                              bitset_container_t *dst);\n+\n+/* Negation across the entire range of the container\n+ * Compute the  negation of src  and write the result\n+ * to *dst.  A true return value indicates a bitset result,\n+ * otherwise the result is an array container.\n+ *  We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+bool bitset_container_negation(\n+        const bitset_container_t *src,\n+        container_t **dst);\n+\n+/* inplace version */\n+/*\n+ * Same as bitset_container_negation except that if the output is to\n+ * be a\n+ * bitset_container_t, then src is modified and no allocation is made.\n+ * If the output is to be an array_container_t, then caller is responsible\n+ * to free the container.\n+ * In all cases, the result is in *dst.\n+ */\n+bool bitset_container_negation_inplace(\n+        bitset_container_t *src,\n+        container_t **dst);\n+\n+/* Negation across the entire range of container\n+ * Compute the  negation of src  and write the result\n+ * to *dst.\n+ * Return values are the *_TYPECODES as defined * in containers.h\n+ *  We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+int run_container_negation(const run_container_t *src, container_t **dst);\n+\n+/*\n+ * Same as run_container_negation except that if the output is to\n+ * be a\n+ * run_container_t, and has the capacity to hold the result,\n+ * then src is modified and no allocation is made.\n+ * In all cases, the result is in *dst.\n+ */\n+int run_container_negation_inplace(run_container_t *src, container_t **dst);\n+\n+/* Negation across a range of the container.\n+ * Compute the  negation of src  and write the result\n+ * to *dst. Returns true if the result is a bitset container\n+ * and false for an array container.  *dst is not preallocated.\n+ */\n+bool array_container_negation_range(\n+        const array_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+/* Even when the result would fit, it is unclear how to make an\n+ * inplace version without inefficient copying.  Thus this routine\n+ * may be a wrapper for the non-in-place version\n+ */\n+bool array_container_negation_range_inplace(\n+        array_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+/* Negation across a range of the container\n+ * Compute the  negation of src  and write the result\n+ * to *dst.  A true return value indicates a bitset result,\n+ * otherwise the result is an array container.\n+ *  We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+bool bitset_container_negation_range(\n+        const bitset_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+/* inplace version */\n+/*\n+ * Same as bitset_container_negation except that if the output is to\n+ * be a\n+ * bitset_container_t, then src is modified and no allocation is made.\n+ * If the output is to be an array_container_t, then caller is responsible\n+ * to free the container.\n+ * In all cases, the result is in *dst.\n+ */\n+bool bitset_container_negation_range_inplace(\n+        bitset_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+/* Negation across a range of container\n+ * Compute the  negation of src  and write the result\n+ * to *dst.  Return values are the *_TYPECODES as defined * in containers.h\n+ *  We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+int run_container_negation_range(\n+        const run_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+/*\n+ * Same as run_container_negation except that if the output is to\n+ * be a\n+ * run_container_t, and has the capacity to hold the result,\n+ * then src is modified and no allocation is made.\n+ * In all cases, the result is in *dst.\n+ */\n+int run_container_negation_range_inplace(\n+        run_container_t *src,\n+        const int range_start, const int range_end,\n+        container_t **dst);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_MIXED_NEGATION_H_ */\n+/* end file include/roaring/containers/mixed_negation.h */\n+/* begin file include/roaring/containers/mixed_union.h */\n+/*\n+ * mixed_intersection.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_MIXED_UNION_H_\n+#define INCLUDE_CONTAINERS_MIXED_UNION_H_\n+\n+/* These functions appear to exclude cases where the\n+ * inputs have the same type and the output is guaranteed\n+ * to have the same type as the inputs.  Eg, bitset unions\n+ */\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * dst. It is allowed for src_2 to be dst.   */\n+void array_bitset_container_union(const array_container_t *src_1,\n+                                  const bitset_container_t *src_2,\n+                                  bitset_container_t *dst);\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * dst. It is allowed for src_2 to be dst.  This version does not\n+ * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY). */\n+void array_bitset_container_lazy_union(const array_container_t *src_1,\n+                                       const bitset_container_t *src_2,\n+                                       bitset_container_t *dst);\n+\n+/*\n+ * Compute the union between src_1 and src_2 and write the result\n+ * to *dst. If the return function is true, the result is a bitset_container_t\n+ * otherwise is a array_container_t. We assume that dst is not pre-allocated. In\n+ * case of failure, *dst will be NULL.\n+ */\n+bool array_array_container_union(\n+        const array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/*\n+ * Compute the union between src_1 and src_2 and write the result\n+ * to *dst if it cannot be written to src_1. If the return function is true,\n+ * the result is a bitset_container_t\n+ * otherwise is a array_container_t. When the result is an array_container_t, it\n+ * it either written to src_1 (if *dst is null) or to *dst.\n+ * If the result is a bitset_container_t and *dst is null, then there was a failure.\n+ */\n+bool array_array_container_inplace_union(\n+        array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/*\n+ * Same as array_array_container_union except that it will more eagerly produce\n+ * a bitset.\n+ */\n+bool array_array_container_lazy_union(\n+        const array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/*\n+ * Same as array_array_container_inplace_union except that it will more eagerly produce\n+ * a bitset.\n+ */\n+bool array_array_container_lazy_inplace_union(\n+        array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * dst. We assume that dst is a\n+ * valid container. The result might need to be further converted to array or\n+ * bitset container,\n+ * the caller is responsible for the eventual conversion. */\n+void array_run_container_union(const array_container_t *src_1,\n+                               const run_container_t *src_2,\n+                               run_container_t *dst);\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * src2. The result might need to be further converted to array or\n+ * bitset container,\n+ * the caller is responsible for the eventual conversion. */\n+void array_run_container_inplace_union(const array_container_t *src_1,\n+                                       run_container_t *src_2);\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * dst. It is allowed for dst to be src_2.\n+ * If run_container_is_full(src_1) is true, you must not be calling this\n+ *function.\n+ **/\n+void run_bitset_container_union(const run_container_t *src_1,\n+                                const bitset_container_t *src_2,\n+                                bitset_container_t *dst);\n+\n+/* Compute the union of src_1 and src_2 and write the result to\n+ * dst. It is allowed for dst to be src_2.  This version does not\n+ * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY).\n+ * If run_container_is_full(src_1) is true, you must not be calling this\n+ * function.\n+ * */\n+void run_bitset_container_lazy_union(const run_container_t *src_1,\n+                                     const bitset_container_t *src_2,\n+                                     bitset_container_t *dst);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif /* INCLUDE_CONTAINERS_MIXED_UNION_H_ */\n+/* end file include/roaring/containers/mixed_union.h */\n+/* begin file include/roaring/containers/mixed_xor.h */\n+/*\n+ * mixed_xor.h\n+ *\n+ */\n+\n+#ifndef INCLUDE_CONTAINERS_MIXED_XOR_H_\n+#define INCLUDE_CONTAINERS_MIXED_XOR_H_\n+\n+/* These functions appear to exclude cases where the\n+ * inputs have the same type and the output is guaranteed\n+ * to have the same type as the inputs.  Eg, bitset unions\n+ */\n+\n+/*\n+ * Java implementation (as of May 2016) for array_run, run_run\n+ * and  bitset_run don't do anything different for inplace.\n+ * (They are not truly in place.)\n+ */\n+\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).\n+ * Result is true iff dst is a bitset  */\n+bool array_bitset_container_xor(\n+        const array_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst. It is allowed for src_2 to be dst.  This version does not\n+ * update the cardinality of dst (it is set to BITSET_UNKNOWN_CARDINALITY).\n+ */\n+\n+void array_bitset_container_lazy_xor(const array_container_t *src_1,\n+                                     const bitset_container_t *src_2,\n+                                     bitset_container_t *dst);\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst (which has no container initially). Return value is\n+ * \"dst is a bitset\"\n+ */\n+\n+bool bitset_bitset_container_xor(\n+        const bitset_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst. Result may be either a bitset or an array container\n+ * (returns \"result is bitset\"). dst does not initially have\n+ * any container, but becomes either a bitset container (return\n+ * result true) or an array container.\n+ */\n+\n+bool run_bitset_container_xor(\n+        const run_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* lazy xor.  Dst is initialized and may be equal to src_2.\n+ *  Result is left as a bitset container, even if actual\n+ *  cardinality would dictate an array container.\n+ */\n+\n+void run_bitset_container_lazy_xor(const run_container_t *src_1,\n+                                   const bitset_container_t *src_2,\n+                                   bitset_container_t *dst);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any kind of container.\n+ */\n+\n+int array_run_container_xor(\n+        const array_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* dst does not initially have a valid container.  Creates either\n+ * an array or a bitset container, indicated by return code\n+ */\n+\n+bool array_array_container_xor(\n+        const array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* dst does not initially have a valid container.  Creates either\n+ * an array or a bitset container, indicated by return code.\n+ * A bitset container will not have a valid cardinality and the\n+ * container type might not be correct for the actual cardinality\n+ */\n+\n+bool array_array_container_lazy_xor(\n+        const array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+/* Dst is a valid run container. (Can it be src_2? Let's say not.)\n+ * Leaves result as run container, even if other options are\n+ * smaller.\n+ */\n+\n+void array_run_container_lazy_xor(const array_container_t *src_1,\n+                                  const run_container_t *src_2,\n+                                  run_container_t *dst);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any kind of container.\n+ */\n+\n+int run_run_container_xor(\n+        const run_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* INPLACE versions (initial implementation may not exploit all inplace\n+ * opportunities (if any...)\n+ */\n+\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst (which has no container initially).  It will modify src_1\n+ * to be dst if the result is a bitset.  Otherwise, it will\n+ * free src_1 and dst will be a new array container.  In both\n+ * cases, the caller is responsible for deallocating dst.\n+ * Returns true iff dst is a bitset  */\n+\n+bool bitset_array_container_ixor(\n+        bitset_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+bool bitset_bitset_container_ixor(\n+        bitset_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+bool array_bitset_container_ixor(\n+        array_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+/* Compute the xor of src_1 and src_2 and write the result to\n+ * dst. Result may be either a bitset or an array container\n+ * (returns \"result is bitset\"). dst does not initially have\n+ * any container, but becomes either a bitset container (return\n+ * result true) or an array container.\n+ */\n+\n+bool run_bitset_container_ixor(\n+        run_container_t *src_1, const bitset_container_t *src_2,\n+        container_t **dst);\n+\n+bool bitset_run_container_ixor(\n+        bitset_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+/* dst does not indicate a valid container initially.  Eventually it\n+ * can become any kind of container.\n+ */\n+\n+int array_run_container_ixor(\n+        array_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+int run_array_container_ixor(\n+        run_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+bool array_array_container_ixor(\n+        array_container_t *src_1, const array_container_t *src_2,\n+        container_t **dst);\n+\n+int run_run_container_ixor(\n+        run_container_t *src_1, const run_container_t *src_2,\n+        container_t **dst);\n+\n+#ifdef __cplusplus\n+} } }  // extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+#endif\n+/* end file include/roaring/containers/mixed_xor.h */\n+/* begin file include/roaring/containers/containers.h */\n+#ifndef CONTAINERS_CONTAINERS_H\n+#define CONTAINERS_CONTAINERS_H\n+\n+#include <assert.h>\n+#include <stdbool.h>\n+#include <stdio.h>\n+\n+\n+#ifdef __cplusplus\n+extern \"C\" { namespace roaring { namespace internal {\n+#endif\n+\n+// would enum be possible or better?\n+\n+/**\n+ * The switch case statements follow\n+ * BITSET_CONTAINER_TYPE -- ARRAY_CONTAINER_TYPE -- RUN_CONTAINER_TYPE\n+ * so it makes more sense to number them 1, 2, 3 (in the vague hope that the\n+ * compiler might exploit this ordering).\n+ */\n+\n+#define BITSET_CONTAINER_TYPE 1\n+#define ARRAY_CONTAINER_TYPE 2\n+#define RUN_CONTAINER_TYPE 3\n+#define SHARED_CONTAINER_TYPE 4\n+\n+/**\n+ * Macros for pairing container type codes, suitable for switch statements.\n+ * Use PAIR_CONTAINER_TYPES() for the switch, CONTAINER_PAIR() for the cases:\n+ *\n+ *     switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+ *        case CONTAINER_PAIR(BITSET,ARRAY):\n+ *        ...\n+ *     }\n+ */\n+#define PAIR_CONTAINER_TYPES(type1,type2) \\\n+    (4 * (type1) + (type2))\n+\n+#define CONTAINER_PAIR(name1,name2) \\\n+    (4 * (name1##_CONTAINER_TYPE) + (name2##_CONTAINER_TYPE))\n+\n+/**\n+ * A shared container is a wrapper around a container\n+ * with reference counting.\n+ */\n+\n+STRUCT_CONTAINER(shared_container_s) {\n+    container_t *container;\n+    uint8_t typecode;\n+    uint32_t counter;  // to be managed atomically\n+};\n+\n+typedef struct shared_container_s shared_container_t;\n+\n+#define CAST_shared(c)         CAST(shared_container_t *, c)  // safer downcast\n+#define const_CAST_shared(c)   CAST(const shared_container_t *, c)\n+#define movable_CAST_shared(c) movable_CAST(shared_container_t **, c)\n+\n+/*\n+ * With copy_on_write = true\n+ *  Create a new shared container if the typecode is not SHARED_CONTAINER_TYPE,\n+ * otherwise, increase the count\n+ * If copy_on_write = false, then clone.\n+ * Return NULL in case of failure.\n+ **/\n+container_t *get_copy_of_container(container_t *container, uint8_t *typecode,\n+                                   bool copy_on_write);\n+\n+/* Frees a shared container (actually decrement its counter and only frees when\n+ * the counter falls to zero). */\n+void shared_container_free(shared_container_t *container);\n+\n+/* extract a copy from the shared container, freeing the shared container if\n+there is just one instance left,\n+clone instances when the counter is higher than one\n+*/\n+container_t *shared_container_extract_copy(shared_container_t *container,\n+                                           uint8_t *typecode);\n+\n+/* access to container underneath */\n+static inline const container_t *container_unwrap_shared(\n+    const container_t *candidate_shared_container, uint8_t *type\n+){\n+    if (*type == SHARED_CONTAINER_TYPE) {\n+        *type = const_CAST_shared(candidate_shared_container)->typecode;\n+        assert(*type != SHARED_CONTAINER_TYPE);\n+        return const_CAST_shared(candidate_shared_container)->container;\n+    } else {\n+        return candidate_shared_container;\n+    }\n+}\n+\n+\n+/* access to container underneath */\n+static inline container_t *container_mutable_unwrap_shared(\n+    container_t *c, uint8_t *type\n+) {\n+    if (*type == SHARED_CONTAINER_TYPE) {  // the passed in container is shared\n+        *type = CAST_shared(c)->typecode;\n+        assert(*type != SHARED_CONTAINER_TYPE);\n+        return CAST_shared(c)->container;  // return the enclosed container\n+    } else {\n+        return c;  // wasn't shared, so return as-is\n+    }\n+}\n+\n+/* access to container underneath and queries its type */\n+static inline uint8_t get_container_type(\n+    const container_t *c, uint8_t type\n+){\n+    if (type == SHARED_CONTAINER_TYPE) {\n+        return const_CAST_shared(c)->typecode;\n+    } else {\n+        return type;\n+    }\n+}\n+\n+/**\n+ * Copies a container, requires a typecode. This allocates new memory, caller\n+ * is responsible for deallocation. If the container is not shared, then it is\n+ * physically cloned. Sharable containers are not cloneable.\n+ */\n+container_t *container_clone(const container_t *container, uint8_t typecode);\n+\n+/* access to container underneath, cloning it if needed */\n+static inline container_t *get_writable_copy_if_shared(\n+    container_t *c, uint8_t *type\n+){\n+    if (*type == SHARED_CONTAINER_TYPE) {  // shared, return enclosed container\n+        return shared_container_extract_copy(CAST_shared(c), type);\n+    } else {\n+        return c;  // not shared, so return as-is\n+    }\n+}\n+\n+/**\n+ * End of shared container code\n+ */\n+\n+static const char *container_names[] = {\"bitset\", \"array\", \"run\", \"shared\"};\n+static const char *shared_container_names[] = {\n+    \"bitset (shared)\", \"array (shared)\", \"run (shared)\"};\n+\n+// no matter what the initial container was, convert it to a bitset\n+// if a new container is produced, caller responsible for freeing the previous\n+// one\n+// container should not be a shared container\n+static inline bitset_container_t *container_to_bitset(\n+    container_t *c, uint8_t typecode\n+){\n+    bitset_container_t *result = NULL;\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return CAST_bitset(c);  // nothing to do\n+        case ARRAY_CONTAINER_TYPE:\n+            result = bitset_container_from_array(CAST_array(c));\n+            return result;\n+        case RUN_CONTAINER_TYPE:\n+            result = bitset_container_from_run(CAST_run(c));\n+            return result;\n+        case SHARED_CONTAINER_TYPE:\n+            assert(false);\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * Get the container name from the typecode\n+ * (unused at time of writing)\n+ */\n+static inline const char *get_container_name(uint8_t typecode) {\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return container_names[0];\n+        case ARRAY_CONTAINER_TYPE:\n+            return container_names[1];\n+        case RUN_CONTAINER_TYPE:\n+            return container_names[2];\n+        case SHARED_CONTAINER_TYPE:\n+            return container_names[3];\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return \"unknown\";\n+    }\n+}\n+\n+static inline const char *get_full_container_name(\n+    const container_t *c, uint8_t typecode\n+){\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return container_names[0];\n+        case ARRAY_CONTAINER_TYPE:\n+            return container_names[1];\n+        case RUN_CONTAINER_TYPE:\n+            return container_names[2];\n+        case SHARED_CONTAINER_TYPE:\n+            switch (const_CAST_shared(c)->typecode) {\n+                case BITSET_CONTAINER_TYPE:\n+                    return shared_container_names[0];\n+                case ARRAY_CONTAINER_TYPE:\n+                    return shared_container_names[1];\n+                case RUN_CONTAINER_TYPE:\n+                    return shared_container_names[2];\n+                default:\n+                    assert(false);\n+                    __builtin_unreachable();\n+                    return \"unknown\";\n+            }\n+            break;\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return \"unknown\";\n+    }\n+    __builtin_unreachable();\n+    return NULL;\n+}\n+\n+/**\n+ * Get the container cardinality (number of elements), requires a  typecode\n+ */\n+static inline int container_get_cardinality(\n+    const container_t *c, uint8_t typecode\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_cardinality(const_CAST_bitset(c));\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_cardinality(const_CAST_array(c));\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_cardinality(const_CAST_run(c));\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+\n+\n+// returns true if a container is known to be full. Note that a lazy bitset\n+// container\n+// might be full without us knowing\n+static inline bool container_is_full(const container_t *c, uint8_t typecode) {\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_cardinality(\n+                       const_CAST_bitset(c)) == (1 << 16);\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_cardinality(\n+                       const_CAST_array(c)) == (1 << 16);\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_is_full(const_CAST_run(c));\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+static inline int container_shrink_to_fit(\n+    container_t *c, uint8_t type\n+){\n+    c = container_mutable_unwrap_shared(c, &type);\n+    switch (type) {\n+        case BITSET_CONTAINER_TYPE:\n+            return 0;  // no shrinking possible\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_shrink_to_fit(CAST_array(c));\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_shrink_to_fit(CAST_run(c));\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+\n+/**\n+ * make a container with a run of ones\n+ */\n+/* initially always use a run container, even if an array might be\n+ * marginally\n+ * smaller */\n+static inline container_t *container_range_of_ones(\n+    uint32_t range_start, uint32_t range_end,\n+    uint8_t *result_type\n+){\n+    assert(range_end >= range_start);\n+    uint64_t cardinality =  range_end - range_start + 1;\n+    if(cardinality <= 2) {\n+      *result_type = ARRAY_CONTAINER_TYPE;\n+      return array_container_create_range(range_start, range_end);\n+    } else {\n+      *result_type = RUN_CONTAINER_TYPE;\n+      return run_container_create_range(range_start, range_end);\n+    }\n+}\n+\n+\n+/*  Create a container with all the values between in [min,max) at a\n+    distance k*step from min. */\n+static inline container_t *container_from_range(\n+    uint8_t *type, uint32_t min,\n+    uint32_t max, uint16_t step\n+){\n+    if (step == 0) return NULL;  // being paranoid\n+    if (step == 1) {\n+        return container_range_of_ones(min,max,type);\n+        // Note: the result is not always a run (need to check the cardinality)\n+        //*type = RUN_CONTAINER_TYPE;\n+        //return run_container_create_range(min, max);\n+    }\n+    int size = (max - min + step - 1) / step;\n+    if (size <= DEFAULT_MAX_SIZE) {  // array container\n+        *type = ARRAY_CONTAINER_TYPE;\n+        array_container_t *array = array_container_create_given_capacity(size);\n+        array_container_add_from_range(array, min, max, step);\n+        assert(array->cardinality == size);\n+        return array;\n+    } else {  // bitset container\n+        *type = BITSET_CONTAINER_TYPE;\n+        bitset_container_t *bitset = bitset_container_create();\n+        bitset_container_add_from_range(bitset, min, max, step);\n+        assert(bitset->cardinality == size);\n+        return bitset;\n+    }\n+}\n+\n+/**\n+ * \"repair\" the container after lazy operations.\n+ */\n+static inline container_t *container_repair_after_lazy(\n+    container_t *c, uint8_t *type\n+){\n+    c = get_writable_copy_if_shared(c, type);  // !!! unnecessary cloning\n+    container_t *result = NULL;\n+    switch (*type) {\n+        case BITSET_CONTAINER_TYPE: {\n+            bitset_container_t *bc = CAST_bitset(c);\n+            bc->cardinality = bitset_container_compute_cardinality(bc);\n+            if (bc->cardinality <= DEFAULT_MAX_SIZE) {\n+                result = array_container_from_bitset(bc);\n+                bitset_container_free(bc);\n+                *type = ARRAY_CONTAINER_TYPE;\n+                return result;\n+            }\n+            return c; }\n+        case ARRAY_CONTAINER_TYPE:\n+            return c;  // nothing to do\n+        case RUN_CONTAINER_TYPE:\n+            return convert_run_to_efficient_container_and_free(\n+                            CAST_run(c), type);\n+        case SHARED_CONTAINER_TYPE:\n+            assert(false);\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * Writes the underlying array to buf, outputs how many bytes were written.\n+ * This is meant to be byte-by-byte compatible with the Java and Go versions of\n+ * Roaring.\n+ * The number of bytes written should be\n+ * container_write(container, buf).\n+ *\n+ */\n+static inline int32_t container_write(\n+    const container_t *c, uint8_t typecode,\n+    char *buf\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_write(const_CAST_bitset(c), buf);\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_write(const_CAST_array(c), buf);\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_write(const_CAST_run(c), buf);\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * Get the container size in bytes under portable serialization (see\n+ * container_write), requires a\n+ * typecode\n+ */\n+static inline int32_t container_size_in_bytes(\n+    const container_t *c, uint8_t typecode\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_size_in_bytes(const_CAST_bitset(c));\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_size_in_bytes(const_CAST_array(c));\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_size_in_bytes(const_CAST_run(c));\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * print the container (useful for debugging), requires a  typecode\n+ */\n+void container_printf(const container_t *container, uint8_t typecode);\n+\n+/**\n+ * print the content of the container as a comma-separated list of 32-bit values\n+ * starting at base, requires a  typecode\n+ */\n+void container_printf_as_uint32_array(const container_t *container,\n+                                      uint8_t typecode, uint32_t base);\n+\n+/**\n+ * Checks whether a container is not empty, requires a  typecode\n+ */\n+static inline bool container_nonzero_cardinality(\n+    const container_t *c, uint8_t typecode\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_const_nonzero_cardinality(\n+                            const_CAST_bitset(c));\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_nonzero_cardinality(const_CAST_array(c));\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_nonzero_cardinality(const_CAST_run(c));\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * Recover memory from a container, requires a  typecode\n+ */\n+void container_free(container_t *container, uint8_t typecode);\n+\n+/**\n+ * Convert a container to an array of values, requires a  typecode as well as a\n+ * \"base\" (most significant values)\n+ * Returns number of ints added.\n+ */\n+static inline int container_to_uint32_array(\n+    uint32_t *output,\n+    const container_t *c, uint8_t typecode,\n+    uint32_t base\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_to_uint32_array(\n+                            output, const_CAST_bitset(c), base);\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_to_uint32_array(\n+                            output, const_CAST_array(c), base);\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_to_uint32_array(\n+                            output, const_CAST_run(c), base);\n+    }\n+    assert(false);\n+    __builtin_unreachable();\n+    return 0;  // unreached\n+}\n+\n+/**\n+ * Add a value to a container, requires a  typecode, fills in new_typecode and\n+ * return (possibly different) container.\n+ * This function may allocate a new container, and caller is responsible for\n+ * memory deallocation\n+ */\n+static inline container_t *container_add(\n+    container_t *c, uint16_t val,\n+    uint8_t typecode,  // !!! should be second argument?\n+    uint8_t *new_typecode\n+){\n+    c = get_writable_copy_if_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            bitset_container_set(CAST_bitset(c), val);\n+            *new_typecode = BITSET_CONTAINER_TYPE;\n+            return c;\n+        case ARRAY_CONTAINER_TYPE: {\n+            array_container_t *ac = CAST_array(c);\n+            if (array_container_try_add(ac, val, DEFAULT_MAX_SIZE) != -1) {\n+                *new_typecode = ARRAY_CONTAINER_TYPE;\n+                return ac;\n+            } else {\n+                bitset_container_t* bitset = bitset_container_from_array(ac);\n+                bitset_container_add(bitset, val);\n+                *new_typecode = BITSET_CONTAINER_TYPE;\n+                return bitset;\n+            }\n+        } break;\n+        case RUN_CONTAINER_TYPE:\n+            // per Java, no container type adjustments are done (revisit?)\n+            run_container_add(CAST_run(c), val);\n+            *new_typecode = RUN_CONTAINER_TYPE;\n+            return c;\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return NULL;\n+    }\n+}\n+\n+/**\n+ * Remove a value from a container, requires a  typecode, fills in new_typecode\n+ * and\n+ * return (possibly different) container.\n+ * This function may allocate a new container, and caller is responsible for\n+ * memory deallocation\n+ */\n+static inline container_t *container_remove(\n+    container_t *c, uint16_t val,\n+    uint8_t typecode,  // !!! should be second argument?\n+    uint8_t *new_typecode\n+){\n+    c = get_writable_copy_if_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            if (bitset_container_remove(CAST_bitset(c), val)) {\n+                int card = bitset_container_cardinality(CAST_bitset(c));\n+                if (card <= DEFAULT_MAX_SIZE) {\n+                    *new_typecode = ARRAY_CONTAINER_TYPE;\n+                    return array_container_from_bitset(CAST_bitset(c));\n+                }\n+            }\n+            *new_typecode = typecode;\n+            return c;\n+        case ARRAY_CONTAINER_TYPE:\n+            *new_typecode = typecode;\n+            array_container_remove(CAST_array(c), val);\n+            return c;\n+        case RUN_CONTAINER_TYPE:\n+            // per Java, no container type adjustments are done (revisit?)\n+            run_container_remove(CAST_run(c), val);\n+            *new_typecode = RUN_CONTAINER_TYPE;\n+            return c;\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return NULL;\n+    }\n+}\n+\n+/**\n+ * Check whether a value is in a container, requires a  typecode\n+ */\n+static inline bool container_contains(\n+    const container_t *c,\n+    uint16_t val,\n+    uint8_t typecode  // !!! should be second argument?\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_get(const_CAST_bitset(c), val);\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_contains(const_CAST_array(c), val);\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_contains(const_CAST_run(c), val);\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return false;\n+    }\n+}\n+\n+/**\n+ * Check whether a range of values from range_start (included) to range_end (excluded)\n+ * is in a container, requires a typecode\n+ */\n+static inline bool container_contains_range(\n+    const container_t *c,\n+    uint32_t range_start, uint32_t range_end,\n+    uint8_t typecode  // !!! should be second argument?\n+){\n+    c = container_unwrap_shared(c, &typecode);\n+    switch (typecode) {\n+        case BITSET_CONTAINER_TYPE:\n+            return bitset_container_get_range(const_CAST_bitset(c),\n+                                                range_start, range_end);\n+        case ARRAY_CONTAINER_TYPE:\n+            return array_container_contains_range(const_CAST_array(c),\n+                                                    range_start, range_end);\n+        case RUN_CONTAINER_TYPE:\n+            return run_container_contains_range(const_CAST_run(c),\n+                                                    range_start, range_end);\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return false;\n+    }\n+}\n+\n+/**\n+ * Returns true if the two containers have the same content. Note that\n+ * two containers having different types can be \"equal\" in this sense.\n+ */\n+static inline bool container_equals(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            return bitset_container_equals(const_CAST_bitset(c1),\n+                                           const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            return run_container_equals_bitset(const_CAST_run(c2),\n+                                               const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            return run_container_equals_bitset(const_CAST_run(c1),\n+                                               const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            // java would always return false?\n+            return array_container_equal_bitset(const_CAST_array(c2),\n+                                                const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            // java would always return false?\n+            return array_container_equal_bitset(const_CAST_array(c1),\n+                                                const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            return run_container_equals_array(const_CAST_run(c2),\n+                                              const_CAST_array(c1));\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            return run_container_equals_array(const_CAST_run(c1),\n+                                              const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            return array_container_equals(const_CAST_array(c1),\n+                                          const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            return run_container_equals(const_CAST_run(c1),\n+                                        const_CAST_run(c2));\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return false;\n+    }\n+}\n+\n+/**\n+ * Returns true if the container c1 is a subset of the container c2. Note that\n+ * c1 can be a subset of c2 even if they have a different type.\n+ */\n+static inline bool container_is_subset(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            return bitset_container_is_subset(const_CAST_bitset(c1),\n+                                              const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            return bitset_container_is_subset_run(const_CAST_bitset(c1),\n+                                                  const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            return run_container_is_subset_bitset(const_CAST_run(c1),\n+                                                  const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            return false;  // by construction, size(c1) > size(c2)\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            return array_container_is_subset_bitset(const_CAST_array(c1),\n+                                                    const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            return array_container_is_subset_run(const_CAST_array(c1),\n+                                                 const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            return run_container_is_subset_array(const_CAST_run(c1),\n+                                                 const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            return array_container_is_subset(const_CAST_array(c1),\n+                                             const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            return run_container_is_subset(const_CAST_run(c1),\n+                                           const_CAST_run(c2));\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return false;\n+    }\n+}\n+\n+// macro-izations possibilities for generic non-inplace binary-op dispatch\n+\n+/**\n+ * Compute intersection between two containers, generate a new container (having\n+ * type result_type), requires a typecode. This allocates new memory, caller\n+ * is responsible for deallocation.\n+ */\n+static inline container_t *container_and(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2,\n+    uint8_t *result_type\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    container_t *result = NULL;\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            *result_type = bitset_bitset_container_intersection(\n+                                const_CAST_bitset(c1),\n+                                const_CAST_bitset(c2), &result)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            result = array_container_create();\n+            array_container_intersection(const_CAST_array(c1),\n+                                         const_CAST_array(c2),\n+                                         CAST_array(result));\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            return result;\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            result = run_container_create();\n+            run_container_intersection(const_CAST_run(c1),\n+                                       const_CAST_run(c2),\n+                                       CAST_run(result));\n+            return convert_run_to_efficient_container_and_free(\n+                        CAST_run(result), result_type);\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            result = array_container_create();\n+            array_bitset_container_intersection(const_CAST_array(c2),\n+                                                const_CAST_bitset(c1),\n+                                                CAST_array(result));\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            result = array_container_create();\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_bitset_container_intersection(const_CAST_array(c1),\n+                                                const_CAST_bitset(c2),\n+                                                CAST_array(result));\n+            return result;\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            *result_type = run_bitset_container_intersection(\n+                                const_CAST_run(c2),\n+                                const_CAST_bitset(c1), &result)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            *result_type = run_bitset_container_intersection(\n+                                const_CAST_run(c1),\n+                                const_CAST_bitset(c2), &result)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            result = array_container_create();\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_run_container_intersection(const_CAST_array(c1),\n+                                             const_CAST_run(c2),\n+                                             CAST_array(result));\n+            return result;\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            result = array_container_create();\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_run_container_intersection(const_CAST_array(c2),\n+                                             const_CAST_run(c1),\n+                                             CAST_array(result));\n+            return result;\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return NULL;\n+    }\n+}\n+\n+/**\n+ * Compute the size of the intersection between two containers.\n+ */\n+static inline int container_and_cardinality(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            return bitset_container_and_justcard(\n+                const_CAST_bitset(c1), const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            return array_container_intersection_cardinality(\n+                const_CAST_array(c1), const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            return run_container_intersection_cardinality(\n+                const_CAST_run(c1), const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            return array_bitset_container_intersection_cardinality(\n+                const_CAST_array(c2), const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            return array_bitset_container_intersection_cardinality(\n+                const_CAST_array(c1), const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            return run_bitset_container_intersection_cardinality(\n+                const_CAST_run(c2), const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            return run_bitset_container_intersection_cardinality(\n+                const_CAST_run(c1), const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            return array_run_container_intersection_cardinality(\n+                const_CAST_array(c1), const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            return array_run_container_intersection_cardinality(\n+                const_CAST_array(c2), const_CAST_run(c1));\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return 0;\n+    }\n+}\n+\n+/**\n+ * Check whether two containers intersect.\n+ */\n+static inline bool container_intersect(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            return bitset_container_intersect(const_CAST_bitset(c1),\n+                                              const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            return array_container_intersect(const_CAST_array(c1),\n+                                             const_CAST_array(c2));\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            return run_container_intersect(const_CAST_run(c1),\n+                                           const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            return array_bitset_container_intersect(const_CAST_array(c2),\n+                                                    const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            return array_bitset_container_intersect(const_CAST_array(c1),\n+                                                    const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            return run_bitset_container_intersect(const_CAST_run(c2),\n+                                                  const_CAST_bitset(c1));\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            return run_bitset_container_intersect(const_CAST_run(c1),\n+                                                  const_CAST_bitset(c2));\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            return array_run_container_intersect(const_CAST_array(c1),\n+                                                 const_CAST_run(c2));\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            return array_run_container_intersect(const_CAST_array(c2),\n+                                                 const_CAST_run(c1));\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return 0;\n+    }\n+}\n+\n+/**\n+ * Compute intersection between two containers, with result in the first\n+ container if possible. If the returned pointer is identical to c1,\n+ then the container has been modified. If the returned pointer is different\n+ from c1, then a new container has been created and the caller is responsible\n+ for freeing it.\n+ The type of the first container may change. Returns the modified\n+ (and possibly new) container.\n+*/\n+static inline container_t *container_iand(\n+    container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2,\n+    uint8_t *result_type\n+){\n+    c1 = get_writable_copy_if_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    container_t *result = NULL;\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            *result_type =\n+                bitset_bitset_container_intersection_inplace(\n+                    CAST_bitset(c1), const_CAST_bitset(c2), &result)\n+                        ? BITSET_CONTAINER_TYPE\n+                        : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            array_container_intersection_inplace(CAST_array(c1),\n+                                                 const_CAST_array(c2));\n+            *result_type = ARRAY_CONTAINER_TYPE;\n+            return c1;\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            result = run_container_create();\n+            run_container_intersection(const_CAST_run(c1),\n+                                       const_CAST_run(c2),\n+                                       CAST_run(result));\n+            // as of January 2016, Java code used non-in-place intersection for\n+            // two runcontainers\n+            return convert_run_to_efficient_container_and_free(\n+                            CAST_run(result), result_type);\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            // c1 is a bitmap so no inplace possible\n+            result = array_container_create();\n+            array_bitset_container_intersection(const_CAST_array(c2),\n+                                                const_CAST_bitset(c1),\n+                                                CAST_array(result));\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_bitset_container_intersection(\n+                    const_CAST_array(c1), const_CAST_bitset(c2),\n+                    CAST_array(c1));  // result is allowed to be same as c1\n+            return c1;\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            // will attempt in-place computation\n+            *result_type = run_bitset_container_intersection(\n+                                const_CAST_run(c2),\n+                                const_CAST_bitset(c1), &c1)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return c1;\n+\n+        case CONTAINER_PAIR(RUN,BITSET):\n+            *result_type = run_bitset_container_intersection(\n+                                const_CAST_run(c1),\n+                                const_CAST_bitset(c2), &result)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,RUN):\n+            result = array_container_create();\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_run_container_intersection(const_CAST_array(c1),\n+                                             const_CAST_run(c2),\n+                                             CAST_array(result));\n+            return result;\n+\n+        case CONTAINER_PAIR(RUN,ARRAY):\n+            result = array_container_create();\n+            *result_type = ARRAY_CONTAINER_TYPE;  // never bitset\n+            array_run_container_intersection(const_CAST_array(c2),\n+                                             const_CAST_run(c1),\n+                                             CAST_array(result));\n+            return result;\n+\n+        default:\n+            assert(false);\n+            __builtin_unreachable();\n+            return NULL;\n+    }\n+}\n+\n+/**\n+ * Compute union between two containers, generate a new container (having type\n+ * result_type), requires a typecode. This allocates new memory, caller\n+ * is responsible for deallocation.\n+ */\n+static inline container_t *container_or(\n+    const container_t *c1, uint8_t type1,\n+    const container_t *c2, uint8_t type2,\n+    uint8_t *result_type\n+){\n+    c1 = container_unwrap_shared(c1, &type1);\n+    c2 = container_unwrap_shared(c2, &type2);\n+    container_t *result = NULL;\n+    switch (PAIR_CONTAINER_TYPES(type1, type2)) {\n+        case CONTAINER_PAIR(BITSET,BITSET):\n+            result = bitset_container_create();\n+            bitset_container_or(const_CAST_bitset(c1),\n+                                const_CAST_bitset(c2),\n+                                CAST_bitset(result));\n+            *result_type = BITSET_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,ARRAY):\n+            *result_type = array_array_container_union(\n+                                const_CAST_array(c1),\n+                                const_CAST_array(c2), &result)\n+                                    ? BITSET_CONTAINER_TYPE\n+                                    : ARRAY_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(RUN,RUN):\n+            result = run_container_create();\n+            run_container_union(const_CAST_run(c1),\n+                                const_CAST_run(c2),\n+                                CAST_run(result));\n+            *result_type = RUN_CONTAINER_TYPE;\n+            // todo: could be optimized since will never convert to array\n+            result = convert_run_to_efficient_container_and_free(\n+                            CAST_run(result), result_type);\n+            return result;\n+\n+        case CONTAINER_PAIR(BITSET,ARRAY):\n+            result = bitset_container_create();\n+            array_bitset_container_union(const_CAST_array(c2),\n+                                         const_CAST_bitset(c1),\n+                                         CAST_bitset(result));\n+            *result_type = BITSET_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(ARRAY,BITSET):\n+            result = bitset_container_create();\n+            array_bitset_container_union(const_CAST_array(c1),\n+                                         const_CAST_bitset(c2),\n+                                         CAST_bitset(result));\n+            *result_type = BITSET_CONTAINER_TYPE;\n+            return result;\n+\n+        case CONTAINER_PAIR(BITSET,RUN):\n+            if (run_container_is_full(const_CAST_run(c2))) {\n+                result = run_container_create();\n+                *result_type = RUN_CONTAINER_TYPE;\n+                run_container_copy(const_CAST_run(c2),\n+                                   CAST_run(result));"},{"id":"463206","messageId":"683b0c625418278e4775978cb663edd03a113faa.1663609660.git.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"[PATCH 4/5] roaring: introduce a new config option for roaring bitmaps","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:38Z","receivedAt":"2022-09-19T17:48:04Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n\nThough Git can write roaring bitmaps, there is still no way (e.g.\nconfigurations) to control the writing of roaring bitmaps.\n\nIntroduce `pack.useroaringbitmap` option to control the writing of\nroaring bitmaps.\n\nMentored-by: Taylor Blau <me@ttaylorr.com>\nMentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\nSigned-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n---\n builtin/multi-pack-index.c | 5 +++++\n builtin/pack-objects.c     | 7 ++++++-\n midx.c                     | 7 +++++++\n midx.h                     | 1 +\n pack-bitmap.h              | 7 +++----\n 5 files changed, 22 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c\nindex 9b126d6ce0e..9e221dd7cc9 100644\n--- a/builtin/multi-pack-index.c\n+++ b/builtin/multi-pack-index.c\n@@ -80,6 +80,11 @@ static struct option *add_common_options(struct option *prev)\n static int git_multi_pack_index_write_config(const char *var, const char *value,\n \t\t\t\t\t     void *cb UNUSED)\n {\n+\tif (!strcmp(var, \"pack.useroaringbitmap\")) {\n+\t\tif (git_config_bool(var, value))\n+\t\t\topts.flags |= MIDX_WRITE_ROARING_BITMAP;\n+\t}\n+\n \tif (!strcmp(var, \"pack.writebitmaphashcache\")) {\n \t\tif (git_config_bool(var, value))\n \t\t\topts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 3658c05cafc..439c5572c18 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -228,7 +228,7 @@ static enum {\n \tWRITE_BITMAP_QUIET,\n \tWRITE_BITMAP_TRUE,\n } write_bitmap_index;\n-static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE;\n+static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE | BITMAP_SET_EWAH_BITMAP;\n \n static int exclude_promisor_objects;\n \n@@ -1258,6 +1258,7 @@ static void write_pack_file(void)\n \t\t\t\t    hash_to_hex(hash));\n \n \t\t\tif (write_bitmap_index) {\n+\t\t\t\tbitmap_writer_init_bm_type(write_bitmap_options);\n \t\t\t\tbitmap_writer_set_checksum(hash);\n \t\t\t\tbitmap_writer_build_type_index(\n \t\t\t\t\t&to_pack, written_list, nr_written);\n@@ -3143,6 +3144,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tcache_max_small_delta_size = git_config_int(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"pack.useroaringbitmap\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\twrite_bitmap_options |= BITMAP_SET_ROARING_BITMAP;\n+\t}\n \tif (!strcmp(k, \"pack.writebitmaphashcache\")) {\n \t\tif (git_config_bool(k, v))\n \t\t\twrite_bitmap_options |= BITMAP_OPT_HASH_CACHE;\ndiff --git a/midx.c b/midx.c\nindex c27d0e5f151..b80db2239a8 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1112,10 +1112,16 @@ static int write_midx_bitmap(const char *midx_name,\n {\n \tint ret, i;\n \tuint16_t options = 0;\n+\tunsigned version = 0;\n \tstruct pack_idx_entry **index;\n \tchar *bitmap_name = xstrfmt(\"%s-%s.bitmap\", midx_name,\n \t\t\t\t\thash_to_hex(midx_hash));\n \n+\tif (flags & MIDX_WRITE_ROARING_BITMAP)\n+\t\tversion |= BITMAP_SET_ROARING_BITMAP;\n+\telse\n+\t\tversion |= BITMAP_SET_EWAH_BITMAP;\n+\n \tif (flags & MIDX_WRITE_BITMAP_HASH_CACHE)\n \t\toptions |= BITMAP_OPT_HASH_CACHE;\n \n@@ -1131,6 +1137,7 @@ static int write_midx_bitmap(const char *midx_name,\n \tfor (i = 0; i < pdata->nr_objects; i++)\n \t\tindex[i] = &pdata->objects[i].idx;\n \n+\tbitmap_writer_init_bm_type(version);\n \tbitmap_writer_show_progress(flags & MIDX_PROGRESS);\n \tbitmap_writer_build_type_index(pdata, index, pdata->nr_objects);\n \ndiff --git a/midx.h b/midx.h\nindex 5578cd7b835..c0b19b93c9c 100644\n--- a/midx.h\n+++ b/midx.h\n@@ -48,6 +48,7 @@ struct multi_pack_index {\n #define MIDX_WRITE_BITMAP (1 << 2)\n #define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3)\n #define MIDX_WRITE_BITMAP_LOOKUP_TABLE (1 << 4)\n+#define MIDX_WRITE_ROARING_BITMAP (1 << 5)\n \n const unsigned char *get_midx_checksum(struct multi_pack_index *m);\n void get_midx_filename(struct strbuf *out, const char *object_dir);\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex 7d71deca023..6103e0d57e7 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -30,9 +30,6 @@ struct bitmap_disk_header {\n \n #define NEEDS_BITMAP (1u<<22)\n \n-#define BITMAP_SET_EWAH_BITMAP 0x1\n-#define BITMAP_SET_ROARING_BITMAP (1 << 1)\n-\n /*\n  * The width in bytes of a single triplet in the lookup table\n  * extension:\n@@ -44,7 +41,9 @@ struct bitmap_disk_header {\n \n enum pack_bitmap_opts {\n \tBITMAP_OPT_FULL_DAG = 0x1,\n-\tBITMAP_OPT_HASH_CACHE = 0x4,\n+\tBITMAP_SET_EWAH_BITMAP = 0x2,\n+\tBITMAP_SET_ROARING_BITMAP = 0x4,\n+\tBITMAP_OPT_HASH_CACHE = 0x8,\n \tBITMAP_OPT_LOOKUP_TABLE = 0x10,\n };\n \n-- \ngitgitgadget\n\n"},{"id":"463207","messageId":"38ec2360f4fbfe65fa2d9f1e9cfb7d4944d1714f.1663609659.git.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"[PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:36Z","receivedAt":"2022-09-19T17:48:06Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n\nThough the Roaring library is introduced in previous commit, the library\ncannot be used as is. One reason is that the library doesn't support Big\nendian machines. Besides, Git specific file related functions does use\n`hashwrite()` (or similar). So there is a need to modify the library.\n\nImplement and modify new functions so that Git can actually use the\nlibrary.\n\nMentored-by: Taylor Blau <me@ttaylorr.com>\nMentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\nSigned-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n---\n roaring/roaring.c | 565 ++++++++++++++++++++++++++++++++++++++++++++--\n roaring/roaring.h |  17 ++\n 2 files changed, 562 insertions(+), 20 deletions(-)\n\ndiff --git a/roaring/roaring.c b/roaring/roaring.c\nindex df2d90544cd..ee44de20996 100644\n--- a/roaring/roaring.c\n+++ b/roaring/roaring.c\n@@ -1791,6 +1791,10 @@ bool array_container_iterate64(const array_container_t *cont, uint32_t base,\n  *\n  */\n int32_t array_container_write(const array_container_t *container, char *buf);\n+\n+int array_container_network_write(const array_container_t *container,\n+\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t  void *data);\n /**\n  * Reads the instance from buf, outputs how many bytes were read.\n  * This is meant to be byte-by-byte compatible with the Java and Go versions of\n@@ -1801,6 +1805,9 @@ int32_t array_container_write(const array_container_t *container, char *buf);\n int32_t array_container_read(int32_t cardinality, array_container_t *container,\n                              const char *buf);\n \n+int32_t array_container_network_read(int32_t cardinality, array_container_t *container,\n+                        \t     const char *buf);\n+\n /**\n  * Return the serialized size in bytes of a container (see\n  * bitset_container_write)\n@@ -2506,6 +2513,10 @@ bool bitset_container_iterate64(const bitset_container_t *cont, uint32_t base,\n  */\n int32_t bitset_container_write(const bitset_container_t *container, char *buf);\n \n+int bitset_container_network_write(const bitset_container_t *container,\n+\t\t\t\t   int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t   void *data);\n+\n /**\n  * Reads the instance from buf, outputs how many bytes were read.\n  * This is meant to be byte-by-byte compatible with the Java and Go versions of\n@@ -2515,6 +2526,9 @@ int32_t bitset_container_write(const bitset_container_t *container, char *buf);\n  */\n int32_t bitset_container_read(int32_t cardinality,\n                               bitset_container_t *container, const char *buf);\n+\n+int32_t bitset_container_network_read(int32_t cardinality, bitset_container_t *container,\n+\t\t\t\t      const char *buf);\n /**\n  * Return the serialized size in bytes of a container (see\n  * bitset_container_write).\n@@ -3029,6 +3043,10 @@ bool run_container_iterate64(const run_container_t *cont, uint32_t base,\n  */\n int32_t run_container_write(const run_container_t *container, char *buf);\n \n+int run_container_network_write(const run_container_t *container,\n+\t\t\t\tint (*write_fn) (void *, const void*, size_t),\n+\t\t\t\tvoid *data);\n+\n /**\n  * Reads the instance from buf, outputs how many bytes were read.\n  * This is meant to be byte-by-byte compatible with the Java and Go versions of\n@@ -3041,6 +3059,9 @@ int32_t run_container_write(const run_container_t *container, char *buf);\n int32_t run_container_read(int32_t cardinality, run_container_t *container,\n                            const char *buf);\n \n+int32_t run_container_network_read(int32_t cardinality, run_container_t *container,\n+                        \t   const char *buf);\n+\n /**\n  * Return the serialized size in bytes of a container (see run_container_write).\n  * This is meant to be compatible with the Java and Go versions of Roaring.\n@@ -4513,6 +4534,24 @@ static inline int32_t container_write(\n     return 0;  // unreached\n }\n \n+static int container_network_write(const container_t *c, uint8_t typecode,\n+\t\t\t\t   int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t   void *data)\n+{\n+\tc = container_unwrap_shared(c, &typecode);\n+\tswitch (typecode) {\n+\t\tcase BITSET_CONTAINER_TYPE:\n+\t\t\treturn bitset_container_network_write(const_CAST_bitset(c), write_fn, data);\n+\t\tcase ARRAY_CONTAINER_TYPE:\n+\t\t\treturn array_container_network_write(const_CAST_array(c), write_fn, data);\n+\t\tcase RUN_CONTAINER_TYPE:\n+\t\t\treturn run_container_network_write(const_CAST_run(c), write_fn, data);\n+\t}\n+\tassert(false);\n+\t__builtin_unreachable();\n+\treturn 0;\n+}\n+\n /**\n  * Get the container size in bytes under portable serialization (see\n  * container_write), requires a\n@@ -6609,6 +6648,7 @@ static inline container_t *container_remove_range(\n #include <assert.h>\n #include <stdbool.h>\n #include <stdint.h>\n+#include <arpa/inet.h>\n \n \n #ifdef __cplusplus\n@@ -6811,6 +6851,10 @@ bool ra_range_uint32_array(const roaring_array_t *ra, size_t offset, size_t limi\n  */\n size_t ra_portable_serialize(const roaring_array_t *ra, char *buf);\n \n+int ra_portable_network_serialize(const roaring_array_t *ra,\n+\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t  void *data);\n+\n /**\n  * read a bitmap from a serialized version. This is meant to be compatible\n  * with the Java and Go versions.\n@@ -7421,15 +7465,6 @@ void ra_append_range(roaring_array_t *ra, roaring_array_t *sa,\n     }\n }\n \n-container_t *ra_get_container(\n-    roaring_array_t *ra, uint16_t x, uint8_t *typecode\n-){\n-    int i = binarySearch(ra->keys, (int32_t)ra->size, x);\n-    if (i < 0) return NULL;\n-    *typecode = ra->typecodes[i];\n-    return ra->containers[i];\n-}\n-\n extern inline container_t *ra_get_container_at_index(\n     const roaring_array_t *ra, uint16_t i,\n     uint8_t *typecode);\n@@ -7670,6 +7705,18 @@ uint32_t ra_portable_header_size(const roaring_array_t *ra) {\n     }\n }\n \n+static uint32_t ra_portable_network_header_size(const roaring_array_t *ra)\n+{\n+\tif (ra_has_run_container(ra)) {\n+\t\tif (ra->size < NO_OFFSET_THRESHOLD) // for small bitmaps, we omit the offsets\n+\t\t\treturn 4 + (ra->size + 7) / 8 + 4 * ra->size;\n+\n+\t\treturn 4 + (ra->size + 7) / 8 + 8 * ra->size;\n+\t} else {\n+\t\treturn 4 + 8 * ra->size;\n+\t}\n+}\n+\n size_t ra_portable_size_in_bytes(const roaring_array_t *ra) {\n     size_t count = ra_portable_header_size(ra);\n \n@@ -7679,6 +7726,15 @@ size_t ra_portable_size_in_bytes(const roaring_array_t *ra) {\n     return count;\n }\n \n+static size_t ra_portable_network_size_in_bytes(const roaring_array_t *ra)\n+{\n+\tsize_t count = ra_portable_network_header_size(ra);\n+\n+\tfor (int32_t k = 0; k < ra->size; ++k)\n+\t\tcount += container_size_in_bytes(ra->containers[k], ra->typecodes[k]);\n+\treturn count;\n+}\n+\n size_t ra_portable_serialize(const roaring_array_t *ra, char *buf) {\n     char *initbuf = buf;\n     uint32_t startOffset = 0;\n@@ -7740,6 +7796,80 @@ size_t ra_portable_serialize(const roaring_array_t *ra, char *buf) {\n     return buf - initbuf;\n }\n \n+int ra_portable_network_serialize(const roaring_array_t *ra,\n+\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t  void *data)\n+{\n+\tuint32_t initial_offset;\n+\tuint32_t cookie;\n+\tbool has_run = ra_has_run_container(ra);\n+\n+\tif (has_run) {\n+\t\tuint8_t *bitmap_of_run_containers = NULL;\n+\t\tsize_t bitmap_run_container_size = (ra->size + 7) / 8;\n+\n+\t\tcookie = htonl(SERIAL_COOKIE | ((ra->size - 1) << 16));\n+\t\tif (write_fn(data, &cookie, 4) != 4)\n+\t\t\treturn -1;\n+\t\tinitial_offset = sizeof(cookie);\n+\n+\t\tbitmap_of_run_containers = (uint8_t *)roaring_calloc(bitmap_run_container_size, 1);\n+\n+\t\tfor (uint32_t i = 0; i < ra->size; i++) {\n+\t\t\tif (get_container_type(ra->containers[i], ra->typecodes[i]) ==\n+                \t    RUN_CONTAINER_TYPE)\n+\t\t\t\tbitmap_of_run_containers[i / 8] |= (1 << (i % 8));\n+\t\t}\n+\n+\t\tfor (size_t i = 0; i < bitmap_run_container_size; i++) {\n+\t\t\tif (write_fn(data, bitmap_of_run_containers + i, 1) != 1) {\n+\t\t\t\tfree(bitmap_of_run_containers);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t\tfree(bitmap_of_run_containers);\n+\t\tinitial_offset += bitmap_run_container_size;\n+\t} else {\n+\t\tcookie = htonl(SERIAL_COOKIE_NO_RUNCONTAINER | (ra->size - 1) << 16);\n+\t\tif (write_fn(data, &cookie, 4) != 4)\n+\t\t\treturn -1;\n+\n+\t\tinitial_offset = sizeof(cookie);\n+\t}\n+\n+\t/* description table */\n+\tfor (uint32_t i = 0; i < ra->size; i++) {\n+\t\tuint16_t card;\n+\t\tuint16_t nt_key = htons(ra->keys[i]);\n+\n+\t\tif (write_fn(data, &nt_key, sizeof(uint16_t)) != sizeof(uint16_t))\n+\t\t\treturn -1;\n+\n+\t\tcard = (uint16_t)(container_get_cardinality(ra->containers[i], ra->typecodes[i]) - 1);\n+\t\tcard = htons(card);\n+\t\tif (write_fn(data, &card, 2) != 2)\n+\t\t\treturn -1;\n+\t}\n+\tinitial_offset += 4 * ra->size;\n+\n+\tif ((!has_run) || (ra->size >= NO_OFFSET_THRESHOLD)) {\n+\t\tuint32_t nt_offset;\n+\t\tinitial_offset += 4 * ra->size;\n+\t\tnt_offset = htonl(initial_offset);\n+\t\t// writing the containers offsets\n+\t\tfor (int32_t k = 0; k < ra->size; k++) {\n+\t\t\tif (write_fn(data, &nt_offset, sizeof(int32_t)) != sizeof(int32_t))\n+\t\t\t\treturn -1;\n+\t\t\tinitial_offset += container_size_in_bytes(ra->containers[k], ra->typecodes[k]);\n+\t\t}\n+\t}\n+\tfor (int32_t k = 0; k < ra->size; ++k) {\n+\t\tcontainer_network_write(ra->containers[k], ra->typecodes[k], write_fn, data);\n+\t}\n+\n+\treturn 0;\n+}\n+\n // Quickly checks whether there is a serialized bitmap at the pointer,\n // not exceeding size \"maxbytes\" in bytes. This function does not allocate\n // memory dynamically.\n@@ -7827,6 +7957,83 @@ size_t ra_portable_deserialize_size(const char *buf, const size_t maxbytes) {\n     return bytestotal;\n }\n \n+size_t ra_portable_network_deserialize_size(const char *buf, const size_t maxbytes) {\n+\tsize_t bytestotal = sizeof(int32_t);// for cookie\n+\tuint32_t cookie;\n+\tint32_t size;\n+\tif(bytestotal > maxbytes)\n+\t\treturn 0;\n+\tmemcpy(&cookie, buf, sizeof(int32_t));\n+\tcookie = ntohl(cookie);\n+\tbuf += sizeof(uint32_t);\n+\tif ((cookie & 0xFFFF) != SERIAL_COOKIE &&\n+\t\t(cookie & 0xFFFF) != SERIAL_COOKIE_NO_RUNCONTAINER) {\n+\t\treturn 0;\n+\t}\n+\n+\tsize = (cookie >> 16) + 1;\n+\tif (size > (1<<16)) {\n+\t\treturn 0; // logically impossible\n+\t}\n+\telse if (size == (1 << 16))\n+\t\treturn bytestotal;\n+\tchar *bitmapOfRunContainers = NULL;\n+\tbool hasrun = (cookie & 0xFFFF) == SERIAL_COOKIE;\n+\tif (hasrun) {\n+\t\tint32_t s = (size + 7) / 8;\n+\t\tbytestotal += s;\n+\t\tif(bytestotal > maxbytes) return 0;\n+\t\tbitmapOfRunContainers = (char *)buf;\n+\t\tbuf += s;\n+\t}\n+\tbytestotal += size * 2 * sizeof(uint16_t);\n+\tif(bytestotal > maxbytes) return 0;\n+\tuint16_t *keyscards = (uint16_t *)buf;\n+\tbuf += size * 2 * sizeof(uint16_t);\n+\tif ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) {\n+\t\t// skipping the offsets\n+\t\tbytestotal += size * 4;\n+\t\tif(bytestotal > maxbytes) return 0;\n+\t\tbuf += size * 4;\n+\t}\n+\t// Reading the containers\n+\tfor (int32_t k = 0; k < size; ++k) {\n+\t\tuint16_t tmp;\n+\t\tmemcpy(&tmp, keyscards + 2*k+1, sizeof(tmp));\n+\t\tuint32_t thiscard = ntohs(tmp) + 1;\n+\t\tbool isbitmap = (thiscard > DEFAULT_MAX_SIZE);\n+\t\tbool isrun = false;\n+\t\tif(hasrun && (bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) {\n+\t\t\tisbitmap = false;\n+\t\t\tisrun = true;\n+\t\t}\n+\t\tif (isbitmap) {\n+\t\t\tsize_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t);\n+\t\t\tbytestotal += containersize;\n+\t\t\tif(bytestotal > maxbytes)\n+\t\t\t\treturn 0;\n+\t\t\tbuf += containersize;\n+\t\t} else if (isrun) {\n+\t\t\tbytestotal += sizeof(uint16_t);\n+\t\t\tif(bytestotal > maxbytes) return 0;\n+\t\t\tuint16_t n_runs;\n+\t\t\tmemcpy(&n_runs, buf, sizeof(uint16_t));\n+\t\t\tn_runs = ntohs(n_runs);\n+\t\t\tbuf += sizeof(uint16_t);\n+\t\t\tsize_t containersize = n_runs * sizeof(rle16_t);\n+\t\t\tbytestotal += containersize;\n+\t\t\tif(bytestotal > maxbytes) return 0;\n+\t\t\tbuf += containersize;\n+\t\t} else {\n+\t\t\tsize_t containersize = thiscard * sizeof(uint16_t);\n+\t\t\tbytestotal += containersize;\n+\t\t\tif(bytestotal > maxbytes)\n+\t\t\t\treturn 0;\n+\t\t\tbuf += containersize;\n+\t\t}\n+\t}\n+\treturn bytestotal;\n+}\n \n // this function populates answer from the content of buf (reading up to maxbytes bytes).\n // The function returns false if a properly serialized bitmap cannot be found.\n@@ -8000,6 +8207,177 @@ bool ra_portable_deserialize(roaring_array_t *answer, const char *buf, const siz\n     return true;\n }\n \n+bool ra_portable_network_deserialize(roaring_array_t *answer, const char *buf, const size_t maxbytes, size_t *readbytes)\n+{\n+\tuint32_t cookie;\n+\tint32_t size;\n+\tconst char *bitmapOfRunContainers = NULL;\n+\tint hasrun = 0;\n+\tuint16_t *keyscards;\n+\n+\t*readbytes = sizeof(int32_t);// for cookie\n+\tif(*readbytes > maxbytes) {\n+\tfprintf(stderr, \"Ran out of bytes while reading first 4 bytes.\\n\");\n+\treturn false;\n+\t}\n+\tmemcpy(&cookie, buf, sizeof(int32_t));\n+\tcookie = ntohl(cookie);\n+\tbuf += sizeof(uint32_t);\n+\tif ((cookie & 0xFFFF) != SERIAL_COOKIE &&\n+\t\t(cookie & 0xFFFF) != SERIAL_COOKIE_NO_RUNCONTAINER) {\n+\t\tfprintf(stderr, \"I failed to find one of the right cookies. Found %\" PRIu32 \"\\n\",\n+\t\t\tcookie);\n+\t\treturn false;\n+\t}\n+\n+\tsize = (cookie >> 16) + 1;\n+\tif (size < 0) {\n+\t\tfprintf(stderr, \"You cannot have a negative number of containers, the data must be corrupted: %\" PRId32 \"\\n\",\n+\t\t\t\tsize);\n+\t\treturn false; // logically impossible\n+\t}\n+\tif (size > (1<<16)) {\n+\t\tfprintf(stderr, \"You cannot have so many containers, the data must be corrupted: %\" PRId32 \"\\n\",\n+\t\t\t\tsize);\n+\t\treturn false; // logically impossible\n+\t}\n+\telse if (size == (1 << 16)) {\n+\t\tra_init_with_capacity(answer, size);\n+\t\treturn true;\n+\t}\n+\thasrun = (cookie & 0xFFFF) == SERIAL_COOKIE;\n+\tif (hasrun) {\n+\t\tint32_t s = (size + 7) / 8;\n+\t\t*readbytes += s;\n+\t\tif(*readbytes > maxbytes) {// data is corrupted?\n+\t\tfprintf(stderr, \"Ran out of bytes while reading run bitmap.\\n\");\n+\t\treturn false;\n+\t\t}\n+\t\tbitmapOfRunContainers = buf;\n+\t\tbuf += s;\n+\t}\n+\tkeyscards = (uint16_t *)buf;\n+\n+\t*readbytes += size * 2 * sizeof(uint16_t);\n+\tif(*readbytes > maxbytes) {\n+\t\tfprintf(stderr, \"Ran out of bytes while reading key-cardinality array.\\n\");\n+\t\treturn false;\n+\t}\n+\tbuf += size * 2 * sizeof(uint16_t);\n+\n+\tbool is_ok = ra_init_with_capacity(answer, size);\n+\tif (!is_ok) {\n+\t\tfprintf(stderr, \"Failed to allocate memory for roaring array. Bailing out.\\n\");\n+\t\treturn false;\n+\t}\n+\n+\tfor (int32_t k = 0; k < size; ++k) {\n+\t\tuint16_t tmp;\n+\t\tmemcpy(&tmp, keyscards + 2*k, sizeof(tmp));\n+\t\tanswer->keys[k] = ntohs(tmp);\n+\t}\n+\tif ((!hasrun) || (size >= NO_OFFSET_THRESHOLD)) {\n+\t\t*readbytes += size * 4;\n+\t\tif(*readbytes > maxbytes) {// data is corrupted?\n+\t\t\tfprintf(stderr, \"Ran out of bytes while reading offsets.\\n\");\n+\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\t// skipping the offsets\n+\t\tbuf += size * 4;\n+\t}\n+\t// Reading the containers\n+\tfor (int32_t k = 0; k < size; ++k) {\n+\t\tuint16_t tmp;\n+\t\tuint32_t thiscard;\n+\t\tbool isbitmap;\n+\t\tbool isrun;\n+\n+\t\tmemcpy(&tmp, keyscards + 2*k+1, sizeof(tmp));\n+\t\tthiscard = ntohs(tmp) + 1;\n+\t\tisbitmap = (thiscard > DEFAULT_MAX_SIZE);\n+\t\tisrun = false;\n+\t\tif(hasrun && (bitmapOfRunContainers[k / 8] & (1 << (k % 8))) != 0) {\n+\t\t\tisbitmap = false;\n+\t\t\tisrun = true;\n+\t\t}\n+\t\tif (isbitmap) {\n+\t\t\t// we check that the read is allowed\n+\t\t\tsize_t containersize = BITSET_CONTAINER_SIZE_IN_WORDS * sizeof(uint64_t);\n+\t\t\t*readbytes += containersize;\n+\t\t\tif(*readbytes > maxbytes) {\n+\t\t\t\tfprintf(stderr, \"Running out of bytes while reading a bitset container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\t// it is now safe to read\n+\t\t\tbitset_container_t *c = bitset_container_create();\n+\t\t\tif(c == NULL) {// memory allocation failure\n+\t\t\t\tfprintf(stderr, \"Failed to allocate memory for a bitset container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\tanswer->size++;\n+\t\t\tbuf += bitset_container_network_read(thiscard, c, buf);\n+\t\t\tanswer->containers[k] = c;\n+\t\t\tanswer->typecodes[k] = BITSET_CONTAINER_TYPE;\n+\t\t} else if (isrun) {\n+\t\t\t// we check that the read is allowed\n+\t\t\t*readbytes += sizeof(uint16_t);\n+\t\t\tif(*readbytes > maxbytes) {\n+\t\t\t\tfprintf(stderr, \"Running out of bytes while reading a run container (header).\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\tuint16_t n_runs;\n+\t\t\tmemcpy(&n_runs, buf, sizeof(uint16_t));\n+\t\t\tn_runs = ntohs(n_runs);\n+\t\t\tsize_t containersize = n_runs * sizeof(rle16_t);\n+\t\t\t*readbytes += containersize;\n+\t\t\tif(*readbytes > maxbytes) {// data is corrupted?\n+\t\t\t\tfprintf(stderr, \"Running out of bytes while reading a run container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\t// it is now safe to read\n+\n+\t\t\trun_container_t *c = run_container_create();\n+\t\t\tif(c == NULL) {// memory allocation failure\n+\t\t\t\tfprintf(stderr, \"Failed to allocate memory for a run container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\tanswer->size++;\n+\t\t\tbuf += run_container_network_read(thiscard, c, buf);\n+\t\t\tanswer->containers[k] = c;\n+\t\t\tanswer->typecodes[k] = RUN_CONTAINER_TYPE;\n+\t\t} else {\n+\t\t\t// we check that the read is allowed\n+\t\t\tsize_t containersize = thiscard * sizeof(uint16_t);\n+\t\t\t*readbytes += containersize;\n+\t\t\tif(*readbytes > maxbytes) {// data is corrupted?\n+\t\t\t\tfprintf(stderr, \"Running out of bytes while reading an array container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\t// it is now safe to read\n+\t\t\tarray_container_t *c =\n+\t\t\t\tarray_container_create_given_capacity(thiscard);\n+\t\t\tif(c == NULL) {// memory allocation failure\n+\t\t\t\tfprintf(stderr, \"Failed to allocate memory for an array container.\\n\");\n+\t\t\t\tra_clear(answer);// we need to clear the containers already allocated, and the roaring array\n+\t\t\t\treturn false;\n+\t\t\t}\n+\t\t\tanswer->size++;\n+\t\t\tbuf += array_container_network_read(thiscard, c, buf);\n+\t\t\tanswer->containers[k] = c;\n+\t\t\tanswer->typecodes[k] = ARRAY_CONTAINER_TYPE;\n+\t\t}\n+\t}\n+\treturn true;\n+}\n+\n #ifdef __cplusplus\n } } }  // extern \"C\" { namespace roaring { namespace internal {\n #endif\n@@ -8603,16 +8981,16 @@ extern inline void roaring_bitmap_remove_range(roaring_bitmap_t *r, uint64_t min\n void roaring_bitmap_printf(const roaring_bitmap_t *r) {\n     const roaring_array_t *ra = &r->high_low_container;\n \n-    printf(\"{\");\n+    fprintf(stderr, \"{\");\n     for (int i = 0; i < ra->size; ++i) {\n         container_printf_as_uint32_array(ra->containers[i], ra->typecodes[i],\n                                          ((uint32_t)ra->keys[i]) << 16);\n \n         if (i + 1 < ra->size) {\n-            printf(\",\");\n+            fprintf(stderr, \",\");\n         }\n     }\n-    printf(\"}\");\n+    fprintf(stderr, \"}\");\n }\n \n void roaring_bitmap_printf_describe(const roaring_bitmap_t *r) {\n@@ -8736,6 +9114,14 @@ void roaring_bitmap_free(const roaring_bitmap_t *r) {\n     roaring_free((roaring_bitmap_t*)r);\n }\n \n+void roaring_bitmap_free_safe(roaring_bitmap_t **r)\n+{\n+\tif (*r) {\n+\t\troaring_bitmap_free((const roaring_bitmap_t *)*r);\n+\t\tr = NULL;\n+\t}\n+}\n+\n void roaring_bitmap_clear(roaring_bitmap_t *r) {\n   ra_reset(&r->high_low_container);\n }\n@@ -9700,6 +10086,11 @@ size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *r) {\n     return ra_portable_size_in_bytes(&r->high_low_container);\n }\n \n+size_t roaring_bitmap_network_portable_size_in_bytes(const roaring_bitmap_t *r)\n+{\n+\treturn ra_portable_network_size_in_bytes(&r->high_low_container);\n+}\n+\n \n roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size_t maxbytes) {\n     roaring_bitmap_t *ans =\n@@ -9718,21 +10109,50 @@ roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf, size\n     return ans;\n }\n \n+roaring_bitmap_t *roaring_bitmap_portable_network_deserialize_safe(const char *buf, size_t maxbytes)\n+{\n+\troaring_bitmap_t *ans =\n+\t\t(roaring_bitmap_t *)roaring_malloc(sizeof(roaring_bitmap_t));\n+\tif (ans == NULL) {\n+\t\treturn NULL;\n+\t}\n+\tsize_t bytesread;\n+\tbool is_ok = ra_portable_network_deserialize(&ans->high_low_container, buf, maxbytes, &bytesread);\n+\tif(is_ok) assert(bytesread <= maxbytes);\n+\troaring_bitmap_set_copy_on_write(ans, false);\n+\tif (!is_ok) {\n+\t\troaring_free(ans);\n+\t\treturn NULL;\n+\t}\n+\treturn ans;\n+}\n+\n roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf) {\n     return roaring_bitmap_portable_deserialize_safe(buf, SIZE_MAX);\n }\n \n-\n size_t roaring_bitmap_portable_deserialize_size(const char *buf, size_t maxbytes) {\n   return ra_portable_deserialize_size(buf, maxbytes);\n }\n \n+size_t roaring_bitmap_portable_network_deserialize_size(const char *buf, size_t maxbytes) {\n+\tsize_t size = ra_portable_network_deserialize_size(buf, maxbytes);\n+\treturn size;\n+}\n+\n \n size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *r,\n                                          char *buf) {\n     return ra_portable_serialize(&r->high_low_container, buf);\n }\n \n+int roaring_bitmap_portable_network_serialize(roaring_bitmap_t *rb,\n+\t\t\t\t     int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t     void *data)\n+{\n+\treturn ra_portable_network_serialize(&rb->high_low_container, write_fn, data);\n+}\n+\n roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) {\n     const char *bufaschar = (const char *)buf;\n     if (*(const unsigned char *)buf == CROARING_SERIALIZATION_ARRAY_UINT32) {\n@@ -13827,9 +14247,9 @@ void array_container_printf_as_uint32_array(const array_container_t *v,\n     if (v->cardinality == 0) {\n         return;\n     }\n-    printf(\"%u\", v->array[0] + base);\n+    fprintf(stderr, \"%u\", v->array[0] + base);\n     for (int i = 1; i < v->cardinality; ++i) {\n-        printf(\",%u\", v->array[i] + base);\n+        fprintf(stderr, \",%u\", v->array[i] + base);\n     }\n }\n \n@@ -13856,6 +14276,20 @@ int32_t array_container_write(const array_container_t *container, char *buf) {\n     return array_container_size_in_bytes(container);\n }\n \n+int array_container_network_write(const array_container_t *container,\n+\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t  void *data)\n+{\n+\tint32_t i;\n+\tsize_t size = container->cardinality * sizeof(uint16_t);\n+\tfor (i = 0; i < container->cardinality; i++) {\n+\t\tuint16_t nt_elem = htons(container->array[i]);\n+\t\tif (write_fn(data, &nt_elem, sizeof(uint16_t)) != sizeof(uint16_t))\n+\t\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n bool array_container_is_subset(const array_container_t *container1,\n                                const array_container_t *container2) {\n     if (container1->cardinality > container2->cardinality) {\n@@ -13890,6 +14324,23 @@ int32_t array_container_read(int32_t cardinality, array_container_t *container,\n     return array_container_size_in_bytes(container);\n }\n \n+int32_t array_container_network_read(int32_t cardinality, array_container_t *container,\n+                        \t     const char *buf)\n+{\n+\tuint32_t i;\n+\tif (container->capacity < cardinality) {\n+\t\tarray_container_grow(container, cardinality, false);\n+\t}\n+\tcontainer->cardinality = cardinality;\n+\tfor (i = 0; i < container->cardinality; i++) {\n+\t\tuint16_t val;\n+\t\tmemcpy(&val, buf + i * sizeof(uint16_t), sizeof(uint16_t));\n+\t\tval = ntohs(val);\n+\t\tcontainer->array[i] = val;\n+\t}\n+\treturn array_container_size_in_bytes(container);\n+}\n+\n bool array_container_iterate(const array_container_t *cont, uint32_t base,\n                              roaring_iterator iterator, void *ptr) {\n     for (int i = 0; i < cont->cardinality; i++)\n@@ -15208,13 +15659,13 @@ void run_container_printf_as_uint32_array(const run_container_t *cont,\n     {\n         uint32_t run_start = base + cont->runs[0].value;\n         uint16_t le = cont->runs[0].length;\n-        printf(\"%u\", run_start);\n-        for (uint32_t j = 1; j <= le; ++j) printf(\",%u\", run_start + j);\n+        fprintf(stderr, \"%u\", run_start);\n+        for (uint32_t j = 1; j <= le; ++j) fprintf(stderr, \",%u\", run_start + j);\n     }\n     for (int32_t i = 1; i < cont->n_runs; ++i) {\n         uint32_t run_start = base + cont->runs[i].value;\n         uint16_t le = cont->runs[i].length;\n-        for (uint32_t j = 0; j <= le; ++j) printf(\",%u\", run_start + j);\n+        for (uint32_t j = 0; j <= le; ++j) fprintf(stderr, \",%u\", run_start + j);\n     }\n }\n \n@@ -15225,6 +15676,28 @@ int32_t run_container_write(const run_container_t *container, char *buf) {\n     return run_container_size_in_bytes(container);\n }\n \n+int run_container_network_write(const run_container_t *container,\n+\t\t\t\tint (*write_fn) (void *, const void *, size_t),\n+\t\t\t\tvoid *data)\n+{\n+\tuint16_t i;\n+\tint32_t nt_nruns = htonl(container->n_runs);\n+\tif (write_fn(data, &nt_nruns, sizeof(uint16_t)))\n+\t\treturn -1;\n+\n+\tfor (i = 0; i < container->n_runs; i++) {\n+\t\trle16_t run = container->runs[i];\n+\t\tuint16_t nt_value = htons(run.value);\n+\t\tuint16_t nt_len = htons(run.length);\n+\t\tif (write_fn(data, &nt_value, sizeof(uint16_t)) != sizeof(uint16_t))\n+\t\t\treturn -1;\n+\t\tif (write_fn(data, &nt_len, sizeof(uint16_t)) != sizeof(uint16_t))\n+\t\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n int32_t run_container_read(int32_t cardinality, run_container_t *container,\n                            const char *buf) {\n     (void)cardinality;\n@@ -15238,6 +15711,29 @@ int32_t run_container_read(int32_t cardinality, run_container_t *container,\n     return run_container_size_in_bytes(container);\n }\n \n+int32_t run_container_network_read(int32_t cardinality, run_container_t *container,\n+                        \t   const char *buf)\n+{\n+\tint32_t n_runs;\n+\tmemcpy(&n_runs, buf, sizeof(uint16_t));\n+\tn_runs = ntohs(n_runs);\n+\tcontainer->n_runs = n_runs;\n+\tif (container->n_runs > container->capacity)\n+        run_container_grow(container, container->n_runs, false);\n+\tif(container->n_runs > 0) {\n+\t\tuint32_t i;\n+\n+\t\tfor (i = 0; i < container->n_runs; i++) {\n+\t\t\trle16_t run;\n+\t\t\tmemcpy(&run, buf + sizeof(uint16_t) + i * sizeof(rle16_t), sizeof(rle16_t));\n+\t\t\trun.length = ntohs(run.length);\n+\t\t\trun.value = ntohs(run.value);\n+\t\t\tcontainer->runs[i] = run;\n+\t\t}\n+\t}\n+\treturn run_container_size_in_bytes(container);\n+}\n+\n bool run_container_iterate(const run_container_t *cont, uint32_t base,\n                            roaring_iterator iterator, void *ptr) {\n     for (int i = 0; i < cont->n_runs; ++i) {\n@@ -17417,10 +17913,10 @@ void bitset_container_printf_as_uint32_array(const bitset_container_t * v, uint3\n \t\t\tuint64_t t = w & (~w + 1);\n \t\t\tint r = __builtin_ctzll(w);\n \t\t\tif(iamfirst) {// predicted to be false\n-\t\t\t\tprintf(\"%u\", r + base);\n+\t\t\t\tfprintf(stderr, \"%u\", r + base);\n \t\t\t\tiamfirst = false;\n \t\t\t} else {\n-\t\t\t\tprintf(\",%u\",r + base);\n+\t\t\t\tfprintf(stderr, \",%u\",r + base);\n \t\t\t}\n \t\t\tw ^= t;\n \t\t}\n@@ -17454,6 +17950,18 @@ int32_t bitset_container_write(const bitset_container_t *container,\n \treturn bitset_container_size_in_bytes(container);\n }\n \n+int bitset_container_network_write(const bitset_container_t *container,\n+\t\t\t\t   int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t   void *data)\n+{\n+\tuint32_t i = 0;\n+\tfor (i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) {\n+\t\tuint64_t nt_word = htonll(container->words[i]);\n+\t\tif (write_fn(data, &nt_word, sizeof(uint64_t)) != sizeof(uint64_t))\n+\t\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n \n int32_t bitset_container_read(int32_t cardinality, bitset_container_t *container,\n \t\tconst char *buf)  {\n@@ -17462,6 +17970,23 @@ int32_t bitset_container_read(int32_t cardinality, bitset_container_t *container\n \treturn bitset_container_size_in_bytes(container);\n }\n \n+int32_t bitset_container_network_read(int32_t cardinality, bitset_container_t *container,\n+\t\t\t\t      const char *buf)\n+{\n+\tuint32_t i = 0;\n+\tconst char *mbuf = buf;\n+\tcontainer->cardinality = cardinality;\n+\n+\tfor (i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; i++) {\n+\t\tuint64_t nt_word;\n+\t\tmemcpy(&nt_word, mbuf, sizeof(uint64_t));\n+\t\tmbuf += sizeof(uint64_t);\n+\n+\t\tcontainer->words[i] = ntohll(nt_word);\n+\t}\n+\treturn bitset_container_size_in_bytes(container);\n+}\n+\n bool bitset_container_iterate(const bitset_container_t *cont, uint32_t base, roaring_iterator iterator, void *ptr) {\n   for (int32_t i = 0; i < BITSET_CONTAINER_SIZE_IN_WORDS; ++i ) {\n     uint64_t w = cont->words[i];\ndiff --git a/roaring/roaring.h b/roaring/roaring.h\nindex bd5e0a0fe1c..84489eaa260 100644\n--- a/roaring/roaring.h\n+++ b/roaring/roaring.h\n@@ -409,6 +409,11 @@ void roaring_bitmap_andnot_inplace(roaring_bitmap_t *r1,\n  */\n void roaring_bitmap_free(const roaring_bitmap_t *r);\n \n+/**\n+ * Frees the memory if exists\n+ */\n+void roaring_bitmap_free_safe(roaring_bitmap_t **r);\n+\n /**\n  * Add value n_args from pointer vals, faster than repeatedly calling\n  * `roaring_bitmap_add()`\n@@ -605,6 +610,9 @@ roaring_bitmap_t *roaring_bitmap_portable_deserialize(const char *buf);\n roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf,\n                                                            size_t maxbytes);\n \n+roaring_bitmap_t *roaring_bitmap_portable_network_deserialize_safe(const char *buf,\n+\t\t\t\t\t\t\t\t   size_t maxbytes);\n+\n /**\n  * Check how many bytes would be read (up to maxbytes) at this pointer if there\n  * is a bitmap, returns zero if there is no valid bitmap.\n@@ -615,6 +623,9 @@ roaring_bitmap_t *roaring_bitmap_portable_deserialize_safe(const char *buf,\n size_t roaring_bitmap_portable_deserialize_size(const char *buf,\n                                                 size_t maxbytes);\n \n+size_t roaring_bitmap_portable_network_deserialize_size(const char *buf,\n+\t\t\t\t\t\t\tsize_t maxbytes);\n+\n /**\n  * How many bytes are required to serialize this bitmap.\n  *\n@@ -623,6 +634,8 @@ size_t roaring_bitmap_portable_deserialize_size(const char *buf,\n  */\n size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *r);\n \n+size_t roaring_bitmap_network_portable_size_in_bytes(const roaring_bitmap_t *r);\n+\n /**\n  * Write a bitmap to a char buffer.  The output buffer should refer to at least\n  * `roaring_bitmap_portable_size_in_bytes(r)` bytes of allocated memory.\n@@ -635,6 +648,10 @@ size_t roaring_bitmap_portable_size_in_bytes(const roaring_bitmap_t *r);\n  */\n size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *r, char *buf);\n \n+int roaring_bitmap_portable_network_serialize(roaring_bitmap_t *rb,\n+\t\t\t\t     int (*write_fn) (void *, const void *, size_t),\n+\t\t\t\t     void *data);\n+\n /*\n  * \"Frozen\" serialization format imitates memory layout of roaring_bitmap_t.\n  * Deserialized bitmap is a constant view of the underlying buffer.\n-- \ngitgitgadget\n\n"},{"id":"463208","messageId":"4364224f9bddc8f1e40875ebc540b28225317176.1663609659.git.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"[PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:37Z","receivedAt":"2022-09-19T17:48:07Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n\nRoaring bitmaps are said to be more efficient (most of the time) than\newah bitmaps. So Git might gain some optimization if it support roaring\nbitmaps. As Roaring library has all the changes it needed to implement\nroaring bitmaps in Git, Git can learn to write roaring bitmaps. However,\nall the changes are backward-compatible.\n\nTeach Git to write roaring bitmaps.\n\nMentored-by: Taylor Blau <me@ttaylorr.com>\nMentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\nSigned-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n---\n Makefile                |   1 +\n bitmap.c                | 225 +++++++++++++++++++++++++++\n bitmap.h                |  33 ++++\n builtin/diff.c          |  10 +-\n ewah/bitmap.c           |  61 +++++---\n ewah/ewok.h             |  37 ++---\n pack-bitmap-write.c     | 326 ++++++++++++++++++++++++++++++----------\n pack-bitmap.c           | 114 +++++++-------\n pack-bitmap.h           |  22 ++-\n t/t5310-pack-bitmaps.sh |  17 +++\n 10 files changed, 664 insertions(+), 182 deletions(-)\n create mode 100644 bitmap.c\n create mode 100644 bitmap.h\n\ndiff --git a/Makefile b/Makefile\nindex e9537951105..9ca19b3ca8d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -900,6 +900,7 @@ LIB_OBJS += archive.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n LIB_OBJS += bisect.o\n+LIB_OBJS += bitmap.o\n LIB_OBJS += blame.o\n LIB_OBJS += blob.o\n LIB_OBJS += bloom.o\ndiff --git a/bitmap.c b/bitmap.c\nnew file mode 100644\nindex 00000000000..7d547eb9f53\n--- /dev/null\n+++ b/bitmap.c\n@@ -0,0 +1,225 @@\n+#include \"bitmap.h\"\n+#include \"cache.h\"\n+\n+static enum bitmap_type bitmap_type = INIT_BITMAP_TYPE;\n+\n+void set_bitmap_type(enum bitmap_type type)\n+{\n+\tbitmap_type = type;\n+}\n+\n+enum bitmap_type get_bitmap_type(void)\n+{\n+\treturn bitmap_type;\n+}\n+\n+void *roaring_or_ewah_bitmap_init(void)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn ewah_new();\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_create();\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t\treturn NULL;\n+\t}\n+}\n+\n+void *roaring_or_raw_bitmap_new(void)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn raw_bitmap_new();\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_create();\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t\t\treturn NULL;\n+\t}\n+}\n+\n+void *roaring_or_raw_bitmap_copy(void *bitmap)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn raw_bitmap_dup(bitmap);\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_copy(bitmap);\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t\t\treturn NULL;\n+\t}\n+}\n+\n+int roaring_or_ewah_bitmap_set(void *bitmap, uint32_t i)\n+{\n+\tswitch (bitmap_type) {\n+\tcase EWAH:\n+\t\tewah_set(bitmap, i);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_add(bitmap, i);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn error(_(\"bitmap type not initialized\\n\"));\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void roaring_or_raw_bitmap_set(void *bitmap, uint32_t i)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_set(bitmap, i);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_add(bitmap, i);\n+\t\tbreak;\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_unset(void *bitmap, uint32_t i)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_unset(bitmap, i);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_remove(bitmap, i);\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\n+\n+int roaring_or_raw_bitmap_get(void *bitmap, uint32_t pos)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn raw_bitmap_get(bitmap, pos);\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_contains(bitmap, pos);\n+\tdefault:\n+\t\treturn error(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+int roaring_or_raw_bitmap_equals(void *a, void *b)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn raw_bitmap_equals(a, b);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_equals(a, b);\n+\tdefault:\n+\t\treturn error(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+size_t roaring_or_raw_bitmap_cardinality(void *bitmap)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\treturn raw_bitmap_popcount(bitmap);\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_get_cardinality(bitmap);\n+\tdefault:\n+\t\treturn error(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+int roaring_or_raw_bitmap_is_subset(void *a, void *b)\n+{\n+\tswitch (bitmap_type) {\n+\tcase EWAH:\n+\t\treturn raw_bitmap_is_subset(a, b);\n+\tcase ROARING:\n+\t\treturn roaring_bitmap_andnot_cardinality(a, b) ? 1: 0;\n+\tdefault:\n+\t\treturn error(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_printf(void *a)\n+{\n+\tswitch (bitmap_type) {\n+\tcase EWAH:\n+\t\tewah_bitmap_print(a);\n+\t\treturn;\n+\tcase ROARING:\n+\t\troaring_bitmap_printf(a);\n+\t\treturn;\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_or(void *self, void *other)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_or(self, other);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_or_inplace(self, other);\n+\t\tbreak;\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_and_not(void *self, void *other)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_and_not(self, other);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_andnot_inplace(self, other);\n+\t\tbreak;\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_free(void *bitmap)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_free(bitmap);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_free(bitmap);\n+\t\tbreak;\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n+\n+void roaring_or_raw_bitmap_free_safe(void **bitmap)\n+{\n+\tswitch (bitmap_type)\n+\t{\n+\tcase EWAH:\n+\t\traw_bitmap_free(*bitmap);\n+\t\tbreak;\n+\tcase ROARING:\n+\t\troaring_bitmap_free_safe((roaring_bitmap_t **)bitmap);\n+\t\tbreak;\n+\tdefault:\n+\t\terror(_(\"bitmap type not initialized\\n\"));\n+\t}\n+}\n\\ No newline at end of file\ndiff --git a/bitmap.h b/bitmap.h\nnew file mode 100644\nindex 00000000000..d75400922cc\n--- /dev/null\n+++ b/bitmap.h\n@@ -0,0 +1,33 @@\n+#ifndef __BITMAP_H__\n+#define __BITMAP_H__\n+\n+\n+#include \"git-compat-util.h\"\n+#include \"ewah/ewok.h\"\n+#include \"roaring/roaring.h\"\n+\n+enum bitmap_type {\n+\tINIT_BITMAP_TYPE = 0,\n+\tEWAH,\n+\tROARING\n+};\n+\n+enum bitmap_type get_bitmap_type(void);\n+void set_bitmap_type(enum bitmap_type type);\n+void *roaring_or_ewah_bitmap_init(void);\n+void *roaring_or_raw_bitmap_new(void);\n+void *roaring_or_raw_bitmap_copy(void *bitmap);\n+int roaring_or_ewah_bitmap_set(void *bitmap, uint32_t i);\n+void roaring_or_raw_bitmap_set(void *bitmap, uint32_t i);\n+int roaring_or_raw_bitmap_get(void *bitmap, uint32_t pos);\n+int roaring_or_raw_bitmap_is_subset(void *a, void *b);\n+void roaring_or_raw_bitmap_or(void *self, void *other);\n+void roaring_or_raw_bitmap_free(void *bitmap);\n+void roaring_or_raw_bitmap_free_safe(void **bitmap);\n+void roaring_or_raw_bitmap_unset(void *bitmap, uint32_t i);\n+void roaring_or_raw_bitmap_printf(void *a);\n+int roaring_or_raw_bitmap_equals(void *a, void *b);\n+void roaring_or_raw_bitmap_and_not(void *self, void *other);\n+size_t roaring_or_raw_bitmap_cardinality(void *bitmap);\n+\n+#endif\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex 54bb3de964c..8cf7481d325 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -353,8 +353,8 @@ static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)\n \t\t\tcontinue;\n \t\t}\n \t\tif (!map)\n-\t\t\tmap = bitmap_new();\n-\t\tbitmap_set(map, i);\n+\t\t\tmap = raw_bitmap_new();\n+\t\traw_bitmap_set(map, i);\n \t}\n \n \t/*\n@@ -364,7 +364,7 @@ static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)\n \t\tusage(builtin_diff_usage);\n \n \tif (!is_symdiff) {\n-\t\tbitmap_free(map);\n+\t\traw_bitmap_free(map);\n \t\tsym->warn = 0;\n \t\tsym->skip = NULL;\n \t\treturn;\n@@ -375,7 +375,7 @@ static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)\n \tif (basecount == 0)\n \t\tdie(_(\"%s...%s: no merge base\"), sym->left, sym->right);\n \tsym->base = rev->pending.objects[basepos].name;\n-\tbitmap_unset(map, basepos);\t/* unmark the base we want */\n+\traw_bitmap_unset(map, basepos);\t/* unmark the base we want */\n \tsym->warn = basecount > 1;\n \tsym->skip = map;\n }\n@@ -539,7 +539,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \t\t\tobj = &get_commit_tree(((struct commit *)obj))->object;\n \n \t\tif (obj->type == OBJ_TREE) {\n-\t\t\tif (sdiff.skip && bitmap_get(sdiff.skip, i))\n+\t\t\tif (sdiff.skip && raw_bitmap_get(sdiff.skip, i))\n \t\t\t\tcontinue;\n \t\t\tobj->flags |= flags;\n \t\t\tadd_object_array(obj, name, &ent);\ndiff --git a/ewah/bitmap.c b/ewah/bitmap.c\nindex ac618641632..499bf2e03d0 100644\n--- a/ewah/bitmap.c\n+++ b/ewah/bitmap.c\n@@ -22,7 +22,7 @@\n #define EWAH_MASK(x) ((eword_t)1 << (x % BITS_IN_EWORD))\n #define EWAH_BLOCK(x) (x / BITS_IN_EWORD)\n \n-struct bitmap *bitmap_word_alloc(size_t word_alloc)\n+struct bitmap *raw_bitmap_word_alloc(size_t word_alloc)\n {\n \tstruct bitmap *bitmap = xmalloc(sizeof(struct bitmap));\n \tCALLOC_ARRAY(bitmap->words, word_alloc);\n@@ -30,14 +30,14 @@ struct bitmap *bitmap_word_alloc(size_t word_alloc)\n \treturn bitmap;\n }\n \n-struct bitmap *bitmap_new(void)\n+struct bitmap *raw_bitmap_new(void)\n {\n-\treturn bitmap_word_alloc(32);\n+\treturn raw_bitmap_word_alloc(32);\n }\n \n-struct bitmap *bitmap_dup(const struct bitmap *src)\n+struct bitmap *raw_bitmap_dup(const struct bitmap *src)\n {\n-\tstruct bitmap *dst = bitmap_word_alloc(src->word_alloc);\n+\tstruct bitmap *dst = raw_bitmap_word_alloc(src->word_alloc);\n \tCOPY_ARRAY(dst->words, src->words, src->word_alloc);\n \treturn dst;\n }\n@@ -50,7 +50,7 @@ static void bitmap_grow(struct bitmap *self, size_t word_alloc)\n \t       (self->word_alloc - old_size) * sizeof(eword_t));\n }\n \n-void bitmap_set(struct bitmap *self, size_t pos)\n+void raw_bitmap_set(struct bitmap *self, size_t pos)\n {\n \tsize_t block = EWAH_BLOCK(pos);\n \n@@ -58,7 +58,7 @@ void bitmap_set(struct bitmap *self, size_t pos)\n \tself->words[block] |= EWAH_MASK(pos);\n }\n \n-void bitmap_unset(struct bitmap *self, size_t pos)\n+void raw_bitmap_unset(struct bitmap *self, size_t pos)\n {\n \tsize_t block = EWAH_BLOCK(pos);\n \n@@ -66,14 +66,14 @@ void bitmap_unset(struct bitmap *self, size_t pos)\n \t\tself->words[block] &= ~EWAH_MASK(pos);\n }\n \n-int bitmap_get(struct bitmap *self, size_t pos)\n+int raw_bitmap_get(struct bitmap *self, size_t pos)\n {\n \tsize_t block = EWAH_BLOCK(pos);\n \treturn block < self->word_alloc &&\n \t\t(self->words[block] & EWAH_MASK(pos)) != 0;\n }\n \n-struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)\n+struct ewah_bitmap *raw_bitmap_to_ewah(struct bitmap *bitmap)\n {\n \tstruct ewah_bitmap *ewah = ewah_new();\n \tsize_t i, running_empty_words = 0;\n@@ -100,9 +100,9 @@ struct ewah_bitmap *bitmap_to_ewah(struct bitmap *bitmap)\n \treturn ewah;\n }\n \n-struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)\n+struct bitmap *ewah_to_raw_bitmap(struct ewah_bitmap *ewah)\n {\n-\tstruct bitmap *bitmap = bitmap_new();\n+\tstruct bitmap *bitmap = raw_bitmap_new();\n \tstruct ewah_iterator it;\n \teword_t blowup;\n \tsize_t i = 0;\n@@ -118,7 +118,7 @@ struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah)\n \treturn bitmap;\n }\n \n-void bitmap_and_not(struct bitmap *self, struct bitmap *other)\n+void raw_bitmap_and_not(struct bitmap *self, struct bitmap *other)\n {\n \tconst size_t count = (self->word_alloc < other->word_alloc) ?\n \t\tself->word_alloc : other->word_alloc;\n@@ -129,7 +129,7 @@ void bitmap_and_not(struct bitmap *self, struct bitmap *other)\n \t\tself->words[i] &= ~other->words[i];\n }\n \n-void bitmap_or(struct bitmap *self, const struct bitmap *other)\n+void raw_bitmap_or(struct bitmap *self, const struct bitmap *other)\n {\n \tsize_t i;\n \n@@ -138,7 +138,7 @@ void bitmap_or(struct bitmap *self, const struct bitmap *other)\n \t\tself->words[i] |= other->words[i];\n }\n \n-void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n+void raw_bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n {\n \tsize_t original_size = self->word_alloc;\n \tsize_t other_final = (other->bit_size / BITS_IN_EWORD) + 1;\n@@ -159,7 +159,7 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n \t\tself->words[i++] |= word;\n }\n \n-size_t bitmap_popcount(struct bitmap *self)\n+size_t raw_bitmap_popcount(struct bitmap *self)\n {\n \tsize_t i, count = 0;\n \n@@ -169,7 +169,7 @@ size_t bitmap_popcount(struct bitmap *self)\n \treturn count;\n }\n \n-int bitmap_equals(struct bitmap *self, struct bitmap *other)\n+int raw_bitmap_equals(struct bitmap *self, struct bitmap *other)\n {\n \tstruct bitmap *big, *small;\n \tsize_t i;\n@@ -195,7 +195,32 @@ int bitmap_equals(struct bitmap *self, struct bitmap *other)\n \treturn 1;\n }\n \n-int bitmap_is_subset(struct bitmap *self, struct bitmap *other)\n+void ewah_bitmap_print(struct ewah_bitmap *bm)\n+{\n+\tuint32_t i;\n+\tstruct bitmap *raw_bm = ewah_to_raw_bitmap(bm);\n+\n+\tfprintf(stderr, \"\\n[ \");\n+\tfor (i = 0; i < raw_bm->word_alloc; i++) {\n+\t\teword_t word = raw_bm->words[i];\n+\t\tunsigned offset;\n+\n+\t\tfor (offset = 0; offset < BITS_IN_EWORD; offset++) {\n+\t\t\tuint32_t pos;\n+\n+\t\t\tif ((word >> offset) == 0)\n+\t\t\t\tbreak;\n+\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\tpos = i * BITS_IN_EWORD + offset;\n+\n+\t\t\tfprintf(stderr, \"%d, \", pos);\n+\t\t}\n+\t}\n+\tfprintf(stderr, \"]\\n\");\n+\n+}\n+\n+int raw_bitmap_is_subset(struct bitmap *self, struct bitmap *other)\n {\n \tsize_t common_size, i;\n \n@@ -216,7 +241,7 @@ int bitmap_is_subset(struct bitmap *self, struct bitmap *other)\n \treturn 0;\n }\n \n-void bitmap_free(struct bitmap *bitmap)\n+void raw_bitmap_free(struct bitmap *bitmap)\n {\n \tif (!bitmap)\n \t\treturn;\ndiff --git a/ewah/ewok.h b/ewah/ewok.h\nindex 7eb8b9b6301..4fc96fd73d0 100644\n--- a/ewah/ewok.h\n+++ b/ewah/ewok.h\n@@ -171,23 +171,24 @@ struct bitmap {\n \tsize_t word_alloc;\n };\n \n-struct bitmap *bitmap_new(void);\n-struct bitmap *bitmap_word_alloc(size_t word_alloc);\n-struct bitmap *bitmap_dup(const struct bitmap *src);\n-void bitmap_set(struct bitmap *self, size_t pos);\n-void bitmap_unset(struct bitmap *self, size_t pos);\n-int bitmap_get(struct bitmap *self, size_t pos);\n-void bitmap_free(struct bitmap *self);\n-int bitmap_equals(struct bitmap *self, struct bitmap *other);\n-int bitmap_is_subset(struct bitmap *self, struct bitmap *other);\n-\n-struct ewah_bitmap * bitmap_to_ewah(struct bitmap *bitmap);\n-struct bitmap *ewah_to_bitmap(struct ewah_bitmap *ewah);\n-\n-void bitmap_and_not(struct bitmap *self, struct bitmap *other);\n-void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);\n-void bitmap_or(struct bitmap *self, const struct bitmap *other);\n-\n-size_t bitmap_popcount(struct bitmap *self);\n+struct bitmap *raw_bitmap_new(void);\n+struct bitmap *raw_bitmap_word_alloc(size_t word_alloc);\n+struct bitmap *raw_bitmap_dup(const struct bitmap *src);\n+void raw_bitmap_set(struct bitmap *self, size_t pos);\n+void raw_bitmap_unset(struct bitmap *self, size_t pos);\n+int raw_bitmap_get(struct bitmap *self, size_t pos);\n+void raw_bitmap_free(struct bitmap *self);\n+int raw_bitmap_equals(struct bitmap *self, struct bitmap *other);\n+int raw_bitmap_is_subset(struct bitmap *self, struct bitmap *other);\n+\n+struct ewah_bitmap * raw_bitmap_to_ewah(struct bitmap *bitmap);\n+struct bitmap *ewah_to_raw_bitmap(struct ewah_bitmap *ewah);\n+\n+void raw_bitmap_and_not(struct bitmap *self, struct bitmap *other);\n+void raw_bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);\n+void raw_bitmap_or(struct bitmap *self, const struct bitmap *other);\n+void ewah_bitmap_print(struct ewah_bitmap *bm);\n+\n+size_t raw_bitmap_popcount(struct bitmap *self);\n \n #endif\ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex a213f5eddc5..7ea2f8e0065 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -12,22 +12,27 @@\n #include \"hash-lookup.h\"\n #include \"pack-objects.h\"\n #include \"commit-reach.h\"\n+#include \"chunk-format.h\"\n #include \"prio-queue.h\"\n \n struct bitmapped_commit {\n \tstruct commit *commit;\n-\tstruct ewah_bitmap *bitmap;\n-\tstruct ewah_bitmap *write_as;\n+\tvoid *bitmap;\n+\tvoid *write_as;\n+\n+\tuint32_t bitmap_type;\n \tint flags;\n \tint xor_offset;\n \tuint32_t commit_pos;\n };\n \n-struct bitmap_writer {\n-\tstruct ewah_bitmap *commits;\n-\tstruct ewah_bitmap *trees;\n-\tstruct ewah_bitmap *blobs;\n-\tstruct ewah_bitmap *tags;\n+struct write_bitmap_context {\n+\tvoid *commits;\n+\tvoid *trees;\n+\tvoid *blobs;\n+\tvoid *tags;\n+\n+\tuint32_t bitmap_type;\n \n \tkh_oid_map_t *bitmaps;\n \tstruct packing_data *to_pack;\n@@ -35,18 +40,37 @@ struct bitmap_writer {\n \tstruct bitmapped_commit *selected;\n \tunsigned int selected_nr, selected_alloc;\n \n+\tstruct pack_idx_entry **index;\n+\tuint32_t index_nr;\n+\toff_t *offsets;\n+\tuint32_t *commit_positions;\n+\n+\tvoid *lookup_table;\n+\tvoid *hash_cache;\n+\n \tstruct progress *progress;\n \tint show_progress;\n \tunsigned char pack_checksum[GIT_MAX_RAWSZ];\n };\n \n-static struct bitmap_writer writer;\n+static struct write_bitmap_context writer;\n \n void bitmap_writer_show_progress(int show)\n {\n \twriter.show_progress = show;\n }\n \n+void bitmap_writer_init_bm_type(unsigned version_type)\n+{\n+\tif (version_type & BITMAP_SET_ROARING_BITMAP) {\n+\t\twriter.bitmap_type = ROARING;\n+\t\tset_bitmap_type(ROARING);\n+\t}\n+\telse if (version_type & BITMAP_SET_EWAH_BITMAP) {\n+\t\twriter.bitmap_type = EWAH;\n+\t\tset_bitmap_type(EWAH);\n+\t}\n+}\n /**\n  * Build the initial type index for the packfile or multi-pack-index\n  */\n@@ -56,11 +80,13 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,\n {\n \tuint32_t i;\n \n-\twriter.commits = ewah_new();\n-\twriter.trees = ewah_new();\n-\twriter.blobs = ewah_new();\n-\twriter.tags = ewah_new();\n+\twriter.commits = roaring_or_ewah_bitmap_init();\n+\twriter.trees = roaring_or_ewah_bitmap_init();\n+\twriter.blobs = roaring_or_ewah_bitmap_init();\n+\twriter.tags = roaring_or_ewah_bitmap_init();\n \tALLOC_ARRAY(to_pack->in_pack_pos, to_pack->nr_objects);\n+\twriter.index = index;\n+\twriter.index_nr = index_nr;\n \n \tfor (i = 0; i < index_nr; ++i) {\n \t\tstruct object_entry *entry = (struct object_entry *)index[i];\n@@ -84,25 +110,25 @@ void bitmap_writer_build_type_index(struct packing_data *to_pack,\n \n \t\tswitch (real_type) {\n \t\tcase OBJ_COMMIT:\n-\t\t\tewah_set(writer.commits, i);\n+\t\t\troaring_or_ewah_bitmap_set(writer.commits, i);\n \t\t\tbreak;\n \n \t\tcase OBJ_TREE:\n-\t\t\tewah_set(writer.trees, i);\n+\t\t\troaring_or_ewah_bitmap_set(writer.trees, i);\n \t\t\tbreak;\n \n \t\tcase OBJ_BLOB:\n-\t\t\tewah_set(writer.blobs, i);\n+\t\t\troaring_or_ewah_bitmap_set(writer.blobs, i);\n \t\t\tbreak;\n \n \t\tcase OBJ_TAG:\n-\t\t\tewah_set(writer.tags, i);\n+\t\t\troaring_or_ewah_bitmap_set(writer.tags, i);\n \t\t\tbreak;\n \n \t\tdefault:\n \t\t\tdie(\"Missing type information for %s (%d/%d)\",\n-\t\t\t    oid_to_hex(&entry->idx.oid), real_type,\n-\t\t\t    oe_type(entry));\n+\t\t\t\toid_to_hex(&entry->idx.oid), real_type,\n+\t\t\t\toe_type(entry));\n \t\t}\n \t}\n }\n@@ -184,8 +210,8 @@ static void compute_xor_offsets(void)\n \n struct bb_commit {\n \tstruct commit_list *reverse_edges;\n-\tstruct bitmap *commit_mask;\n-\tstruct bitmap *bitmap;\n+\tvoid *commit_mask;\n+\tvoid *bitmap;\n \tunsigned selected:1,\n \t\t maximal:1;\n \tunsigned idx; /* within selected array */\n@@ -200,7 +226,7 @@ struct bitmap_builder {\n };\n \n static void bitmap_builder_init(struct bitmap_builder *bb,\n-\t\t\t\tstruct bitmap_writer *writer,\n+\t\t\t\tstruct write_bitmap_context *writer,\n \t\t\t\tstruct bitmap_index *old_bitmap)\n {\n \tstruct rev_info revs;\n@@ -225,8 +251,8 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \t\tent->maximal = 1;\n \t\tent->idx = i;\n \n-\t\tent->commit_mask = bitmap_new();\n-\t\tbitmap_set(ent->commit_mask, i);\n+\t\tent->commit_mask = roaring_or_raw_bitmap_new();\n+\t\troaring_or_raw_bitmap_set(ent->commit_mask, i);\n \n \t\tadd_pending_object(&revs, &c->object, \"\");\n \t}\n@@ -278,18 +304,18 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \t\t\tint c_not_p, p_not_c;\n \n \t\t\tif (!p_ent->commit_mask) {\n-\t\t\t\tp_ent->commit_mask = bitmap_new();\n+\t\t\t\tp_ent->commit_mask = roaring_or_raw_bitmap_new();\n \t\t\t\tc_not_p = 1;\n \t\t\t\tp_not_c = 0;\n \t\t\t} else {\n-\t\t\t\tc_not_p = bitmap_is_subset(c_ent->commit_mask, p_ent->commit_mask);\n-\t\t\t\tp_not_c = bitmap_is_subset(p_ent->commit_mask, c_ent->commit_mask);\n+\t\t\t\tc_not_p = roaring_or_raw_bitmap_is_subset(c_ent->commit_mask, p_ent->commit_mask);\n+\t\t\t\tp_not_c = roaring_or_raw_bitmap_is_subset(p_ent->commit_mask, c_ent->commit_mask);\n \t\t\t}\n \n \t\t\tif (!c_not_p)\n \t\t\t\tcontinue;\n \n-\t\t\tbitmap_or(p_ent->commit_mask, c_ent->commit_mask);\n+\t\t\troaring_or_raw_bitmap_or(p_ent->commit_mask, c_ent->commit_mask);\n \n \t\t\tif (p_not_c)\n \t\t\t\tp_ent->maximal = 1;\n@@ -312,7 +338,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \t\t}\n \n next:\n-\t\tbitmap_free(c_ent->commit_mask);\n+\t\troaring_or_raw_bitmap_free(c_ent->commit_mask);\n \t\tc_ent->commit_mask = NULL;\n \t}\n \n@@ -337,7 +363,7 @@ static void bitmap_builder_clear(struct bitmap_builder *bb)\n \tbb->commits_nr = bb->commits_alloc = 0;\n }\n \n-static int fill_bitmap_tree(struct bitmap *bitmap,\n+static int fill_bitmap_tree(void *bitmap,\n \t\t\t    struct tree *tree)\n {\n \tint found;\n@@ -352,9 +378,9 @@ static int fill_bitmap_tree(struct bitmap *bitmap,\n \tpos = find_object_pos(&tree->object.oid, &found);\n \tif (!found)\n \t\treturn -1;\n-\tif (bitmap_get(bitmap, pos))\n+\tif (roaring_or_raw_bitmap_get(bitmap, pos))\n \t\treturn 0;\n-\tbitmap_set(bitmap, pos);\n+\troaring_or_raw_bitmap_set(bitmap, pos);\n \n \tif (parse_tree(tree) < 0)\n \t\tdie(\"unable to load tree object %s\",\n@@ -372,7 +398,7 @@ static int fill_bitmap_tree(struct bitmap *bitmap,\n \t\t\tpos = find_object_pos(&entry.oid, &found);\n \t\t\tif (!found)\n \t\t\t\treturn -1;\n-\t\t\tbitmap_set(bitmap, pos);\n+\t\t\troaring_or_raw_bitmap_set(bitmap, pos);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\t/* Gitlink, etc; not reachable */\n@@ -394,7 +420,7 @@ static int fill_bitmap_commit(struct bb_commit *ent,\n \tint found;\n \tuint32_t pos;\n \tif (!ent->bitmap)\n-\t\tent->bitmap = bitmap_new();\n+\t\tent->bitmap = roaring_or_raw_bitmap_new();\n \n \tprio_queue_put(queue, commit);\n \n@@ -403,13 +429,13 @@ static int fill_bitmap_commit(struct bb_commit *ent,\n \t\tstruct commit *c = prio_queue_get(queue);\n \n \t\tif (old_bitmap && mapping) {\n-\t\t\tstruct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);\n+\t\t\tvoid *old = bitmap_for_commit(old_bitmap, c);\n \t\t\t/*\n \t\t\t * If this commit has an old bitmap, then translate that\n \t\t\t * bitmap and add its bits to this one. No need to walk\n \t\t\t * parents or the tree for this commit.\n \t\t\t */\n-\t\t\tif (old && !rebuild_bitmap(mapping, old, ent->bitmap))\n+\t\t\tif (old && !rebuild_bitmap(old_bitmap, mapping, old, ent->bitmap))\n \t\t\t\tcontinue;\n \t\t}\n \n@@ -420,15 +446,15 @@ static int fill_bitmap_commit(struct bb_commit *ent,\n \t\tpos = find_object_pos(&c->object.oid, &found);\n \t\tif (!found)\n \t\t\treturn -1;\n-\t\tbitmap_set(ent->bitmap, pos);\n+\t\troaring_or_raw_bitmap_set(ent->bitmap, pos);\n \t\tprio_queue_put(tree_queue, get_commit_tree(c));\n \n \t\tfor (p = c->parents; p; p = p->next) {\n \t\t\tpos = find_object_pos(&p->item->object.oid, &found);\n \t\t\tif (!found)\n \t\t\t\treturn -1;\n-\t\t\tif (!bitmap_get(ent->bitmap, pos)) {\n-\t\t\t\tbitmap_set(ent->bitmap, pos);\n+\t\t\tif (!roaring_or_raw_bitmap_get(ent->bitmap, pos)) {\n+\t\t\t\troaring_or_raw_bitmap_set(ent->bitmap, pos);\n \t\t\t\tprio_queue_put(queue, p->item);\n \t\t\t}\n \t\t}\n@@ -447,8 +473,15 @@ static void store_selected(struct bb_commit *ent, struct commit *commit)\n \tstruct bitmapped_commit *stored = &writer.selected[ent->idx];\n \tkhiter_t hash_pos;\n \tint hash_ret;\n-\n-\tstored->bitmap = bitmap_to_ewah(ent->bitmap);\n+\tenum bitmap_type bm_type = get_bitmap_type();\n+\n+\tif (bm_type == EWAH)\n+\t\tstored->bitmap = raw_bitmap_to_ewah(ent->bitmap);\n+\telse if (bm_type == ROARING) {\n+\t\tstored->bitmap = roaring_bitmap_copy(ent->bitmap);\n+\t\tstored->write_as = stored->bitmap;\n+\t\tstored->xor_offset = 0;\n+\t}\n \n \thash_pos = kh_put_oid_map(writer.bitmaps, commit->object.oid, &hash_ret);\n \tif (hash_ret == 0)\n@@ -506,16 +539,16 @@ int bitmap_writer_build(struct packing_data *to_pack)\n \t\t\t\tbb_data_at(&bb.data, child);\n \n \t\t\tif (child_ent->bitmap)\n-\t\t\t\tbitmap_or(child_ent->bitmap, ent->bitmap);\n+\t\t\t\troaring_or_raw_bitmap_or(child_ent->bitmap, ent->bitmap);\n \t\t\telse if (reused)\n-\t\t\t\tchild_ent->bitmap = bitmap_dup(ent->bitmap);\n+\t\t\t\tchild_ent->bitmap = roaring_or_raw_bitmap_copy(ent->bitmap);\n \t\t\telse {\n \t\t\t\tchild_ent->bitmap = ent->bitmap;\n \t\t\t\treused = 1;\n \t\t\t}\n \t\t}\n \t\tif (!reused)\n-\t\t\tbitmap_free(ent->bitmap);\n+\t\t\troaring_or_raw_bitmap_free(ent->bitmap);\n \t\tent->bitmap = NULL;\n \t}\n \tclear_prio_queue(&queue);\n@@ -529,7 +562,7 @@ int bitmap_writer_build(struct packing_data *to_pack)\n \n \tstop_progress(&writer.progress);\n \n-\tif (closed)\n+\tif (closed && writer.bitmap_type == EWAH)\n \t\tcompute_xor_offsets();\n \treturn closed ? 0 : -1;\n }\n@@ -626,7 +659,7 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,\n }\n \n \n-static int hashwrite_ewah_helper(void *f, const void *buf, size_t len)\n+static int hashwrite_bitmap_helper(void *f, const void *buf, size_t len)\n {\n \t/* hashwrite will die on error */\n \thashwrite(f, buf, len);\n@@ -638,7 +671,7 @@ static int hashwrite_ewah_helper(void *f, const void *buf, size_t len)\n  */\n static inline void dump_bitmap(struct hashfile *f, struct ewah_bitmap *bitmap)\n {\n-\tif (ewah_serialize_to(bitmap, hashwrite_ewah_helper, f) < 0)\n+\tif (ewah_serialize_to(bitmap, hashwrite_bitmap_helper, f) < 0)\n \t\tdie(\"Failed to write bitmap index\");\n }\n \n@@ -649,10 +682,15 @@ static const struct object_id *oid_access(size_t pos, const void *table)\n }\n \n static void write_selected_commits_v1(struct hashfile *f,\n-\t\t\t\t      uint32_t *commit_positions,\n-\t\t\t\t      off_t *offsets)\n+\t\t\t\t      struct pack_idx_entry **index,\n+\t\t\t\t      uint32_t index_nr)\n {\n \tint i;\n+\tuint32_t *commit_positions = writer.commit_positions;\n+\toff_t *offsets = writer.offsets;\n+\n+\tif (!commit_positions)\n+\t\tdie(_(\"commit positions are not initialized properly\\n\"));\n \n \tfor (i = 0; i < writer.selected_nr; ++i) {\n \t\tstruct bitmapped_commit *stored = &writer.selected[i];\n@@ -683,11 +721,13 @@ static int table_cmp(const void *_va, const void *_vb, void *_data)\n }\n \n static void write_lookup_table(struct hashfile *f,\n-\t\t\t       uint32_t *commit_positions,\n-\t\t\t       off_t *offsets)\n+\t\t\t       struct pack_idx_entry **index,\n+\t\t\t       uint32_t index_nr)\n {\n \tuint32_t i;\n \tuint32_t *table, *table_inv;\n+\tuint32_t *commit_positions = writer.commit_positions;\n+\toff_t *offsets = writer.offsets;\n \n \tALLOC_ARRAY(table, writer.selected_nr);\n \tALLOC_ARRAY(table_inv, writer.selected_nr);\n@@ -758,59 +798,188 @@ void bitmap_writer_set_checksum(const unsigned char *sha1)\n \thashcpy(writer.pack_checksum, sha1);\n }\n \n-void bitmap_writer_finish(struct pack_idx_entry **index,\n-\t\t\t  uint32_t index_nr,\n-\t\t\t  const char *filename,\n-\t\t\t  uint16_t options)\n+static size_t compute_pt_serialize_type_indexes_size(void)\n {\n-\tstatic uint16_t default_version = 1;\n-\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n-\tstruct strbuf tmp_file = STRBUF_INIT;\n-\tstruct hashfile *f;\n-\tuint32_t *commit_positions = NULL;\n-\toff_t *offsets = NULL;\n-\tuint32_t i;\n+\tsize_t type_index_size = 0;\n+\ttype_index_size += roaring_bitmap_network_portable_size_in_bytes(writer.commits);\n+\ttype_index_size += roaring_bitmap_network_portable_size_in_bytes(writer.trees);\n+\ttype_index_size += roaring_bitmap_network_portable_size_in_bytes(writer.blobs);\n+\ttype_index_size += roaring_bitmap_network_portable_size_in_bytes(writer.tags);\n+\treturn type_index_size;\n+}\n \n-\tstruct bitmap_disk_header header;\n+static size_t compute_pt_serialize_commit_bitmap_sec_size(void)\n+{\n+\tsize_t  size = 0;\n+\tint i;\n \n-\tint fd = odb_mkstemp(&tmp_file, \"pack/tmp_bitmap_XXXXXX\");\n+\tfor (i = 0; i < writer.selected_nr; ++i) {\n+\t\tstruct bitmapped_commit *stored = &writer.selected[i];\n \n-\tf = hashfd(fd, tmp_file.buf);\n+\t\tsize += sizeof(uint32_t) + sizeof(uint8_t) * 2;\n+\t\tsize += roaring_bitmap_network_portable_size_in_bytes(stored->write_as);\n+\t}\n+\treturn size;\n+}\n \n-\tmemcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));\n-\theader.version = htons(default_version);\n-\theader.options = htons(flags | options);\n-\theader.entry_count = htonl(writer.selected_nr);\n-\thashcpy(header.checksum, writer.pack_checksum);\n+static size_t compute_hash_cache_size(void)\n+{\n+\treturn st_mult(writer.index_nr, sizeof(uint32_t));\n+}\n \n-\thashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);\n+static size_t compute_bitmap_lookup_table_size(void)\n+{\n+\treturn st_mult(writer.selected_nr, BITMAP_LOOKUP_TABLE_TRIPLET_WIDTH);\n+}\n+\n+static int write_bitmap_type_indexes(struct hashfile *f, void *data)\n+{\n+\tstruct write_bitmap_context *writer = data;\n+\troaring_bitmap_portable_network_serialize(writer->commits, hashwrite_bitmap_helper, f);\n+\troaring_bitmap_portable_network_serialize(writer->trees, hashwrite_bitmap_helper, f);\n+\troaring_bitmap_portable_network_serialize(writer->blobs, hashwrite_bitmap_helper, f);\n+\troaring_bitmap_portable_network_serialize(writer->tags, hashwrite_bitmap_helper, f);\n+\treturn 0;\n+}\n+\n+static int write_reachability_roaring_bitmaps(struct hashfile *f, void *data)\n+{\n+\tstruct write_bitmap_context *writer = data;\n+\tuint32_t *commit_positions = writer->commit_positions;\n+\tint i;\n+\n+\tif (!commit_positions)\n+\t\tdie(_(\"commit positions are not initialized properly\\n\"));\n+\n+\tfor (i = 0; i < writer->selected_nr; ++i) {\n+\t\tstruct bitmapped_commit *stored = &writer->selected[i];\n+\n+\t\tif (writer->offsets)\n+\t\t\twriter->offsets[i] = hashfile_total(f);\n+\n+\t\thashwrite_be32(f, commit_positions[i]);\n+\t\thashwrite_u8(f, stored->xor_offset);\n+\t\thashwrite_u8(f, stored->flags);\n+\n+\t\troaring_bitmap_portable_network_serialize(stored->write_as, hashwrite_bitmap_helper, f);\n+\t}\n+\treturn 0;\n+}\n+\n+static int write_chunk_hash_cache(struct hashfile *f, void *data)\n+{\n+\tstruct write_bitmap_context *writer = data;\n+\twrite_hash_cache(f, writer->index, writer->index_nr);\n+\treturn 0;\n+}\n+\n+static int write_chunk_lookup_table(struct hashfile *f, void *data)\n+{\n+\tstruct write_bitmap_context *writer = data;\n+\twrite_lookup_table(f, writer->index, writer->index_nr);\n+\treturn 0;\n+}\n+\n+static void write_roaring_bitmap_file(struct hashfile *f,\n+\t\t\t       const char *filename,\n+\t\t\t       uint16_t options)\n+{\n+\tstruct chunkfile *cf = init_chunkfile(f);\n+\n+\tadd_chunk(cf, BITMAP_TYPE_INDEXES,\n+\t\t  compute_pt_serialize_type_indexes_size(),\n+\t\t  write_bitmap_type_indexes);\n+\n+\ttrace2_region_enter(\"pack-bitmap-write\", \"write-roaring-bitmap\", the_repository);\n+\tadd_chunk(cf, BITMAP_REACHABILITY_BITMAPS,\n+\t\t  compute_pt_serialize_commit_bitmap_sec_size(),\n+\t\t  write_reachability_roaring_bitmaps);\n+\n+\tif (options & BITMAP_OPT_HASH_CACHE)\n+\t\tadd_chunk(cf, BITMAP_HASH_CACHE,\n+\t\t\tcompute_hash_cache_size(),\n+\t\t\twrite_chunk_hash_cache);\n+\n+\tif (options & BITMAP_OPT_LOOKUP_TABLE)\n+\t\tadd_chunk(cf, BITMAP_LOOKUP_TABLE,\n+\t\t\tcompute_bitmap_lookup_table_size(),\n+\t\t\twrite_chunk_lookup_table);\n+\n+\thashwrite_u8(f, get_num_chunks(cf));\n+\twrite_chunkfile(cf, &writer);\n+\ttrace2_region_leave(\"pack-bitmap-write\", \"write-roaring-bitmap\", the_repository);\n+}\n+\n+static void write_ewah_bitmap_file(struct hashfile *f,\n+\t\t\t    struct pack_idx_entry **index,\n+\t\t\t    uint32_t index_nr,\n+\t\t\t    const char *filename,\n+\t\t\t    uint16_t options)\n+{\n \tdump_bitmap(f, writer.commits);\n \tdump_bitmap(f, writer.trees);\n \tdump_bitmap(f, writer.blobs);\n \tdump_bitmap(f, writer.tags);\n \n+\twrite_selected_commits_v1(f, index, index_nr);\n+\n \tif (options & BITMAP_OPT_LOOKUP_TABLE)\n-\t\tCALLOC_ARRAY(offsets, index_nr);\n+\t\twrite_lookup_table(f, index, index_nr);\n \n+\tif (options & BITMAP_OPT_HASH_CACHE)\n+\t\twrite_hash_cache(f, index, index_nr);\n+}\n+\n+static void fill_writer_commit_positions(void)\n+{\n+\tuint32_t *commit_positions = NULL;\n+\tint i;\n \tALLOC_ARRAY(commit_positions, writer.selected_nr);\n \n \tfor (i = 0; i < writer.selected_nr; i++) {\n \t\tstruct bitmapped_commit *stored = &writer.selected[i];\n-\t\tint commit_pos = oid_pos(&stored->commit->object.oid, index, index_nr, oid_access);\n+\t\tint commit_pos = oid_pos(&stored->commit->object.oid, writer.index, writer.index_nr, oid_access);\n \n \t\tif (commit_pos < 0)\n \t\t\tBUG(_(\"trying to write commit not in index\"));\n \n \t\tcommit_positions[i] = commit_pos;\n \t}\n+\twriter.commit_positions = commit_positions;\n+}\n+\n+void bitmap_writer_finish(struct pack_idx_entry **index,\n+\t\t\t  uint32_t index_nr,\n+\t\t\t  const char *filename,\n+\t\t\t  uint16_t options)\n+{\n+\tstruct strbuf tmp_file = STRBUF_INIT;\n+\tstruct hashfile *f = NULL;\n+\tstatic uint16_t version = 1;\n+\tstatic uint16_t flags = BITMAP_OPT_FULL_DAG;\n+\tstruct bitmap_disk_header header;\n \n-\twrite_selected_commits_v1(f, commit_positions, offsets);\n+\tint fd = odb_mkstemp(&tmp_file, \"pack/tmp_bitmap_XXXXXX\");\n+\tf = hashfd(fd, tmp_file.buf);\n+\tif (writer.bitmap_type & ROARING)\n+\t\tversion = 2;\n+\n+\tmemcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));\n+\theader.version = htons(version);\n+\theader.options = htons(flags | options);\n+\theader.entry_count = htonl(writer.selected_nr);\n+\thashcpy(header.checksum, writer.pack_checksum);\n+\n+\thashwrite(f, &header, sizeof(header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz);\n \n \tif (options & BITMAP_OPT_LOOKUP_TABLE)\n-\t\twrite_lookup_table(f, commit_positions, offsets);\n+\t\tCALLOC_ARRAY(writer.offsets, index_nr);\n \n-\tif (options & BITMAP_OPT_HASH_CACHE)\n-\t\twrite_hash_cache(f, index, index_nr);\n+\tfill_writer_commit_positions();\n+\tif (writer.bitmap_type == ROARING)\n+\t\twrite_roaring_bitmap_file(f, filename, options);\n+\telse if (writer.bitmap_type == EWAH)\n+\t\twrite_ewah_bitmap_file(f, index, index_nr, filename, options);\n \n \tfinalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,\n \t\t\t  CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);\n@@ -820,8 +989,7 @@ void bitmap_writer_finish(struct pack_idx_entry **index,\n \n \tif (rename(tmp_file.buf, filename))\n \t\tdie_errno(\"unable to rename temporary bitmap file to '%s'\", filename);\n-\n \tstrbuf_release(&tmp_file);\n-\tfree(commit_positions);\n-\tfree(offsets);\n+\tfree(writer.offsets);\n+\tfree(writer.commit_positions);\n }\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex 9a208abc1fd..c1a0bc26d02 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -936,7 +936,7 @@ static void show_object(struct object *object, const char *name, void *data_)\n \t\tbitmap_pos = ext_index_add_object(data->bitmap_git, object,\n \t\t\t\t\t\t  name);\n \n-\tbitmap_set(data->base, bitmap_pos);\n+\traw_bitmap_set(data->base, bitmap_pos);\n }\n \n static void show_commit(struct commit *commit, void *data)\n@@ -950,19 +950,19 @@ static int add_to_include_set(struct bitmap_index *bitmap_git,\n {\n \tstruct ewah_bitmap *partial;\n \n-\tif (data->seen && bitmap_get(data->seen, bitmap_pos))\n+\tif (data->seen && raw_bitmap_get(data->seen, bitmap_pos))\n \t\treturn 0;\n \n-\tif (bitmap_get(data->base, bitmap_pos))\n+\tif (raw_bitmap_get(data->base, bitmap_pos))\n \t\treturn 0;\n \n \tpartial = bitmap_for_commit(bitmap_git, commit);\n \tif (partial) {\n-\t\tbitmap_or_ewah(data->base, partial);\n+\t\traw_bitmap_or_ewah(data->base, partial);\n \t\treturn 0;\n \t}\n \n-\tbitmap_set(data->base, bitmap_pos);\n+\traw_bitmap_set(data->base, bitmap_pos);\n \treturn 1;\n }\n \n@@ -999,8 +999,8 @@ static int should_include_obj(struct object *obj, void *_data)\n \tbitmap_pos = bitmap_position(data->bitmap_git, &obj->oid);\n \tif (bitmap_pos < 0)\n \t\treturn 1;\n-\tif ((data->seen && bitmap_get(data->seen, bitmap_pos)) ||\n-\t     bitmap_get(data->base, bitmap_pos)) {\n+\tif ((data->seen && raw_bitmap_get(data->seen, bitmap_pos)) ||\n+\t     raw_bitmap_get(data->base, bitmap_pos)) {\n \t\tobj->flags |= SEEN;\n \t\treturn 0;\n \t}\n@@ -1017,9 +1017,9 @@ static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,\n \t\treturn 0;\n \n \tif (!*base)\n-\t\t*base = ewah_to_bitmap(or_with);\n+\t\t*base = ewah_to_raw_bitmap(or_with);\n \telse\n-\t\tbitmap_or_ewah(*base, or_with);\n+\t\traw_bitmap_or_ewah(*base, or_with);\n \n \treturn 1;\n }\n@@ -1080,7 +1080,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,\n \t\troots = roots->next;\n \t\tpos = bitmap_position(bitmap_git, &object->oid);\n \n-\t\tif (pos < 0 || base == NULL || !bitmap_get(base, pos)) {\n+\t\tif (pos < 0 || base == NULL || !raw_bitmap_get(base, pos)) {\n \t\t\tobject->flags &= ~UNINTERESTING;\n \t\t\tadd_pending_object(revs, object, \"\");\n \t\t\tneeds_walk = 1;\n@@ -1094,7 +1094,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,\n \t\tstruct bitmap_show_data show_data;\n \n \t\tif (!base)\n-\t\t\tbase = bitmap_new();\n+\t\t\tbase = raw_bitmap_new();\n \n \t\tincdata.bitmap_git = bitmap_git;\n \t\tincdata.base = base;\n@@ -1133,7 +1133,7 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,\n \tfor (i = 0; i < eindex->count; ++i) {\n \t\tstruct object *obj;\n \n-\t\tif (!bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n+\t\tif (!raw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcontinue;\n \n \t\tobj = eindex->objects[i];\n@@ -1256,7 +1256,7 @@ static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,\n \t\t\t\t       struct object_list *tip_objects,\n \t\t\t\t       enum object_type type)\n {\n-\tstruct bitmap *result = bitmap_new();\n+\tstruct bitmap *result = raw_bitmap_new();\n \tstruct object_list *p;\n \n \tfor (p = tip_objects; p; p = p->next) {\n@@ -1269,7 +1269,7 @@ static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,\n \t\tif (pos < 0)\n \t\t\tcontinue;\n \n-\t\tbitmap_set(result, pos);\n+\t\traw_bitmap_set(result, pos);\n \t}\n \n \treturn result;\n@@ -1314,12 +1314,12 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,\n \tfor (i = 0; i < eindex->count; i++) {\n \t\tuint32_t pos = i + bitmap_num_objects(bitmap_git);\n \t\tif (eindex->objects[i]->type == type &&\n-\t\t    bitmap_get(to_filter, pos) &&\n-\t\t    !bitmap_get(tips, pos))\n-\t\t\tbitmap_unset(to_filter, pos);\n+\t\t    raw_bitmap_get(to_filter, pos) &&\n+\t\t    !raw_bitmap_get(tips, pos))\n+\t\t\traw_bitmap_unset(to_filter, pos);\n \t}\n \n-\tbitmap_free(tips);\n+\traw_bitmap_free(tips);\n }\n \n static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,\n@@ -1396,22 +1396,22 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,\n \t\t\toffset += ewah_bit_ctz64(word >> offset);\n \t\t\tpos = i * BITS_IN_EWORD + offset;\n \n-\t\t\tif (!bitmap_get(tips, pos) &&\n+\t\t\tif (!raw_bitmap_get(tips, pos) &&\n \t\t\t    get_size_by_pos(bitmap_git, pos) >= limit)\n-\t\t\t\tbitmap_unset(to_filter, pos);\n+\t\t\t\traw_bitmap_unset(to_filter, pos);\n \t\t}\n \t}\n \n \tfor (i = 0; i < eindex->count; i++) {\n \t\tuint32_t pos = i + bitmap_num_objects(bitmap_git);\n \t\tif (eindex->objects[i]->type == OBJ_BLOB &&\n-\t\t    bitmap_get(to_filter, pos) &&\n-\t\t    !bitmap_get(tips, pos) &&\n+\t\t    raw_bitmap_get(to_filter, pos) &&\n+\t\t    !raw_bitmap_get(tips, pos) &&\n \t\t    get_size_by_pos(bitmap_git, pos) >= limit)\n-\t\t\tbitmap_unset(to_filter, pos);\n+\t\t\traw_bitmap_unset(to_filter, pos);\n \t}\n \n-\tbitmap_free(tips);\n+\traw_bitmap_free(tips);\n }\n \n static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git,\n@@ -1597,7 +1597,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,\n \t\tBUG(\"failed to perform bitmap walk\");\n \n \tif (haves_bitmap)\n-\t\tbitmap_and_not(wants_bitmap, haves_bitmap);\n+\t\traw_bitmap_and_not(wants_bitmap, haves_bitmap);\n \n \tfilter_bitmap(bitmap_git,\n \t\t      (revs->filter.choice && filter_provided_objects) ? NULL : wants,\n@@ -1702,14 +1702,14 @@ static int try_partial_reuse(struct packed_git *pack,\n \t\t * to REF_DELTA on the fly. Better to just let the normal\n \t\t * object_entry code path handle it.\n \t\t */\n-\t\tif (!bitmap_get(reuse, base_pos))\n+\t\tif (!raw_bitmap_get(reuse, base_pos))\n \t\t\treturn 0;\n \t}\n \n \t/*\n \t * If we got here, then the object is OK to reuse. Mark it.\n \t */\n-\tbitmap_set(reuse, pos);\n+\traw_bitmap_set(reuse, pos);\n \treturn 0;\n }\n \n@@ -1758,7 +1758,7 @@ int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,\n \tif (i > objects_nr / BITS_IN_EWORD)\n \t\ti = objects_nr / BITS_IN_EWORD;\n \n-\treuse = bitmap_word_alloc(i);\n+\treuse = raw_bitmap_word_alloc(i);\n \tmemset(reuse->words, 0xFF, i * sizeof(eword_t));\n \n \tfor (; i < result->word_alloc; ++i) {\n@@ -1789,9 +1789,9 @@ int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,\n done:\n \tunuse_pack(&w_curs);\n \n-\t*entries = bitmap_popcount(reuse);\n+\t*entries = raw_bitmap_popcount(reuse);\n \tif (!*entries) {\n-\t\tbitmap_free(reuse);\n+\t\traw_bitmap_free(reuse);\n \t\treturn -1;\n \t}\n \n@@ -1799,7 +1799,7 @@ done:\n \t * Drop any reused objects from the result, since they will not\n \t * need to be handled separately.\n \t */\n-\tbitmap_and_not(result, reuse);\n+\traw_bitmap_and_not(result, reuse);\n \t*packfile_out = pack;\n \t*reuse_out = reuse;\n \treturn 0;\n@@ -1814,7 +1814,7 @@ int bitmap_walk_contains(struct bitmap_index *bitmap_git,\n \t\treturn 0;\n \n \tidx = bitmap_position(bitmap_git, oid);\n-\treturn idx >= 0 && bitmap_get(bitmap, idx);\n+\treturn idx >= 0 && raw_bitmap_get(bitmap, idx);\n }\n \n void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,\n@@ -1853,7 +1853,7 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,\n \n \tfor (i = 0; i < eindex->count; ++i) {\n \t\tif (eindex->objects[i]->type == type &&\n-\t\t\tbitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n+\t\t\traw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcount++;\n \t}\n \n@@ -1896,19 +1896,19 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,\n \tenum object_type bitmap_type = OBJ_NONE;\n \tint bitmaps_nr = 0;\n \n-\tif (bitmap_get(tdata->commits, pos)) {\n+\tif (raw_bitmap_get(tdata->commits, pos)) {\n \t\tbitmap_type = OBJ_COMMIT;\n \t\tbitmaps_nr++;\n \t}\n-\tif (bitmap_get(tdata->trees, pos)) {\n+\tif (raw_bitmap_get(tdata->trees, pos)) {\n \t\tbitmap_type = OBJ_TREE;\n \t\tbitmaps_nr++;\n \t}\n-\tif (bitmap_get(tdata->blobs, pos)) {\n+\tif (raw_bitmap_get(tdata->blobs, pos)) {\n \t\tbitmap_type = OBJ_BLOB;\n \t\tbitmaps_nr++;\n \t}\n-\tif (bitmap_get(tdata->tags, pos)) {\n+\tif (raw_bitmap_get(tdata->tags, pos)) {\n \t\tbitmap_type = OBJ_TAG;\n \t\tbitmaps_nr++;\n \t}\n@@ -1939,7 +1939,7 @@ static void test_show_object(struct object *object, const char *name,\n \t\tdie(_(\"object not in bitmap: '%s'\"), oid_to_hex(&object->oid));\n \ttest_bitmap_type(tdata, object, bitmap_pos);\n \n-\tbitmap_set(tdata->base, bitmap_pos);\n+\traw_bitmap_set(tdata->base, bitmap_pos);\n \tdisplay_progress(tdata->prg, ++tdata->seen);\n }\n \n@@ -1954,7 +1954,7 @@ static void test_show_commit(struct commit *commit, void *data)\n \t\tdie(_(\"object not in bitmap: '%s'\"), oid_to_hex(&commit->object.oid));\n \ttest_bitmap_type(tdata, &commit->object, bitmap_pos);\n \n-\tbitmap_set(tdata->base, bitmap_pos);\n+\traw_bitmap_set(tdata->base, bitmap_pos);\n \tdisplay_progress(tdata->prg, ++tdata->seen);\n }\n \n@@ -1985,7 +1985,7 @@ void test_bitmap_walk(struct rev_info *revs)\n \t\tfprintf_ln(stderr, \"Found bitmap for '%s'. %d bits / %08x checksum\",\n \t\t\toid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));\n \n-\t\tresult = ewah_to_bitmap(bm);\n+\t\tresult = ewah_to_raw_bitmap(bm);\n \t}\n \n \tif (!result)\n@@ -1995,17 +1995,17 @@ void test_bitmap_walk(struct rev_info *revs)\n \trevs->tree_objects = 1;\n \trevs->blob_objects = 1;\n \n-\tresult_popcnt = bitmap_popcount(result);\n+\tresult_popcnt = raw_bitmap_popcount(result);\n \n \tif (prepare_revision_walk(revs))\n \t\tdie(_(\"revision walk setup failed\"));\n \n \ttdata.bitmap_git = bitmap_git;\n-\ttdata.base = bitmap_new();\n-\ttdata.commits = ewah_to_bitmap(bitmap_git->commits);\n-\ttdata.trees = ewah_to_bitmap(bitmap_git->trees);\n-\ttdata.blobs = ewah_to_bitmap(bitmap_git->blobs);\n-\ttdata.tags = ewah_to_bitmap(bitmap_git->tags);\n+\ttdata.base = raw_bitmap_new();\n+\ttdata.commits = ewah_to_raw_bitmap(bitmap_git->commits);\n+\ttdata.trees = ewah_to_raw_bitmap(bitmap_git->trees);\n+\ttdata.blobs = ewah_to_raw_bitmap(bitmap_git->blobs);\n+\ttdata.tags = ewah_to_raw_bitmap(bitmap_git->tags);\n \ttdata.prg = start_progress(\"Verifying bitmap entries\", result_popcnt);\n \ttdata.seen = 0;\n \n@@ -2013,17 +2013,17 @@ void test_bitmap_walk(struct rev_info *revs)\n \n \tstop_progress(&tdata.prg);\n \n-\tif (bitmap_equals(result, tdata.base))\n+\tif (raw_bitmap_equals(result, tdata.base))\n \t\tfprintf_ln(stderr, \"OK!\");\n \telse\n \t\tdie(_(\"mismatch in bitmap results\"));\n \n-\tbitmap_free(result);\n-\tbitmap_free(tdata.base);\n-\tbitmap_free(tdata.commits);\n-\tbitmap_free(tdata.trees);\n-\tbitmap_free(tdata.blobs);\n-\tbitmap_free(tdata.tags);\n+\traw_bitmap_free(result);\n+\traw_bitmap_free(tdata.base);\n+\traw_bitmap_free(tdata.commits);\n+\traw_bitmap_free(tdata.trees);\n+\traw_bitmap_free(tdata.blobs);\n+\traw_bitmap_free(tdata.tags);\n \tfree_bitmap_index(bitmap_git);\n }\n \n@@ -2102,7 +2102,7 @@ int rebuild_bitmap(const uint32_t *reposition,\n \n \t\t\tbit_pos = reposition[pos + offset];\n \t\t\tif (bit_pos > 0)\n-\t\t\t\tbitmap_set(dest, bit_pos - 1);\n+\t\t\t\traw_bitmap_set(dest, bit_pos - 1);\n \t\t\telse /* can't reuse, we don't have the object */\n \t\t\t\treturn -1;\n \t\t}\n@@ -2171,8 +2171,8 @@ void free_bitmap_index(struct bitmap_index *b)\n \tfree(b->ext_index.objects);\n \tfree(b->ext_index.hashes);\n \tkh_destroy_oid_pos(b->ext_index.positions);\n-\tbitmap_free(b->result);\n-\tbitmap_free(b->haves);\n+\traw_bitmap_free(b->result);\n+\traw_bitmap_free(b->haves);\n \tif (bitmap_is_midx(b)) {\n \t\t/*\n \t\t * Multi-pack bitmaps need to have resources associated with\n@@ -2265,7 +2265,7 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)\n \tfor (i = 0; i < eindex->count; i++) {\n \t\tstruct object *obj = eindex->objects[i];\n \n-\t\tif (!bitmap_get(result, bitmap_num_objects(bitmap_git) + i))\n+\t\tif (!raw_bitmap_get(result, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcontinue;\n \n \t\tif (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex f0180b5276b..7d71deca023 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -2,11 +2,18 @@\n #define PACK_BITMAP_H\n \n #include \"ewah/ewok.h\"\n+#include \"roaring/roaring.h\"\n+#include \"bitmap.h\"\n #include \"khash.h\"\n #include \"pack.h\"\n #include \"pack-objects.h\"\n #include \"string-list.h\"\n \n+#define BITMAP_TYPE_INDEXES 0x54494458 /* \"TIDX\" */\n+#define BITMAP_REACHABILITY_BITMAPS 0x5242544D /* \"RBTM\" */\n+#define BITMAP_HASH_CACHE 0x424D4843 /* \"BMHC\" */\n+#define BITMAP_LOOKUP_TABLE 0x424D4C54 /* \"BMLT\" */\n+\n struct commit;\n struct repository;\n struct rev_info;\n@@ -23,6 +30,9 @@ struct bitmap_disk_header {\n \n #define NEEDS_BITMAP (1u<<22)\n \n+#define BITMAP_SET_EWAH_BITMAP 0x1\n+#define BITMAP_SET_ROARING_BITMAP (1 << 1)\n+\n /*\n  * The width in bytes of a single triplet in the lookup table\n  * extension:\n@@ -86,16 +96,18 @@ off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);\n \n void bitmap_writer_show_progress(int show);\n void bitmap_writer_set_checksum(const unsigned char *sha1);\n+void bitmap_writer_init_bm_type(unsigned version_type);\n void bitmap_writer_build_type_index(struct packing_data *to_pack,\n \t\t\t\t    struct pack_idx_entry **index,\n \t\t\t\t    uint32_t index_nr);\n uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,\n \t\t\t\tstruct packing_data *mapping);\n-int rebuild_bitmap(const uint32_t *reposition,\n-\t\t   struct ewah_bitmap *source,\n-\t\t   struct bitmap *dest);\n-struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,\n-\t\t\t\t      struct commit *commit);\n+int rebuild_bitmap(struct bitmap_index *bitmap_git,\n+\t\t   const uint32_t *reposition,\n+\t\t   void *source,\n+\t\t   void *dest);\n+void *bitmap_for_commit(struct bitmap_index *bitmap_git,\n+\t\t\tstruct commit *commit);\n void bitmap_writer_select_commits(struct commit **indexed_commits,\n \t\tunsigned int indexed_commits_nr, int max_bitmaps);\n int bitmap_writer_build(struct packing_data *to_pack);\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nindex 7e50f8e7653..d953de6b7fe 100755\n--- a/t/t5310-pack-bitmaps.sh\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -475,4 +475,21 @@ test_expect_success 'truncated bitmap fails gracefully (lookup table)' '\n \ttest_i18ngrep corrupted.bitmap.index stderr\n '\n \n+test_expect_success 'setup test repository (roaring)' '\n+\trm -fr * .git &&\n+\tgit init\n+'\n+setup_bitmap_history\n+\n+test_expect_success 'setup writing roaring bitmaps during repack' '\n+\tgit config repack.writeBitmaps true &&\n+\tgit config pack.useRoaringBitmap true\n+'\n+\n+test_expect_success 'full repack creates roaring bitmaps' '\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace6\" \\\n+\t\tgit repack -ad &&\n+\tgrep \"\\\"label\\\":\\\"write-roaring-bitmap\\\"\" trace6\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"463209","messageId":"7b49bbf2e28d3ff3dd648c51d2d8741e72b0e786.1663609660.git.gitgitgadget@gmail.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"[PATCH 5/5] roaring: teach Git to read roaring bitmaps","fromName":"Abhradeep Chakraborty via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-09-19T17:47:39Z","receivedAt":"2022-09-19T17:48:14Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n\nGit knows how to write roaring bitmaps but it still doesn't know\nhow to read roaring bitmaps. The changes are backward-compatible.\n\nTeach Git to read roaring bitmaps.\n\nMentored-by: Taylor Blau <me@ttaylorr.com>\nMentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\nSigned-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n---\n builtin/pack-objects.c  |  74 ++-\n pack-bitmap.c           | 969 ++++++++++++++++++++++++++++------------\n pack-bitmap.h           |   4 +-\n t/t5310-pack-bitmaps.sh |  84 ++--\n 4 files changed, 798 insertions(+), 333 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 439c5572c18..e4011669889 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -218,7 +218,7 @@ static struct progress *progress_state;\n \n static struct packed_git *reuse_packfile;\n static uint32_t reuse_packfile_objects;\n-static struct bitmap *reuse_packfile_bitmap;\n+static void *reuse_packfile_bitmap;\n \n static int use_bitmap_index_default = 1;\n static int use_bitmap_index = -1;\n@@ -1084,15 +1084,29 @@ static size_t write_reused_pack_verbatim(struct hashfile *out,\n \t\t\t\t\t struct pack_window **w_curs)\n {\n \tsize_t pos = 0;\n-\n-\twhile (pos < reuse_packfile_bitmap->word_alloc &&\n-\t\t\treuse_packfile_bitmap->words[pos] == (eword_t)~0)\n-\t\tpos++;\n+\tenum bitmap_type bm_type = get_bitmap_type();\n+\ttrace2_region_enter(\"pack-objects\", \"write-reuse-v2-pack\", the_repository);\n+\n+\tif (bm_type == EWAH) {\n+\t\tstruct bitmap *raw_reuse_packfile_bitmap = reuse_packfile_bitmap;\n+\t\twhile (pos < raw_reuse_packfile_bitmap->word_alloc &&\n+\t\t\t\traw_reuse_packfile_bitmap->words[pos] == (eword_t)~0)\n+\t\t\tpos++;\n+\t\twritten = (pos * BITS_IN_EWORD);\n+\t}\n+\telse if (bm_type == ROARING) {\n+\t\tuint32_t cardinality = roaring_bitmap_get_cardinality(reuse_packfile_bitmap);\n+\t\twhile (pos < cardinality && roaring_bitmap_contains(reuse_packfile_bitmap, pos))\n+\t\t\tpos++;\n+\t\twritten = pos;\n+\t}\n+\telse\n+\t\tdie(_(\"bitmap type is not initialized\\n\"));\n+\ttrace2_region_leave(\"pack-objects\", \"write-reuse-v2-pack\", the_repository);\n \n \tif (pos) {\n \t\toff_t to_write;\n \n-\t\twritten = (pos * BITS_IN_EWORD);\n \t\tto_write = pack_pos_to_offset(reuse_packfile, written)\n \t\t\t- sizeof(struct pack_header);\n \n@@ -1112,30 +1126,49 @@ static void write_reused_pack(struct hashfile *f)\n \tsize_t i = 0;\n \tuint32_t offset;\n \tstruct pack_window *w_curs = NULL;\n+\tenum bitmap_type bm_type = get_bitmap_type();\n+\ttrace2_region_enter(\"pack-objects\", \"write-reused-pack\", the_repository);\n \n \tif (allow_ofs_delta)\n \t\ti = write_reused_pack_verbatim(f, &w_curs);\n \n-\tfor (; i < reuse_packfile_bitmap->word_alloc; ++i) {\n-\t\teword_t word = reuse_packfile_bitmap->words[i];\n-\t\tsize_t pos = (i * BITS_IN_EWORD);\n+\tif (bm_type == EWAH) {\n+\t\tstruct bitmap *raw_reuse_packfile_bitmap = reuse_packfile_bitmap;\n+\t\tfor (; i < raw_reuse_packfile_bitmap->word_alloc; ++i) {\n+\t\t\teword_t word = raw_reuse_packfile_bitmap->words[i];\n+\t\t\tsize_t pos = (i * BITS_IN_EWORD);\n \n-\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n-\t\t\tif ((word >> offset) == 0)\n-\t\t\t\tbreak;\n+\t\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n \n-\t\t\toffset += ewah_bit_ctz64(word >> offset);\n-\t\t\t/*\n-\t\t\t * Can use bit positions directly, even for MIDX\n-\t\t\t * bitmaps. See comment in try_partial_reuse()\n-\t\t\t * for why.\n-\t\t\t */\n-\t\t\twrite_reused_pack_one(pos + offset, f, &w_curs);\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\t\t/*\n+\t\t\t\t* Can use bit positions directly, even for MIDX\n+\t\t\t\t* bitmaps. See comment in try_partial_reuse()\n+\t\t\t\t* for why.\n+\t\t\t\t*/\n+\t\t\t\twrite_reused_pack_one(pos + offset, f, &w_curs);\n+\t\t\t\tdisplay_progress(progress_state, ++written);\n+\t\t\t}\n+\t\t}\n+\t}\n+\telse if (bm_type == ROARING) {\n+\t\tuint32_t cardinality = roaring_bitmap_get_cardinality(reuse_packfile_bitmap);\n+\t\tuint32_t *bm_arr = NULL;\n+\t\troaring_bitmap_to_uint32_array(reuse_packfile_bitmap, bm_arr);\n+\n+\t\tfor (; i < cardinality; ++i) {\n+\t\t\twrite_reused_pack_one(bm_arr[i], f, &w_curs);\n \t\t\tdisplay_progress(progress_state, ++written);\n \t\t}\n+\t\tfree(bm_arr);\n \t}\n+\telse\n+\t\tdie(_(\"bitmap_type not initialized\"));\n \n \tunuse_pack(&w_curs);\n+\ttrace2_region_leave(\"pack-objects\", \"write-reused-pack\", the_repository);\n }\n \n static void write_excluded_by_configs(void)\n@@ -1260,6 +1293,7 @@ static void write_pack_file(void)\n \t\t\tif (write_bitmap_index) {\n \t\t\t\tbitmap_writer_init_bm_type(write_bitmap_options);\n \t\t\t\tbitmap_writer_set_checksum(hash);\n+\t\t\t\tfprintf(stderr, \"hi man\\n\");\n \t\t\t\tbitmap_writer_build_type_index(\n \t\t\t\t\t&to_pack, written_list, nr_written);\n \t\t\t}\n@@ -1279,9 +1313,11 @@ static void write_pack_file(void)\n \t\t\t\tstop_progress(&progress_state);\n \n \t\t\t\tbitmap_writer_show_progress(progress);\n+\t\t\t\tfprintf(stderr, \"hello I am working good\\n\");\n \t\t\t\tbitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);\n \t\t\t\tif (bitmap_writer_build(&to_pack) < 0)\n \t\t\t\t\tdie(_(\"failed to write bitmap index\"));\n+\t\t\t\tfprintf(stderr, \"after building bitmaps\\n\");\n \t\t\t\tbitmap_writer_finish(written_list, nr_written,\n \t\t\t\t\t\t     tmpname.buf, write_bitmap_options);\n \t\t\t\twrite_bitmap_index = 0;\ndiff --git a/pack-bitmap.c b/pack-bitmap.c\nindex c1a0bc26d02..6c0b72d4503 100644\n--- a/pack-bitmap.c\n+++ b/pack-bitmap.c\n@@ -16,6 +16,7 @@\n #include \"list-objects-filter-options.h\"\n #include \"midx.h\"\n #include \"config.h\"\n+#include \"chunk-format.h\"\n \n /*\n  * An entry on the bitmap index, representing the bitmap for a given\n@@ -23,7 +24,7 @@\n  */\n struct stored_bitmap {\n \tstruct object_id oid;\n-\tstruct ewah_bitmap *root;\n+\tvoid *root;\n \tstruct stored_bitmap *xor;\n \tint flags;\n };\n@@ -66,10 +67,10 @@ struct bitmap_index {\n \t * type. This provides type information when yielding the objects from\n \t * the packfile during a walk, which allows for better delta bases.\n \t */\n-\tstruct ewah_bitmap *commits;\n-\tstruct ewah_bitmap *trees;\n-\tstruct ewah_bitmap *blobs;\n-\tstruct ewah_bitmap *tags;\n+\tvoid *commits;\n+\tvoid *trees;\n+\tvoid *blobs;\n+\tvoid *tags;\n \n \t/* Map from object ID -> `stored_bitmap` for all the bitmapped commits */\n \tkh_oid_map_t *bitmaps;\n@@ -104,28 +105,37 @@ struct bitmap_index {\n \t} ext_index;\n \n \t/* Bitmap result of the last performed walk */\n-\tstruct bitmap *result;\n+\tvoid *result;\n \n \t/* \"have\" bitmap from the last performed walk */\n-\tstruct bitmap *haves;\n+\tvoid *haves;\n \n \t/* Version of the bitmap index */\n \tunsigned int version;\n+\n+\t/* for version 2 */\n+\tunsigned char chunk_nr;\n };\n \n-static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)\n+static void *lookup_stored_bitmap(struct bitmap_index *index, struct stored_bitmap *st)\n {\n-\tstruct ewah_bitmap *parent;\n-\tstruct ewah_bitmap *composed;\n+\tvoid *parent;\n+\tvoid *composed;\n \n \tif (!st->xor)\n \t\treturn st->root;\n \n-\tcomposed = ewah_pool_new();\n-\tparent = lookup_stored_bitmap(st->xor);\n-\tewah_xor(st->root, parent, composed);\n+\tif (index->version == 1) {\n+\t\tcomposed = ewah_pool_new();\n+\t\tparent = lookup_stored_bitmap(index, st->xor);\n+\t\tewah_xor(st->root, parent, composed);\n \n-\tewah_pool_free(st->root);\n+\t\tewah_pool_free(st->root);\n+\t}\n+\telse if (index->version == 2) {\n+\t\tparent = lookup_stored_bitmap(index, st->xor);\n+\t\tcomposed = roaring_bitmap_xor(st->root, parent);\n+\t}\n \tst->root = composed;\n \tst->xor = NULL;\n \n@@ -154,6 +164,20 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)\n \treturn b;\n }\n \n+static roaring_bitmap_t *read_roaring_bitmap(struct bitmap_index *index, size_t  max_limit)\n+{\n+\troaring_bitmap_t *rb = NULL;\n+\tsize_t bitmap_size = roaring_bitmap_portable_network_deserialize_size((const char *)index->map + index->map_pos, max_limit);\n+\n+\tif (bitmap_size ==  0) {\n+\t\terror(_(\"failed to load bitmap index (corrupted?)\"));\n+\t\treturn NULL;\n+\t}\n+\trb = roaring_bitmap_portable_network_deserialize_safe((const char *)index->map + index->map_pos, max_limit);\n+\tindex->map_pos += bitmap_size;\n+\treturn rb;\n+}\n+\n static uint32_t bitmap_num_objects(struct bitmap_index *index)\n {\n \tif (index->midx)\n@@ -161,10 +185,39 @@ static uint32_t bitmap_num_objects(struct bitmap_index *index)\n \treturn index->pack->num_objects;\n }\n \n+static int ewah_load_bitmap_header(struct bitmap_index *index,\n+\t\t\t\t       struct bitmap_disk_header *header,\n+\t\t\t\t       uint32_t flags)\n+{\n+\tssize_t header_size = sizeof(*header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz;\n+\tsize_t cache_size = st_mult(bitmap_num_objects(index), sizeof(uint32_t));\n+\tunsigned char *index_end = index->map + index->map_size - the_hash_algo->rawsz;\n+\n+\tif (flags & BITMAP_OPT_HASH_CACHE) {\n+\t\tif (cache_size > index_end - index->map - header_size)\n+\t\t\treturn error(_(\"corrupted bitmap index file (too short to fit hash cache)\"));\n+\t\tindex->hashes = (void *)(index_end - cache_size);\n+\t\tindex_end -= cache_size;\n+\t}\n+\n+\tif (flags & BITMAP_OPT_LOOKUP_TABLE) {\n+\t\tsize_t table_size = st_mult(ntohl(header->entry_count),\n+\t\t\t\t\t\tBITMAP_LOOKUP_TABLE_TRIPLET_WIDTH);\n+\t\tif (table_size > index_end - index->map - header_size)\n+\t\t\treturn error(_(\"corrupted bitmap index file (too short to fit lookup table)\"));\n+\t\tif (git_env_bool(\"GIT_TEST_READ_COMMIT_TABLE\", 1))\n+\t\t\tindex->table_lookup = (void *)(index_end - table_size);\n+\t\tindex_end -= table_size;\n+\t}\n+\tindex->map_pos += header_size;\n+\treturn 0;\n+}\n+\n static int load_bitmap_header(struct bitmap_index *index)\n {\n \tstruct bitmap_disk_header *header = (void *)index->map;\n \tsize_t header_size = sizeof(*header) - GIT_MAX_RAWSZ + the_hash_algo->rawsz;\n+\tuint32_t flags = ntohs(header->options);\n \n \tif (index->map_size < header_size + the_hash_algo->rawsz)\n \t\treturn error(_(\"corrupted bitmap index (too small)\"));\n@@ -172,46 +225,29 @@ static int load_bitmap_header(struct bitmap_index *index)\n \tif (memcmp(header->magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE)) != 0)\n \t\treturn error(_(\"corrupted bitmap index file (wrong header)\"));\n \n-\tindex->version = ntohs(header->version);\n-\tif (index->version != 1)\n-\t\treturn error(_(\"unsupported version '%d' for bitmap index file\"), index->version);\n-\n-\t/* Parse known bitmap format options */\n-\t{\n-\t\tuint32_t flags = ntohs(header->options);\n-\t\tsize_t cache_size = st_mult(bitmap_num_objects(index), sizeof(uint32_t));\n-\t\tunsigned char *index_end = index->map + index->map_size - the_hash_algo->rawsz;\n-\n-\t\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n-\t\t\tBUG(\"unsupported options for bitmap index file \"\n-\t\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n-\n-\t\tif (flags & BITMAP_OPT_HASH_CACHE) {\n-\t\t\tif (cache_size > index_end - index->map - header_size)\n-\t\t\t\treturn error(_(\"corrupted bitmap index file (too short to fit hash cache)\"));\n-\t\t\tindex->hashes = (void *)(index_end - cache_size);\n-\t\t\tindex_end -= cache_size;\n-\t\t}\n-\n-\t\tif (flags & BITMAP_OPT_LOOKUP_TABLE) {\n-\t\t\tsize_t table_size = st_mult(ntohl(header->entry_count),\n-\t\t\t\t\t\t    BITMAP_LOOKUP_TABLE_TRIPLET_WIDTH);\n-\t\t\tif (table_size > index_end - index->map - header_size)\n-\t\t\t\treturn error(_(\"corrupted bitmap index file (too short to fit lookup table)\"));\n-\t\t\tif (git_env_bool(\"GIT_TEST_READ_COMMIT_TABLE\", 1))\n-\t\t\t\tindex->table_lookup = (void *)(index_end - table_size);\n-\t\t\tindex_end -= table_size;\n-\t\t}\n-\t}\n+\tif ((flags & BITMAP_OPT_FULL_DAG) == 0)\n+\t\tBUG(\"unsupported options for bitmap index file \"\n+\t\t\t\"(Git requires BITMAP_OPT_FULL_DAG)\");\n \n+\tindex->version = ntohs(header->version);\n \tindex->entry_count = ntohl(header->entry_count);\n \tindex->checksum = header->checksum;\n-\tindex->map_pos += header_size;\n-\treturn 0;\n+\tif (index->version == 1) {\n+\t\tset_bitmap_type(EWAH);\n+\t\treturn ewah_load_bitmap_header(index, header, flags);\n+\t}\n+\tif (index->version == 2) {\n+\t\tset_bitmap_type(ROARING);\n+\t\tindex->chunk_nr = *(index->map + header_size);\n+\t\tindex->map_pos += header_size + sizeof(char);\n+\t\treturn 0;\n+\t}\n+\n+\treturn error(_(\"unsupported version '%d' for bitmap index file\"), index->version);\n }\n \n static struct stored_bitmap *store_bitmap(struct bitmap_index *index,\n-\t\t\t\t\t  struct ewah_bitmap *root,\n+\t\t\t\t\t  void *root,\n \t\t\t\t\t  const struct object_id *oid,\n \t\t\t\t\t  struct stored_bitmap *xor_with,\n \t\t\t\t\t  int flags)\n@@ -484,6 +520,105 @@ static int load_reverse_index(struct bitmap_index *bitmap_git)\n \treturn load_pack_revindex(bitmap_git->pack);\n }\n \n+static int load_ewah_bitmap(struct bitmap_index *bitmap_git)\n+{\n+\tif (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||\n+\t\t!(bitmap_git->trees = read_bitmap_1(bitmap_git)) ||\n+\t\t!(bitmap_git->blobs = read_bitmap_1(bitmap_git)) ||\n+\t\t!(bitmap_git->tags = read_bitmap_1(bitmap_git)))\n+\t\treturn -1;\n+\n+\tif (!bitmap_git->table_lookup && load_bitmap_entries_v1(bitmap_git) < 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+\n+static int load_roaring_type_index(const unsigned char *chunk_start,\n+\t\t\t\t   size_t chunk_size, void *data)\n+{\n+\tstruct bitmap_index *index = data;\n+\tsize_t initial_map_pos = chunk_start - index->map;\n+\tindex->map_pos = initial_map_pos;\n+\n+\tif (!(index->commits = read_roaring_bitmap(index, chunk_size)) ||\n+\t\t!(index->trees = read_roaring_bitmap(index, chunk_size - (index->map_pos - initial_map_pos))) ||\n+\t\t!(index->blobs = read_roaring_bitmap(index, chunk_size - (index->map_pos - initial_map_pos))) ||\n+\t\t!(index->tags = read_roaring_bitmap(index, chunk_size - (index->map_pos - initial_map_pos))))\n+\t\treturn 1;\n+\treturn 0;\n+}\n+\n+static int load_bitmap_entries_v2(const unsigned char *chunk_start,\n+\t\t\t\t     size_t chunk_size, void *data)\n+{\n+\tstruct stored_bitmap *recent_bitmaps[MAX_XOR_OFFSET] = { NULL };\n+\tstruct bitmap_index *index = data;\n+\tsize_t max_limit = chunk_size;\n+\tsize_t initial_map_pos = chunk_start - index->map;\n+\tuint32_t i;\n+\tindex->map_pos = initial_map_pos;\n+\n+\tfor (i = 0; i < index->entry_count; i++) {\n+\t\tint xor_offset, flags;\n+\t\troaring_bitmap_t *bitmap = NULL;\n+\t\tstruct stored_bitmap *xor_bitmap = NULL;\n+\t\tuint32_t commit_idx_pos;\n+\t\tstruct object_id oid;\n+\n+\t\tmax_limit = chunk_size - (index->map_pos - initial_map_pos);\n+\n+\t\tif (max_limit < 6)\n+\t\t\treturn error(_(\"corrupt roaring bitmap: truncated header for entry %d\"), i);\n+\n+\t\tcommit_idx_pos = read_be32(index->map, &index->map_pos);\n+\t\txor_offset = read_u8(index->map, &index->map_pos);\n+\t\tflags = read_u8(index->map, &index->map_pos);\n+\n+\t\tif (nth_bitmap_object_oid(index, &oid, commit_idx_pos) < 0)\n+\t\t\treturn error(_(\"corrupt roaring bitmap: commit index %u out of range\"),\n+\t\t\t\t     (unsigned)commit_idx_pos);\n+\n+\t\tbitmap = read_roaring_bitmap(index, max_limit);\n+\t\tif (!bitmap)\n+\t\t\treturn -1;\n+\n+\t\tif (xor_offset > MAX_XOR_OFFSET || xor_offset > i)\n+\t\t\treturn error(_(\"corrupted bitmap pack index\"));\n+\n+\t\tif (xor_offset > 0) {\n+\t\t\txor_bitmap = recent_bitmaps[(i - xor_offset) % MAX_XOR_OFFSET];\n+\n+\t\t\tif (!xor_bitmap)\n+\t\t\t\treturn error(_(\"invalid XOR offset in bitmap pack index\"));\n+\t\t}\n+\n+\t\trecent_bitmaps[i % MAX_XOR_OFFSET] = store_bitmap(\n+\t\t\tindex, bitmap, &oid, xor_bitmap, flags);\n+\t}\n+\treturn 0;\n+}\n+\n+static int load_roaring_bitmap(struct bitmap_index *bitmap_git)\n+{\n+\tstruct chunkfile *cf = init_chunkfile(NULL);\n+\ttrace2_region_enter(\"pack-bitmap\", \"load-roaring-bitmap\", the_repository);\n+\tif (read_table_of_contents(cf, bitmap_git->map, bitmap_git->map_size,\n+\t\t\t\t   bitmap_git->map_pos,\n+\t\t\t\t   bitmap_git->chunk_nr))\n+\t\treturn -1;\n+\n+\tif (read_chunk(cf, BITMAP_TYPE_INDEXES, load_roaring_type_index, bitmap_git) == CHUNK_NOT_FOUND)\n+\t\tdie(_(\"bitmap file missing required type index chunk\"));\n+\tpair_chunk(cf, BITMAP_LOOKUP_TABLE, (const unsigned char **)&bitmap_git->table_lookup);\n+\tif (!bitmap_git->table_lookup && read_chunk(cf, BITMAP_REACHABILITY_BITMAPS,\n+\t\t load_bitmap_entries_v2, bitmap_git) == CHUNK_NOT_FOUND)\n+\t\tdie(_(\"bitmap file missing required reachability chunk\"));\n+\tpair_chunk(cf, BITMAP_HASH_CACHE, (const unsigned char **)&bitmap_git->hashes);\n+\ttrace2_region_leave(\"pack-bitmap\", \"load-roaring-bitmap\", the_repository);\n+\treturn 0;\n+}\n+\n static int load_bitmap(struct bitmap_index *bitmap_git)\n {\n \tassert(bitmap_git->map);\n@@ -494,13 +629,9 @@ static int load_bitmap(struct bitmap_index *bitmap_git)\n \tif (load_reverse_index(bitmap_git))\n \t\tgoto failed;\n \n-\tif (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||\n-\t\t!(bitmap_git->trees = read_bitmap_1(bitmap_git)) ||\n-\t\t!(bitmap_git->blobs = read_bitmap_1(bitmap_git)) ||\n-\t\t!(bitmap_git->tags = read_bitmap_1(bitmap_git)))\n+\tif (bitmap_git->version == 1 && load_ewah_bitmap(bitmap_git) < 0)\n \t\tgoto failed;\n-\n-\tif (!bitmap_git->table_lookup && load_bitmap_entries_v1(bitmap_git) < 0)\n+\telse if (bitmap_git->version == 2 && load_roaring_bitmap(bitmap_git) < 0)\n \t\tgoto failed;\n \n \treturn 0;\n@@ -563,6 +694,12 @@ static int open_bitmap(struct repository *r,\n struct bitmap_index *prepare_bitmap_git(struct repository *r)\n {\n \tstruct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));\n+\tbitmap_git->haves = NULL;\n+\tbitmap_git->result = NULL;\n+\tbitmap_git->commits = NULL;\n+\tbitmap_git->trees = NULL;\n+\tbitmap_git->blobs = NULL;\n+\tbitmap_git->tags = NULL;\n \n \tif (!open_bitmap(r, bitmap_git) && !load_bitmap(bitmap_git))\n \t\treturn bitmap_git;\n@@ -584,8 +721,8 @@ struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx)\n \n struct include_data {\n \tstruct bitmap_index *bitmap_git;\n-\tstruct bitmap *base;\n-\tstruct bitmap *seen;\n+\tvoid *base;\n+\tvoid *seen;\n };\n \n struct bitmap_lookup_table_triplet {\n@@ -696,12 +833,13 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_\n \tint flags;\n \tstruct bitmap_lookup_table_triplet triplet;\n \tstruct object_id *oid = &commit->object.oid;\n-\tstruct ewah_bitmap *bitmap;\n+\tvoid *bitmap;\n \tstruct stored_bitmap *xor_bitmap = NULL;\n \tconst int bitmap_header_size = 6;\n \tstatic struct bitmap_lookup_table_xor_item *xor_items = NULL;\n \tstatic size_t xor_items_nr = 0, xor_items_alloc = 0;\n \tstatic int is_corrupt = 0;\n+\tsize_t max_limit = 0;\n \tint xor_flags;\n \tkhiter_t hash_pos;\n \tstruct bitmap_lookup_table_xor_item *xor_item;\n@@ -766,7 +904,15 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_\n \n \t\tbitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t);\n \t\txor_flags = read_u8(bitmap_git->map, &bitmap_git->map_pos);\n-\t\tbitmap = read_bitmap_1(bitmap_git);\n+\t\tif (bitmap_git->version == 1)\n+\t\t\tbitmap = read_bitmap_1(bitmap_git);\n+\t\telse if (bitmap_git->version == 2) {\n+\t\t\tif (bitmap_git->hashes)\n+\t\t\t\tmax_limit = ((unsigned char *)bitmap_git->hashes - bitmap_git->map) - bitmap_git->map_pos;\n+\t\t\telse\n+\t\t\t\tmax_limit = (bitmap_git->table_lookup - bitmap_git->map) - bitmap_git->map_pos;\n+\t\t\tbitmap = read_roaring_bitmap(bitmap_git, max_limit);\n+\t\t}\n \n \t\tif (!bitmap)\n \t\t\tgoto corrupt;\n@@ -807,7 +953,15 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_\n \t */\n \tbitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t);\n \tflags = read_u8(bitmap_git->map, &bitmap_git->map_pos);\n-\tbitmap = read_bitmap_1(bitmap_git);\n+\n+\tif (bitmap_git->hashes)\n+\t\tmax_limit = ((unsigned char *)bitmap_git->hashes - bitmap_git->map) - bitmap_git->map_pos;\n+\telse\n+\t\tmax_limit = (bitmap_git->table_lookup - bitmap_git->map) - bitmap_git->map_pos;\n+\tif (bitmap_git->version == 1)\n+\t\tbitmap = read_bitmap_1(bitmap_git);\n+\telse if (bitmap_git->version == 2)\n+\t\tbitmap = read_roaring_bitmap(bitmap_git, max_limit);\n \n \tif (!bitmap)\n \t\tgoto corrupt;\n@@ -820,8 +974,8 @@ corrupt:\n \treturn NULL;\n }\n \n-struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,\n-\t\t\t\t      struct commit *commit)\n+void *bitmap_for_commit(struct bitmap_index *bitmap_git,\n+\t\t\tstruct commit *commit)\n {\n \tkhiter_t hash_pos = kh_get_oid_map(bitmap_git->bitmaps,\n \t\t\t\t\t   commit->object.oid);\n@@ -836,9 +990,9 @@ struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,\n \t\ttrace2_region_leave(\"pack-bitmap\", \"reading_lookup_table\", the_repository);\n \t\tif (!bitmap)\n \t\t\treturn NULL;\n-\t\treturn lookup_stored_bitmap(bitmap);\n+\t\treturn lookup_stored_bitmap(bitmap_git, bitmap);\n \t}\n-\treturn lookup_stored_bitmap(kh_value(bitmap_git->bitmaps, hash_pos));\n+\treturn lookup_stored_bitmap(bitmap_git, kh_value(bitmap_git->bitmaps, hash_pos));\n }\n \n static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,\n@@ -922,7 +1076,7 @@ static int ext_index_add_object(struct bitmap_index *bitmap_git,\n \n struct bitmap_show_data {\n \tstruct bitmap_index *bitmap_git;\n-\tstruct bitmap *base;\n+\tvoid *base;\n };\n \n static void show_object(struct object *object, const char *name, void *data_)\n@@ -936,7 +1090,7 @@ static void show_object(struct object *object, const char *name, void *data_)\n \t\tbitmap_pos = ext_index_add_object(data->bitmap_git, object,\n \t\t\t\t\t\t  name);\n \n-\traw_bitmap_set(data->base, bitmap_pos);\n+\troaring_or_raw_bitmap_set(data->base, bitmap_pos);\n }\n \n static void show_commit(struct commit *commit, void *data)\n@@ -948,21 +1102,24 @@ static int add_to_include_set(struct bitmap_index *bitmap_git,\n \t\t\t      struct commit *commit,\n \t\t\t      int bitmap_pos)\n {\n-\tstruct ewah_bitmap *partial;\n+\tvoid *partial;\n \n-\tif (data->seen && raw_bitmap_get(data->seen, bitmap_pos))\n+\tif (data->seen && roaring_or_raw_bitmap_get(data->seen, bitmap_pos))\n \t\treturn 0;\n \n-\tif (raw_bitmap_get(data->base, bitmap_pos))\n+\tif (roaring_or_raw_bitmap_get(data->base, bitmap_pos))\n \t\treturn 0;\n \n \tpartial = bitmap_for_commit(bitmap_git, commit);\n \tif (partial) {\n-\t\traw_bitmap_or_ewah(data->base, partial);\n+\t\tif (bitmap_git->version == 1)\n+\t\t\traw_bitmap_or_ewah(data->base, partial);\n+\t\telse if (bitmap_git->version == 2)\n+\t\t\troaring_bitmap_or(data->base, partial);\n \t\treturn 0;\n \t}\n \n-\traw_bitmap_set(data->base, bitmap_pos);\n+\troaring_or_raw_bitmap_set(data->base, bitmap_pos);\n \treturn 1;\n }\n \n@@ -999,8 +1156,8 @@ static int should_include_obj(struct object *obj, void *_data)\n \tbitmap_pos = bitmap_position(data->bitmap_git, &obj->oid);\n \tif (bitmap_pos < 0)\n \t\treturn 1;\n-\tif ((data->seen && raw_bitmap_get(data->seen, bitmap_pos)) ||\n-\t     raw_bitmap_get(data->base, bitmap_pos)) {\n+\tif ((data->seen && roaring_or_raw_bitmap_get(data->seen, bitmap_pos)) ||\n+\t     roaring_or_raw_bitmap_get(data->base, bitmap_pos)) {\n \t\tobj->flags |= SEEN;\n \t\treturn 0;\n \t}\n@@ -1008,28 +1165,36 @@ static int should_include_obj(struct object *obj, void *_data)\n }\n \n static int add_commit_to_bitmap(struct bitmap_index *bitmap_git,\n-\t\t\t\tstruct bitmap **base,\n+\t\t\t\tvoid **base,\n \t\t\t\tstruct commit *commit)\n {\n-\tstruct ewah_bitmap *or_with = bitmap_for_commit(bitmap_git, commit);\n+\tvoid *or_with = bitmap_for_commit(bitmap_git, commit);\n \n \tif (!or_with)\n \t\treturn 0;\n \n-\tif (!*base)\n-\t\t*base = ewah_to_raw_bitmap(or_with);\n-\telse\n-\t\traw_bitmap_or_ewah(*base, or_with);\n+\tif (!*base) {\n+\t\tif (bitmap_git->version == 1)\n+\t\t\t*base = ewah_to_raw_bitmap(or_with);\n+\t\telse if (bitmap_git->version == 2)\n+\t\t\t*base = roaring_bitmap_copy(or_with);\n+\t}\n+\telse {\n+\t\tif (bitmap_git->version == 1)\n+\t\t\traw_bitmap_or_ewah(*base, or_with);\n+\t\telse if (bitmap_git->version == 2)\n+\t\t\troaring_bitmap_or(*base, or_with);\n+\t}\n \n \treturn 1;\n }\n \n-static struct bitmap *find_objects(struct bitmap_index *bitmap_git,\n+static void *find_objects(struct bitmap_index *bitmap_git,\n \t\t\t\t   struct rev_info *revs,\n \t\t\t\t   struct object_list *roots,\n-\t\t\t\t   struct bitmap *seen)\n+\t\t\t\t   void *seen)\n {\n-\tstruct bitmap *base = NULL;\n+\tvoid *base = NULL;\n \tint needs_walk = 0;\n \n \tstruct object_list *not_mapped = NULL;\n@@ -1080,7 +1245,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,\n \t\troots = roots->next;\n \t\tpos = bitmap_position(bitmap_git, &object->oid);\n \n-\t\tif (pos < 0 || base == NULL || !raw_bitmap_get(base, pos)) {\n+\t\tif (pos < 0 || base == NULL || !roaring_or_raw_bitmap_get(base, pos)) {\n \t\t\tobject->flags &= ~UNINTERESTING;\n \t\t\tadd_pending_object(revs, object, \"\");\n \t\t\tneeds_walk = 1;\n@@ -1094,7 +1259,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,\n \t\tstruct bitmap_show_data show_data;\n \n \t\tif (!base)\n-\t\t\tbase = raw_bitmap_new();\n+\t\t\tbase = roaring_or_raw_bitmap_new();\n \n \t\tincdata.bitmap_git = bitmap_git;\n \t\tincdata.base = base;\n@@ -1126,14 +1291,14 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,\n \t\t\t\t  struct rev_info *revs,\n \t\t\t\t  show_reachable_fn show_reach)\n {\n-\tstruct bitmap *objects = bitmap_git->result;\n+\tvoid *objects = bitmap_git->result;\n \tstruct eindex *eindex = &bitmap_git->ext_index;\n \tuint32_t i;\n \n \tfor (i = 0; i < eindex->count; ++i) {\n \t\tstruct object *obj;\n \n-\t\tif (!raw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n+\t\tif (!roaring_or_raw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcontinue;\n \n \t\tobj = eindex->objects[i];\n@@ -1233,6 +1398,77 @@ static void show_objects_for_type(\n \t}\n }\n \n+static void *get_roaring_type_index(struct bitmap_index *bitmap_git,\n+\t\t\t\t   enum object_type object_type)\n+{\n+\tswitch (object_type) {\n+\tcase OBJ_COMMIT:\n+\t\treturn bitmap_git->commits;\n+\n+\tcase OBJ_TREE:\n+\t\treturn bitmap_git->trees;\n+\n+\tcase OBJ_BLOB:\n+\t\treturn bitmap_git->blobs;\n+\n+\tcase OBJ_TAG:\n+\t\treturn bitmap_git->tags;\n+\tdefault:\n+\t\tBUG(\"object type %d not stored by bitmap type index\", object_type);\n+\t\tbreak;\n+\t}\n+\n+}\n+\n+static void show_roaring_objects_for_type(struct bitmap_index *bitmap_git,\n+\t\t\t\t\t  enum object_type object_type,\n+\t\t\t\t\t  show_reachable_fn show_reach)\n+{\n+\tuint32_t i;\n+\troaring_bitmap_t *type_index = NULL;\n+\troaring_bitmap_t *objects = bitmap_git->result;\n+\troaring_bitmap_t *fl_objects = NULL;\n+\tuint32_t *filter_objects = NULL;\n+\tuint32_t cardinality = 0;\n+\n+\ttype_index = get_roaring_type_index(bitmap_git, object_type);\n+\n+\tfl_objects = roaring_bitmap_and(objects, type_index);\n+\tcardinality = roaring_bitmap_get_cardinality(fl_objects);\n+\troaring_bitmap_to_uint32_array(fl_objects, filter_objects);\n+\troaring_bitmap_free(fl_objects);\n+\n+\tfor (i = 0; i < cardinality; i++) {\n+\t\tstruct packed_git *pack;\n+\t\tstruct object_id oid;\n+\t\tuint32_t hash = 0, index_pos;\n+\t\toff_t ofs;\n+\n+\t\tif (bitmap_is_midx(bitmap_git)) {\n+\t\t\tstruct multi_pack_index *m = bitmap_git->midx;\n+\t\t\tuint32_t pack_id;\n+\n+\t\t\tindex_pos = pack_pos_to_midx(m, filter_objects[i]);\n+\t\t\tofs = nth_midxed_offset(m, index_pos);\n+\t\t\tnth_midxed_object_oid(&oid, m, index_pos);\n+\n+\t\t\tpack_id = nth_midxed_pack_int_id(m, index_pos);\n+\t\t\tpack = bitmap_git->midx->packs[pack_id];\n+\t\t} else {\n+\t\t\tindex_pos = pack_pos_to_index(bitmap_git->pack, filter_objects[i]);\n+\t\t\tofs = pack_pos_to_offset(bitmap_git->pack, filter_objects[i]);\n+\t\t\tnth_bitmap_object_oid(bitmap_git, &oid, index_pos);\n+\n+\t\t\tpack = bitmap_git->pack;\n+\t\t}\n+\n+\t\tif (bitmap_git->hashes)\n+\t\t\thash = get_be32(bitmap_git->hashes + index_pos);\n+\n+\t\tshow_reach(&oid, object_type, 0, hash, pack, ofs);\n+\t}\n+}\n+\n static int in_bitmapped_pack(struct bitmap_index *bitmap_git,\n \t\t\t     struct object_list *roots)\n {\n@@ -1252,11 +1488,11 @@ static int in_bitmapped_pack(struct bitmap_index *bitmap_git,\n \treturn 0;\n }\n \n-static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,\n+static void *find_tip_objects(struct bitmap_index *bitmap_git,\n \t\t\t\t       struct object_list *tip_objects,\n \t\t\t\t       enum object_type type)\n {\n-\tstruct bitmap *result = raw_bitmap_new();\n+\tvoid *result = roaring_or_raw_bitmap_new();\n \tstruct object_list *p;\n \n \tfor (p = tip_objects; p; p = p->next) {\n@@ -1269,7 +1505,7 @@ static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,\n \t\tif (pos < 0)\n \t\t\tcontinue;\n \n-\t\traw_bitmap_set(result, pos);\n+\t\troaring_or_raw_bitmap_set(result, pos);\n \t}\n \n \treturn result;\n@@ -1277,13 +1513,11 @@ static struct bitmap *find_tip_objects(struct bitmap_index *bitmap_git,\n \n static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,\n \t\t\t\t       struct object_list *tip_objects,\n-\t\t\t\t       struct bitmap *to_filter,\n+\t\t\t\t       void *to_filter,\n \t\t\t\t       enum object_type type)\n {\n \tstruct eindex *eindex = &bitmap_git->ext_index;\n-\tstruct bitmap *tips;\n-\tstruct ewah_iterator it;\n-\teword_t mask;\n+\tvoid *tips;\n \tuint32_t i;\n \n \t/*\n@@ -1293,17 +1527,33 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,\n \t */\n \ttips = find_tip_objects(bitmap_git, tip_objects, type);\n \n-\t/*\n-\t * We can use the type-level bitmap for 'type' to work in whole\n-\t * words for the objects that are actually in the bitmapped\n-\t * packfile.\n-\t */\n-\tfor (i = 0, init_type_iterator(&it, bitmap_git, type);\n-\t     i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);\n-\t     i++) {\n-\t\tif (i < tips->word_alloc)\n-\t\t\tmask &= ~tips->words[i];\n-\t\tto_filter->words[i] &= ~mask;\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct bitmap *raw_tips = tips;\n+\t\tstruct bitmap *to_filter_raw = to_filter;\n+\t\tstruct ewah_iterator it;\n+\t\teword_t mask;\n+\n+\t\t/*\n+\t\t* We can use the type-level bitmap for 'type' to work in whole\n+\t\t* words for the objects that are actually in the bitmapped\n+\t\t* packfile.\n+\t\t*/\n+\t\tfor (i = 0, init_type_iterator(&it, bitmap_git, type);\n+\t\ti < to_filter_raw->word_alloc && ewah_iterator_next(&mask, &it);\n+\t\ti++) {\n+\t\t\tif (i < raw_tips->word_alloc)\n+\t\t\t\tmask &= ~raw_tips->words[i];\n+\t\t\tto_filter_raw->words[i] &= ~mask;\n+\t\t}\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\troaring_bitmap_t *type_index = NULL;\n+\t\troaring_bitmap_t *not_tip_type = NULL;\n+\n+\t\ttype_index = get_roaring_type_index(bitmap_git, type);\n+\t\tnot_tip_type = roaring_bitmap_andnot(tips, type_index);\n+\t\troaring_bitmap_andnot_inplace(to_filter, not_tip_type);\n+\t\troaring_bitmap_free(not_tip_type);\n \t}\n \n \t/*\n@@ -1314,17 +1564,17 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,\n \tfor (i = 0; i < eindex->count; i++) {\n \t\tuint32_t pos = i + bitmap_num_objects(bitmap_git);\n \t\tif (eindex->objects[i]->type == type &&\n-\t\t    raw_bitmap_get(to_filter, pos) &&\n-\t\t    !raw_bitmap_get(tips, pos))\n-\t\t\traw_bitmap_unset(to_filter, pos);\n+\t\t    roaring_or_raw_bitmap_get(to_filter, pos) &&\n+\t\t    !roaring_or_raw_bitmap_get(tips, pos))\n+\t\t\troaring_or_raw_bitmap_unset(to_filter, pos);\n \t}\n \n-\traw_bitmap_free(tips);\n+\troaring_or_raw_bitmap_free(tips);\n }\n \n static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,\n \t\t\t\t    struct object_list *tip_objects,\n-\t\t\t\t    struct bitmap *to_filter)\n+\t\t\t\t    void *to_filter)\n {\n \tfilter_bitmap_exclude_type(bitmap_git, tip_objects, to_filter,\n \t\t\t\t   OBJ_BLOB);\n@@ -1371,47 +1621,64 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,\n \n static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,\n \t\t\t\t     struct object_list *tip_objects,\n-\t\t\t\t     struct bitmap *to_filter,\n+\t\t\t\t     void *to_filter,\n \t\t\t\t     unsigned long limit)\n {\n-\tstruct eindex *eindex = &bitmap_git->ext_index;\n-\tstruct bitmap *tips;\n-\tstruct ewah_iterator it;\n-\teword_t mask;\n \tuint32_t i;\n+\tstruct eindex *eindex = &bitmap_git->ext_index;\n+\tvoid *tips;\n \n \ttips = find_tip_objects(bitmap_git, tip_objects, OBJ_BLOB);\n \n-\tfor (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);\n-\t     i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);\n-\t     i++) {\n-\t\teword_t word = to_filter->words[i] & mask;\n-\t\tunsigned offset;\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct bitmap *to_filter_raw = to_filter;\n+\t\tstruct ewah_iterator it;\n+\t\teword_t mask;\n \n-\t\tfor (offset = 0; offset < BITS_IN_EWORD; offset++) {\n-\t\t\tuint32_t pos;\n+\t\tfor (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);\n+\t\ti < to_filter_raw->word_alloc && ewah_iterator_next(&mask, &it);\n+\t\ti++) {\n+\t\t\teword_t word = to_filter_raw->words[i] & mask;\n+\t\t\tunsigned offset;\n \n-\t\t\tif ((word >> offset) == 0)\n-\t\t\t\tbreak;\n-\t\t\toffset += ewah_bit_ctz64(word >> offset);\n-\t\t\tpos = i * BITS_IN_EWORD + offset;\n+\t\t\tfor (offset = 0; offset < BITS_IN_EWORD; offset++) {\n+\t\t\t\tuint32_t pos;\n \n-\t\t\tif (!raw_bitmap_get(tips, pos) &&\n-\t\t\t    get_size_by_pos(bitmap_git, pos) >= limit)\n-\t\t\t\traw_bitmap_unset(to_filter, pos);\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\t\tpos = i * BITS_IN_EWORD + offset;\n+\n+\t\t\t\tif (!raw_bitmap_get(tips, pos) &&\n+\t\t\t\tget_size_by_pos(bitmap_git, pos) >= limit)\n+\t\t\t\t\traw_bitmap_unset(to_filter, pos);\n+\t\t\t}\n+\t\t}\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\troaring_bitmap_t *filter_bitmap = roaring_bitmap_and(to_filter, tips);\n+\t\tuint32_t *filter_arr = NULL;\n+\t\tuint32_t cardinality = roaring_bitmap_get_cardinality(filter_bitmap);\n+\t\troaring_bitmap_to_uint32_array(filter_bitmap, filter_arr);\n+\t\troaring_bitmap_free(filter_bitmap);\n+\n+\t\tfor (i = 0; i < cardinality; i++) {\n+\t\t\tif (!roaring_bitmap_contains(to_filter, filter_arr[i]) &&\n+\t\t\tget_size_by_pos(bitmap_git, filter_arr[i]) >= limit)\n+\t\t\t\troaring_bitmap_remove(to_filter, filter_arr[i]);\n \t\t}\n \t}\n \n \tfor (i = 0; i < eindex->count; i++) {\n \t\tuint32_t pos = i + bitmap_num_objects(bitmap_git);\n \t\tif (eindex->objects[i]->type == OBJ_BLOB &&\n-\t\t    raw_bitmap_get(to_filter, pos) &&\n-\t\t    !raw_bitmap_get(tips, pos) &&\n+\t\t    roaring_or_raw_bitmap_get(to_filter, pos) &&\n+\t\t    !roaring_or_raw_bitmap_get(tips, pos) &&\n \t\t    get_size_by_pos(bitmap_git, pos) >= limit)\n-\t\t\traw_bitmap_unset(to_filter, pos);\n+\t\t\troaring_or_raw_bitmap_unset(to_filter, pos);\n \t}\n \n-\traw_bitmap_free(tips);\n+\troaring_or_raw_bitmap_free(tips);\n }\n \n static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git,\n@@ -1430,7 +1697,7 @@ static void filter_bitmap_tree_depth(struct bitmap_index *bitmap_git,\n \n static void filter_bitmap_object_type(struct bitmap_index *bitmap_git,\n \t\t\t\t      struct object_list *tip_objects,\n-\t\t\t\t      struct bitmap *to_filter,\n+\t\t\t\t      void *to_filter,\n \t\t\t\t      enum object_type object_type)\n {\n \tif (object_type < OBJ_COMMIT || object_type > OBJ_TAG)\n@@ -1448,7 +1715,7 @@ static void filter_bitmap_object_type(struct bitmap_index *bitmap_git,\n \n static int filter_bitmap(struct bitmap_index *bitmap_git,\n \t\t\t struct object_list *tip_objects,\n-\t\t\t struct bitmap *to_filter,\n+\t\t\t void *to_filter,\n \t\t\t struct list_objects_filter_options *filter)\n {\n \tif (!filter || filter->choice == LOFC_DISABLED)\n@@ -1513,8 +1780,8 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,\n \tstruct object_list *wants = NULL;\n \tstruct object_list *haves = NULL;\n \n-\tstruct bitmap *wants_bitmap = NULL;\n-\tstruct bitmap *haves_bitmap = NULL;\n+\tvoid *wants_bitmap = NULL;\n+\tvoid *haves_bitmap = NULL;\n \n \tstruct bitmap_index *bitmap_git;\n \n@@ -1597,7 +1864,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,\n \t\tBUG(\"failed to perform bitmap walk\");\n \n \tif (haves_bitmap)\n-\t\traw_bitmap_and_not(wants_bitmap, haves_bitmap);\n+\t\troaring_or_raw_bitmap_and_not(wants_bitmap, haves_bitmap);\n \n \tfilter_bitmap(bitmap_git,\n \t\t      (revs->filter.choice && filter_provided_objects) ? NULL : wants,\n@@ -1625,7 +1892,7 @@ cleanup:\n  */\n static int try_partial_reuse(struct packed_git *pack,\n \t\t\t     size_t pos,\n-\t\t\t     struct bitmap *reuse,\n+\t\t\t     void *reuse,\n \t\t\t     struct pack_window **w_curs)\n {\n \toff_t offset, delta_obj_offset;\n@@ -1702,14 +1969,14 @@ static int try_partial_reuse(struct packed_git *pack,\n \t\t * to REF_DELTA on the fly. Better to just let the normal\n \t\t * object_entry code path handle it.\n \t\t */\n-\t\tif (!raw_bitmap_get(reuse, base_pos))\n+\t\tif (!roaring_or_raw_bitmap_get(reuse, base_pos))\n \t\t\treturn 0;\n \t}\n \n \t/*\n \t * If we got here, then the object is OK to reuse. Mark it.\n \t */\n-\traw_bitmap_set(reuse, pos);\n+\troaring_or_raw_bitmap_set(reuse, pos);\n \treturn 0;\n }\n \n@@ -1724,11 +1991,11 @@ uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git)\n int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,\n \t\t\t\t       struct packed_git **packfile_out,\n \t\t\t\t       uint32_t *entries,\n-\t\t\t\t       struct bitmap **reuse_out)\n+\t\t\t\t       void **reuse_out)\n {\n \tstruct packed_git *pack;\n-\tstruct bitmap *result = bitmap_git->result;\n-\tstruct bitmap *reuse;\n+\tvoid *result = bitmap_git->result;\n+\tvoid *reuse;\n \tstruct pack_window *w_curs = NULL;\n \tsize_t i = 0;\n \tuint32_t offset;\n@@ -1744,54 +2011,71 @@ int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,\n \t\tpack = bitmap_git->pack;\n \tobjects_nr = pack->num_objects;\n \n-\twhile (i < result->word_alloc && result->words[i] == (eword_t)~0)\n-\t\ti++;\n-\n-\t/*\n-\t * Don't mark objects not in the packfile or preferred pack. This bitmap\n-\t * marks objects eligible for reuse, but the pack-reuse code only\n-\t * understands how to reuse a single pack. Since the preferred pack is\n-\t * guaranteed to have all bases for its deltas (in a multi-pack bitmap),\n-\t * we use it instead of another pack. In single-pack bitmaps, the choice\n-\t * is made for us.\n-\t */\n-\tif (i > objects_nr / BITS_IN_EWORD)\n-\t\ti = objects_nr / BITS_IN_EWORD;\n-\n-\treuse = raw_bitmap_word_alloc(i);\n-\tmemset(reuse->words, 0xFF, i * sizeof(eword_t));\n-\n-\tfor (; i < result->word_alloc; ++i) {\n-\t\teword_t word = result->words[i];\n-\t\tsize_t pos = (i * BITS_IN_EWORD);\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct bitmap *result_raw = result;\n+\t\tstruct bitmap *reuse_raw = NULL;\n \n-\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n-\t\t\tif ((word >> offset) == 0)\n-\t\t\t\tbreak;\n+\t\twhile (i < result_raw->word_alloc && result_raw->words[i] == (eword_t)~0)\n+\t\t\ti++;\n \n-\t\t\toffset += ewah_bit_ctz64(word >> offset);\n-\t\t\tif (try_partial_reuse(pack, pos + offset,\n-\t\t\t\t\t      reuse, &w_curs) < 0) {\n-\t\t\t\t/*\n-\t\t\t\t * try_partial_reuse indicated we couldn't reuse\n-\t\t\t\t * any bits, so there is no point in trying more\n-\t\t\t\t * bits in the current word, or any other words\n-\t\t\t\t * in result.\n-\t\t\t\t *\n-\t\t\t\t * Jump out of both loops to avoid future\n-\t\t\t\t * unnecessary calls to try_partial_reuse.\n-\t\t\t\t */\n-\t\t\t\tgoto done;\n+\t\t/*\n+\t\t* Don't mark objects not in the packfile or preferred pack. This bitmap\n+\t\t* marks objects eligible for reuse, but the pack-reuse code only\n+\t\t* understands how to reuse a single pack. Since the preferred pack is\n+\t\t* guaranteed to have all bases for its deltas (in a multi-pack bitmap),\n+\t\t* we use it instead of another pack. In single-pack bitmaps, the choice\n+\t\t* is made for us.\n+\t\t*/\n+\t\tif (i > objects_nr / BITS_IN_EWORD)\n+\t\t\ti = objects_nr / BITS_IN_EWORD;\n+\n+\t\treuse_raw = raw_bitmap_word_alloc(i);\n+\t\tmemset(reuse_raw->words, 0xFF, i * sizeof(eword_t));\n+\n+\t\tfor (; i < result_raw->word_alloc; ++i) {\n+\t\t\teword_t word = result_raw->words[i];\n+\t\t\tsize_t pos = (i * BITS_IN_EWORD);\n+\n+\t\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\t\tif (try_partial_reuse(pack, pos + offset,\n+\t\t\t\t\t\treuse_raw, &w_curs) < 0) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t* try_partial_reuse indicated we couldn't reuse\n+\t\t\t\t\t* any bits, so there is no point in trying more\n+\t\t\t\t\t* bits in the current word, or any other words\n+\t\t\t\t\t* in result.\n+\t\t\t\t\t*\n+\t\t\t\t\t* Jump out of both loops to avoid future\n+\t\t\t\t\t* unnecessary calls to try_partial_reuse.\n+\t\t\t\t\t*/\n+\t\t\t\t\treuse = reuse_raw;\n+\t\t\t\t\tgoto done;\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n+\t\treuse = reuse_raw;\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\tuint32_t cardinality = roaring_bitmap_get_cardinality(result);\n+\t\tfor (i = 0;  i < objects_nr && roaring_bitmap_contains(result, i); i++);\n+\t\treuse = roaring_bitmap_create_with_capacity(i);\n+\t\troaring_bitmap_add_range(reuse, 0, i+1);\n+\t\tfor (; i < cardinality; i++) {\n+\t\t\tif (try_partial_reuse(pack, i, reuse, &w_curs) < 0)\n+\t\t\t\tgoto done;\n+\t\t}\n \t}\n \n done:\n \tunuse_pack(&w_curs);\n \n-\t*entries = raw_bitmap_popcount(reuse);\n-\tif (!*entries) {\n-\t\traw_bitmap_free(reuse);\n+\t*entries = roaring_or_raw_bitmap_cardinality(reuse);\n+\tif (!*entries && reuse) {\n+\t\troaring_or_raw_bitmap_free(reuse);\n \t\treturn -1;\n \t}\n \n@@ -1799,14 +2083,14 @@ done:\n \t * Drop any reused objects from the result, since they will not\n \t * need to be handled separately.\n \t */\n-\traw_bitmap_and_not(result, reuse);\n+\troaring_or_raw_bitmap_and_not(result, reuse);\n \t*packfile_out = pack;\n \t*reuse_out = reuse;\n \treturn 0;\n }\n \n int bitmap_walk_contains(struct bitmap_index *bitmap_git,\n-\t\t\t struct bitmap *bitmap, const struct object_id *oid)\n+\t\t\t void *bitmap, const struct object_id *oid)\n {\n \tint idx;\n \n@@ -1814,15 +2098,13 @@ int bitmap_walk_contains(struct bitmap_index *bitmap_git,\n \t\treturn 0;\n \n \tidx = bitmap_position(bitmap_git, oid);\n-\treturn idx >= 0 && raw_bitmap_get(bitmap, idx);\n+\treturn idx >= 0 && roaring_or_raw_bitmap_get(bitmap, idx);\n }\n \n-void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,\n-\t\t\t\t struct rev_info *revs,\n-\t\t\t\t show_reachable_fn show_reachable)\n+static void show_ewah_objects(struct bitmap_index *bitmap_git,\n+\t\t\t     struct rev_info *revs,\n+\t\t\t     show_reachable_fn show_reachable)\n {\n-\tassert(bitmap_git->result);\n-\n \tshow_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable);\n \tif (revs->tree_objects)\n \t\tshow_objects_for_type(bitmap_git, OBJ_TREE, show_reachable);\n@@ -1830,6 +2112,31 @@ void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,\n \t\tshow_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable);\n \tif (revs->tag_objects)\n \t\tshow_objects_for_type(bitmap_git, OBJ_TAG, show_reachable);\n+}\n+\n+static void show_roaring_objects(struct bitmap_index *bitmap_git,\n+\t\t\t\t  struct rev_info *revs,\n+\t\t\t\t  show_reachable_fn show_reachable)\n+{\n+\tshow_roaring_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable);\n+\tif (revs->tree_objects)\n+\t\tshow_roaring_objects_for_type(bitmap_git, OBJ_TREE, show_reachable);\n+\tif (revs->blob_objects)\n+\t\tshow_roaring_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable);\n+\tif (revs->tag_objects)\n+\t\tshow_roaring_objects_for_type(bitmap_git, OBJ_TAG, show_reachable);\n+}\n+\n+void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,\n+\t\t\t\t struct rev_info *revs,\n+\t\t\t\t show_reachable_fn show_reachable)\n+{\n+\tassert(bitmap_git->result);\n+\n+\tif (bitmap_git->version == 1)\n+\t\tshow_ewah_objects(bitmap_git, revs, show_reachable);\n+\telse if (bitmap_git->version == 2)\n+\t\tshow_roaring_objects(bitmap_git, revs, show_reachable);\n \n \tshow_extended_objects(bitmap_git, revs, show_reachable);\n }\n@@ -1837,23 +2144,36 @@ void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,\n static uint32_t count_object_type(struct bitmap_index *bitmap_git,\n \t\t\t\t  enum object_type type)\n {\n-\tstruct bitmap *objects = bitmap_git->result;\n+\tvoid *objects = bitmap_git->result;\n \tstruct eindex *eindex = &bitmap_git->ext_index;\n \n \tuint32_t i = 0, count = 0;\n-\tstruct ewah_iterator it;\n-\teword_t filter;\n \n-\tinit_type_iterator(&it, bitmap_git, type);\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct ewah_iterator it;\n+\t\tstruct bitmap *raw_objects = objects;\n+\t\teword_t filter;\n+\n+\t\tinit_type_iterator(&it, bitmap_git, type);\n \n-\twhile (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n-\t\teword_t word = objects->words[i++] & filter;\n-\t\tcount += ewah_bit_popcount64(word);\n+\t\twhile (i < raw_objects->word_alloc && ewah_iterator_next(&filter, &it)) {\n+\t\t\teword_t word = raw_objects->words[i++] & filter;\n+\t\t\tcount += ewah_bit_popcount64(word);\n+\t\t}\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\troaring_bitmap_t *type_index = NULL;\n+\t\troaring_bitmap_t *filter_objects = NULL;\n+\n+\t\ttype_index = get_roaring_type_index(bitmap_git, type);\n+\t\tfilter_objects = roaring_bitmap_and(objects, type_index);\n+\n+\t\tcount += roaring_bitmap_get_cardinality(filter_objects);\n \t}\n \n \tfor (i = 0; i < eindex->count; ++i) {\n \t\tif (eindex->objects[i]->type == type &&\n-\t\t\traw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n+\t\t\troaring_or_raw_bitmap_get(objects, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcount++;\n \t}\n \n@@ -1881,11 +2201,11 @@ void count_bitmap_commit_list(struct bitmap_index *bitmap_git,\n \n struct bitmap_test_data {\n \tstruct bitmap_index *bitmap_git;\n-\tstruct bitmap *base;\n-\tstruct bitmap *commits;\n-\tstruct bitmap *trees;\n-\tstruct bitmap *blobs;\n-\tstruct bitmap *tags;\n+\tvoid *base;\n+\tvoid *commits;\n+\tvoid *trees;\n+\tvoid *blobs;\n+\tvoid *tags;\n \tstruct progress *prg;\n \tsize_t seen;\n };\n@@ -1896,19 +2216,19 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,\n \tenum object_type bitmap_type = OBJ_NONE;\n \tint bitmaps_nr = 0;\n \n-\tif (raw_bitmap_get(tdata->commits, pos)) {\n+\tif (roaring_or_raw_bitmap_get(tdata->commits, pos)) {\n \t\tbitmap_type = OBJ_COMMIT;\n \t\tbitmaps_nr++;\n \t}\n-\tif (raw_bitmap_get(tdata->trees, pos)) {\n+\tif (roaring_or_raw_bitmap_get(tdata->trees, pos)) {\n \t\tbitmap_type = OBJ_TREE;\n \t\tbitmaps_nr++;\n \t}\n-\tif (raw_bitmap_get(tdata->blobs, pos)) {\n+\tif (roaring_or_raw_bitmap_get(tdata->blobs, pos)) {\n \t\tbitmap_type = OBJ_BLOB;\n \t\tbitmaps_nr++;\n \t}\n-\tif (raw_bitmap_get(tdata->tags, pos)) {\n+\tif (roaring_or_raw_bitmap_get(tdata->tags, pos)) {\n \t\tbitmap_type = OBJ_TAG;\n \t\tbitmaps_nr++;\n \t}\n@@ -1939,7 +2259,7 @@ static void test_show_object(struct object *object, const char *name,\n \t\tdie(_(\"object not in bitmap: '%s'\"), oid_to_hex(&object->oid));\n \ttest_bitmap_type(tdata, object, bitmap_pos);\n \n-\traw_bitmap_set(tdata->base, bitmap_pos);\n+\troaring_or_raw_bitmap_set(tdata->base, bitmap_pos);\n \tdisplay_progress(tdata->prg, ++tdata->seen);\n }\n \n@@ -1954,18 +2274,18 @@ static void test_show_commit(struct commit *commit, void *data)\n \t\tdie(_(\"object not in bitmap: '%s'\"), oid_to_hex(&commit->object.oid));\n \ttest_bitmap_type(tdata, &commit->object, bitmap_pos);\n \n-\traw_bitmap_set(tdata->base, bitmap_pos);\n+\troaring_or_raw_bitmap_set(tdata->base, bitmap_pos);\n \tdisplay_progress(tdata->prg, ++tdata->seen);\n }\n \n void test_bitmap_walk(struct rev_info *revs)\n {\n \tstruct object *root;\n-\tstruct bitmap *result = NULL;\n+\tvoid *result = NULL;\n \tsize_t result_popcnt;\n \tstruct bitmap_test_data tdata;\n \tstruct bitmap_index *bitmap_git;\n-\tstruct ewah_bitmap *bm;\n+\tvoid *bm;\n \n \tif (!(bitmap_git = prepare_bitmap_git(revs->repo)))\n \t\tdie(_(\"failed to load bitmap indexes\"));\n@@ -1982,10 +2302,17 @@ void test_bitmap_walk(struct rev_info *revs)\n \tbm = bitmap_for_commit(bitmap_git, (struct commit *)root);\n \n \tif (bm) {\n-\t\tfprintf_ln(stderr, \"Found bitmap for '%s'. %d bits / %08x checksum\",\n-\t\t\toid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));\n+\t\tif (bitmap_git->version == 1) {\n+\t\t\tstruct ewah_bitmap *ewah_bm = bm;\n+\t\t\tfprintf_ln(stderr, \"Found bitmap for '%s'. %d bits / %08x checksum\",\n+\t\t\t\toid_to_hex(&root->oid), (int)ewah_bm->bit_size, ewah_checksum(ewah_bm));\n \n-\t\tresult = ewah_to_raw_bitmap(bm);\n+\t\t\tresult = ewah_to_raw_bitmap(ewah_bm);\n+\t\t}\n+\t\telse if (bitmap_git->version == 2) {\n+\t\t\tfprintf_ln(stderr, \"Found bitmap for '%s'.\", oid_to_hex(&root->oid));\n+\t\t\tresult = roaring_bitmap_copy(bm);\n+\t\t}\n \t}\n \n \tif (!result)\n@@ -1995,17 +2322,25 @@ void test_bitmap_walk(struct rev_info *revs)\n \trevs->tree_objects = 1;\n \trevs->blob_objects = 1;\n \n-\tresult_popcnt = raw_bitmap_popcount(result);\n+\tresult_popcnt = roaring_or_raw_bitmap_cardinality(result);\n \n \tif (prepare_revision_walk(revs))\n \t\tdie(_(\"revision walk setup failed\"));\n \n \ttdata.bitmap_git = bitmap_git;\n-\ttdata.base = raw_bitmap_new();\n-\ttdata.commits = ewah_to_raw_bitmap(bitmap_git->commits);\n-\ttdata.trees = ewah_to_raw_bitmap(bitmap_git->trees);\n-\ttdata.blobs = ewah_to_raw_bitmap(bitmap_git->blobs);\n-\ttdata.tags = ewah_to_raw_bitmap(bitmap_git->tags);\n+\ttdata.base = roaring_or_raw_bitmap_new();\n+\tif (bitmap_git->version == 1) {\n+\t\ttdata.commits = ewah_to_raw_bitmap(bitmap_git->commits);\n+\t\ttdata.trees = ewah_to_raw_bitmap(bitmap_git->trees);\n+\t\ttdata.blobs = ewah_to_raw_bitmap(bitmap_git->blobs);\n+\t\ttdata.tags = ewah_to_raw_bitmap(bitmap_git->tags);\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\ttdata.commits = roaring_bitmap_copy(bitmap_git->commits);\n+\t\ttdata.trees = roaring_bitmap_copy(bitmap_git->trees);\n+\t\ttdata.blobs = roaring_bitmap_copy(bitmap_git->blobs);\n+\t\ttdata.tags = roaring_bitmap_copy(bitmap_git->tags);\n+\t}\n \ttdata.prg = start_progress(\"Verifying bitmap entries\", result_popcnt);\n \ttdata.seen = 0;\n \n@@ -2013,17 +2348,17 @@ void test_bitmap_walk(struct rev_info *revs)\n \n \tstop_progress(&tdata.prg);\n \n-\tif (raw_bitmap_equals(result, tdata.base))\n+\tif (roaring_or_raw_bitmap_equals(result, tdata.base))\n \t\tfprintf_ln(stderr, \"OK!\");\n \telse\n \t\tdie(_(\"mismatch in bitmap results\"));\n \n-\traw_bitmap_free(result);\n-\traw_bitmap_free(tdata.base);\n-\traw_bitmap_free(tdata.commits);\n-\traw_bitmap_free(tdata.trees);\n-\traw_bitmap_free(tdata.blobs);\n-\traw_bitmap_free(tdata.tags);\n+\troaring_or_raw_bitmap_free_safe(&result);\n+\troaring_or_raw_bitmap_free_safe(&tdata.base);\n+\troaring_or_raw_bitmap_free_safe(&tdata.commits);\n+\troaring_or_raw_bitmap_free_safe(&tdata.trees);\n+\troaring_or_raw_bitmap_free_safe(&tdata.blobs);\n+\troaring_or_raw_bitmap_free_safe(&tdata.tags);\n \tfree_bitmap_index(bitmap_git);\n }\n \n@@ -2081,33 +2416,52 @@ cleanup:\n \treturn 0;\n }\n \n-int rebuild_bitmap(const uint32_t *reposition,\n-\t\t   struct ewah_bitmap *source,\n-\t\t   struct bitmap *dest)\n+int rebuild_bitmap(struct bitmap_index *bitmap_git,\n+\t\t   const uint32_t *reposition,\n+\t\t   void *source,\n+\t\t   void *dest)\n {\n \tuint32_t pos = 0;\n-\tstruct ewah_iterator it;\n-\teword_t word;\n \n-\tewah_iterator_init(&it, source);\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct ewah_iterator it;\n+\t\teword_t word;\n \n-\twhile (ewah_iterator_next(&word, &it)) {\n-\t\tuint32_t offset, bit_pos;\n+\t\tewah_iterator_init(&it, source);\n \n-\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n-\t\t\tif ((word >> offset) == 0)\n-\t\t\t\tbreak;\n+\t\twhile (ewah_iterator_next(&word, &it)) {\n+\t\t\tuint32_t offset, bit_pos;\n \n-\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\t\tfor (offset = 0; offset < BITS_IN_EWORD; ++offset) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n \n-\t\t\tbit_pos = reposition[pos + offset];\n+\t\t\t\tbit_pos = reposition[pos + offset];\n+\t\t\t\tif (bit_pos > 0)\n+\t\t\t\t\troaring_or_raw_bitmap_set(dest, bit_pos - 1);\n+\t\t\t\telse /* can't reuse, we don't have the object */\n+\t\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tpos += BITS_IN_EWORD;\n+\t\t}\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\tuint32_t cardinality = roaring_bitmap_get_cardinality(source);\n+\t\tuint32_t *source_arr = NULL;\n+\t\tuint32_t i, bit_pos;\n+\n+\t\troaring_bitmap_to_uint32_array(source, source_arr);\n+\n+\t\tfor (i = 0; i < cardinality; i++) {\n+\t\t\tbit_pos = reposition[source_arr[i]];\n \t\t\tif (bit_pos > 0)\n-\t\t\t\traw_bitmap_set(dest, bit_pos - 1);\n-\t\t\telse /* can't reuse, we don't have the object */\n+\t\t\t\troaring_or_raw_bitmap_set(dest, bit_pos);\n+\t\t\telse\n \t\t\t\treturn -1;\n \t\t}\n-\n-\t\tpos += BITS_IN_EWORD;\n \t}\n \treturn 0;\n }\n@@ -2156,23 +2510,39 @@ void free_bitmap_index(struct bitmap_index *b)\n \n \tif (b->map)\n \t\tmunmap(b->map, b->map_size);\n-\tewah_pool_free(b->commits);\n-\tewah_pool_free(b->trees);\n-\tewah_pool_free(b->blobs);\n-\tewah_pool_free(b->tags);\n+\tif (b->version == 1) {\n+\t\tewah_pool_free(b->commits);\n+\t\tewah_pool_free(b->trees);\n+\t\tewah_pool_free(b->blobs);\n+\t\tewah_pool_free(b->tags);\n+\t}\n+\telse if (b->version == 2) {\n+\t\troaring_bitmap_free_safe((roaring_bitmap_t **)&b->commits);\n+\t\troaring_bitmap_free_safe((roaring_bitmap_t **)&b->trees);\n+\t\troaring_bitmap_free_safe((roaring_bitmap_t **)&b->blobs);\n+\t\troaring_bitmap_free_safe((roaring_bitmap_t **)&b->tags);\n+\t}\n \tif (b->bitmaps) {\n \t\tstruct stored_bitmap *sb;\n-\t\tkh_foreach_value(b->bitmaps, sb, {\n-\t\t\tewah_pool_free(sb->root);\n-\t\t\tfree(sb);\n-\t\t});\n+\t\tif (b->version == 1) {\n+\t\t\tkh_foreach_value(b->bitmaps, sb, {\n+\t\t\t\tewah_pool_free(sb->root);\n+\t\t\t\tfree(sb);\n+\t\t\t});\n+\t\t}\n+\t\telse if (b->version == 2) {\n+\t\t\tkh_foreach_value(b->bitmaps, sb, {\n+\t\t\t\troaring_bitmap_free_safe((roaring_bitmap_t **)&sb->root);\n+\t\t\t\tfree(sb);\n+\t\t\t});\n+\t\t}\n \t}\n \tkh_destroy_oid_map(b->bitmaps);\n \tfree(b->ext_index.objects);\n \tfree(b->ext_index.hashes);\n \tkh_destroy_oid_pos(b->ext_index.positions);\n-\traw_bitmap_free(b->result);\n-\traw_bitmap_free(b->haves);\n+\troaring_or_raw_bitmap_free_safe(&b->result);\n+\troaring_or_raw_bitmap_free_safe(&b->haves);\n \tif (bitmap_is_midx(b)) {\n \t\t/*\n \t\t * Multi-pack bitmaps need to have resources associated with\n@@ -2199,31 +2569,74 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *bitmap_git,\n static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,\n \t\t\t\t     enum object_type object_type)\n {\n-\tstruct bitmap *result = bitmap_git->result;\n+\tvoid *result = bitmap_git->result;\n \toff_t total = 0;\n-\tstruct ewah_iterator it;\n-\teword_t filter;\n \tsize_t i;\n \n-\tinit_type_iterator(&it, bitmap_git, object_type);\n-\tfor (i = 0; i < result->word_alloc &&\n-\t\t\tewah_iterator_next(&filter, &it); i++) {\n-\t\teword_t word = result->words[i] & filter;\n-\t\tsize_t base = (i * BITS_IN_EWORD);\n-\t\tunsigned offset;\n-\n-\t\tif (!word)\n-\t\t\tcontinue;\n-\n-\t\tfor (offset = 0; offset < BITS_IN_EWORD; offset++) {\n-\t\t\tif ((word >> offset) == 0)\n-\t\t\t\tbreak;\n+\tif (bitmap_git->version == 1) {\n+\t\tstruct bitmap *raw_result = result;\n+\t\tstruct ewah_iterator it;\n+\t\teword_t filter;\n+\n+\t\tinit_type_iterator(&it, bitmap_git, object_type);\n+\t\tfor (i = 0; i < raw_result->word_alloc &&\n+\t\t\t\tewah_iterator_next(&filter, &it); i++) {\n+\t\t\teword_t word = raw_result->words[i] & filter;\n+\t\t\tsize_t base = (i * BITS_IN_EWORD);\n+\t\t\tunsigned offset;\n+\n+\t\t\tif (!word)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfor (offset = 0; offset < BITS_IN_EWORD; offset++) {\n+\t\t\t\tif ((word >> offset) == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\n+\t\t\t\tif (bitmap_is_midx(bitmap_git)) {\n+\t\t\t\t\tuint32_t pack_pos;\n+\t\t\t\t\tuint32_t midx_pos = pack_pos_to_midx(bitmap_git->midx, base + offset);\n+\t\t\t\t\toff_t offset = nth_midxed_offset(bitmap_git->midx, midx_pos);\n+\n+\t\t\t\t\tuint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);\n+\t\t\t\t\tstruct packed_git *pack = bitmap_git->midx->packs[pack_id];\n+\n+\t\t\t\t\tif (offset_to_pack_pos(pack, offset, &pack_pos) < 0) {\n+\t\t\t\t\t\tstruct object_id oid;\n+\t\t\t\t\t\tnth_midxed_object_oid(&oid, bitmap_git->midx, midx_pos);\n+\n+\t\t\t\t\t\tdie(_(\"could not find '%s' in pack '%s' at offset %\"PRIuMAX),\n+\t\t\t\t\t\toid_to_hex(&oid),\n+\t\t\t\t\t\tpack->pack_name,\n+\t\t\t\t\t\t(uintmax_t)offset);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\ttotal += pack_pos_to_offset(pack, pack_pos + 1) - offset;\n+\t\t\t\t} else {\n+\t\t\t\t\tsize_t pos = base + offset;\n+\t\t\t\t\ttotal += pack_pos_to_offset(bitmap_git->pack, pos + 1) -\n+\t\t\t\t\t\tpack_pos_to_offset(bitmap_git->pack, pos);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\telse if (bitmap_git->version == 2) {\n+\t\tuint32_t *filter_arr = NULL;\n+\t\tuint32_t cardinality = 0;\n+\t\troaring_bitmap_t *filter_bitmap = NULL;\n+\t\troaring_bitmap_t *type_index = NULL;\n \n-\t\t\toffset += ewah_bit_ctz64(word >> offset);\n+\t\ttype_index = get_roaring_type_index(bitmap_git, object_type);\n+\t\tfilter_bitmap = roaring_bitmap_and(result, type_index);\n+\t\tcardinality = roaring_bitmap_get_cardinality(filter_bitmap);\n+\t\troaring_bitmap_to_uint32_array(filter_bitmap, filter_arr);\n+\t\troaring_bitmap_free(filter_bitmap);\n \n+\t\tfor (i = 0; i < cardinality; i++) {\n \t\t\tif (bitmap_is_midx(bitmap_git)) {\n \t\t\t\tuint32_t pack_pos;\n-\t\t\t\tuint32_t midx_pos = pack_pos_to_midx(bitmap_git->midx, base + offset);\n+\t\t\t\tuint32_t midx_pos = pack_pos_to_midx(bitmap_git->midx, filter_arr[i]);\n \t\t\t\toff_t offset = nth_midxed_offset(bitmap_git->midx, midx_pos);\n \n \t\t\t\tuint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);\n@@ -2234,16 +2647,16 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,\n \t\t\t\t\tnth_midxed_object_oid(&oid, bitmap_git->midx, midx_pos);\n \n \t\t\t\t\tdie(_(\"could not find '%s' in pack '%s' at offset %\"PRIuMAX),\n-\t\t\t\t\t    oid_to_hex(&oid),\n-\t\t\t\t\t    pack->pack_name,\n-\t\t\t\t\t    (uintmax_t)offset);\n+\t\t\t\t\toid_to_hex(&oid),\n+\t\t\t\t\tpack->pack_name,\n+\t\t\t\t\t(uintmax_t)offset);\n \t\t\t\t}\n \n \t\t\t\ttotal += pack_pos_to_offset(pack, pack_pos + 1) - offset;\n \t\t\t} else {\n-\t\t\t\tsize_t pos = base + offset;\n+\t\t\t\tsize_t pos = filter_arr[i];\n \t\t\t\ttotal += pack_pos_to_offset(bitmap_git->pack, pos + 1) -\n-\t\t\t\t\t pack_pos_to_offset(bitmap_git->pack, pos);\n+\t\t\t\t\tpack_pos_to_offset(bitmap_git->pack, pos);\n \t\t\t}\n \t\t}\n \t}\n@@ -2253,7 +2666,7 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,\n \n static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)\n {\n-\tstruct bitmap *result = bitmap_git->result;\n+\tvoid *result = bitmap_git->result;\n \tstruct eindex *eindex = &bitmap_git->ext_index;\n \toff_t total = 0;\n \tstruct object_info oi = OBJECT_INFO_INIT;\n@@ -2265,7 +2678,7 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)\n \tfor (i = 0; i < eindex->count; i++) {\n \t\tstruct object *obj = eindex->objects[i];\n \n-\t\tif (!raw_bitmap_get(result, bitmap_num_objects(bitmap_git) + i))\n+\t\tif (!roaring_or_raw_bitmap_get(result, bitmap_num_objects(bitmap_git) + i))\n \t\t\tcontinue;\n \n \t\tif (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)\ndiff --git a/pack-bitmap.h b/pack-bitmap.h\nindex 6103e0d57e7..e9676ec53de 100644\n--- a/pack-bitmap.h\n+++ b/pack-bitmap.h\n@@ -77,12 +77,12 @@ uint32_t midx_preferred_pack(struct bitmap_index *bitmap_git);\n int reuse_partial_packfile_from_bitmap(struct bitmap_index *,\n \t\t\t\t       struct packed_git **packfile,\n \t\t\t\t       uint32_t *entries,\n-\t\t\t\t       struct bitmap **reuse_out);\n+\t\t\t\t       void**reuse_out);\n int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,\n \t\t\t     kh_oid_map_t *reused_bitmaps, int show_progress);\n void free_bitmap_index(struct bitmap_index *);\n int bitmap_walk_contains(struct bitmap_index *,\n-\t\t\t struct bitmap *bitmap, const struct object_id *oid);\n+\t\t\t void *bitmap, const struct object_id *oid);\n \n /*\n  * After a traversal has been performed by prepare_bitmap_walk(), this can be\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nindex d953de6b7fe..e558bfcca5e 100755\n--- a/t/t5310-pack-bitmaps.sh\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -28,17 +28,20 @@ has_any () {\n \n test_bitmap_cases () {\n \twriteLookupTable=false\n+\tuseRoaringBitmap=false\n \tfor i in \"$@\"\n \tdo\n \t\tcase \"$i\" in\n \t\t\"pack.writeBitmapLookupTable\") writeLookupTable=true;;\n+\t\t\"pack.useRoaringBitmap\") useRoaringBitmap=true;;\n \t\tesac\n \tdone\n \n \ttest_expect_success 'setup test repository' '\n \t\trm -fr * .git &&\n \t\tgit init &&\n-\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"'\n+\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\tgit config pack.useRoaringBitmap '\"$useRoaringBitmap\"'\n \t'\n \tsetup_bitmap_history\n \n@@ -48,7 +51,7 @@ test_bitmap_cases () {\n \n \ttest_expect_success 'full repack creates bitmaps' '\n \t\tGIT_TRACE2_EVENT=\"$(pwd)/trace\" \\\n-\t\t\tgit repack -ad &&\n+\t\tgit repack -ad &&\n \t\tls .git/objects/pack/ | grep bitmap >output &&\n \t\ttest_line_count = 1 output &&\n \t\tgrep \"\\\"key\\\":\\\"num_selected_commits\\\",\\\"value\\\":\\\"106\\\"\" trace &&\n@@ -189,27 +192,6 @@ test_bitmap_cases () {\n \t\tgit pack-objects --stdout --revs <revs >/dev/null\n \t'\n \n-\ttest_expect_success JGIT,SHA1 'we can read jgit bitmaps' '\n-\t\tgit clone --bare . compat-jgit.git &&\n-\t\t(\n-\t\t\tcd compat-jgit.git &&\n-\t\t\trm -f objects/pack/*.bitmap &&\n-\t\t\tjgit gc &&\n-\t\t\tgit rev-list --test-bitmap HEAD\n-\t\t)\n-\t'\n-\n-\ttest_expect_success JGIT,SHA1 'jgit can read our bitmaps' '\n-\t\tgit clone --bare . compat-us.git &&\n-\t\t(\n-\t\t\tcd compat-us.git &&\n-\t\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n-\t\t\tgit repack -adb &&\n-\t\t\t# jgit gc will barf if it does not like our bitmaps\n-\t\t\tjgit gc\n-\t\t)\n-\t'\n-\n \ttest_expect_success 'splitting packs does not generate bogus bitmaps' '\n \t\ttest-tool genrandom foo $((1024 * 1024)) >rand &&\n \t\tgit add rand &&\n@@ -371,6 +353,7 @@ test_bitmap_cases () {\n \t\t(\n \t\t\tcd repo &&\n \t\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\t\tgit config pack.useRoaringBitmap '\"$useRoaringBitmap\"' &&\n \n \t\t\t# create enough commits that not all are receive bitmap\n \t\t\t# coverage even if they are all at the tip of some reference.\n@@ -411,6 +394,7 @@ test_bitmap_cases () {\n \t\t(\n \t\t\tcd repo &&\n \t\t\tgit config pack.writeBitmapLookupTable '\"$writeLookupTable\"' &&\n+\t\t\tgit config pack.useRoaringBitmap '\"$useRoaringBitmap\"' &&\n \n \t\t\ttest_commit base &&\n \n@@ -447,6 +431,26 @@ test_expect_success 'incremental repack can disable bitmaps' '\n \tgit repack -d --no-write-bitmap-index\n '\n \n+test_expect_success JGIT,SHA1 'we can read jgit bitmaps' '\n+\tgit clone --bare . compat-jgit.git &&\n+\t(\n+\t\tcd compat-jgit.git &&\n+\t\trm -f objects/pack/*.bitmap &&\n+\t\tjgit gc &&\n+\t\tgit rev-list --test-bitmap HEAD\n+\t)\n+'\n+\n+test_expect_success JGIT,SHA1 'jgit can read our bitmaps' '\n+\tgit clone --bare . compat-us.git &&\n+\t(\n+\t\tcd compat-us.git &&\n+\t\tgit repack -adb &&\n+\t\t# jgit gc will barf if it does not like our bitmaps\n+\t\tjgit gc\n+\t)\n+'\n+\n test_bitmap_cases \"pack.writeBitmapLookupTable\"\n \n test_expect_success 'verify writing bitmap lookup table when enabled' '\n@@ -475,21 +479,33 @@ test_expect_success 'truncated bitmap fails gracefully (lookup table)' '\n \ttest_i18ngrep corrupted.bitmap.index stderr\n '\n \n-test_expect_success 'setup test repository (roaring)' '\n-\trm -fr * .git &&\n-\tgit init\n+test_expect_success JGIT,SHA1 'we can read jgit bitmaps (lookup table)' '\n+\tgit clone --bare . compat-jgit.git &&\n+\t(\n+\t\tcd compat-jgit.git &&\n+\t\trm -f objects/pack/*.bitmap &&\n+\t\tjgit gc &&\n+\t\tgit rev-list --test-bitmap HEAD\n+\t)\n '\n-setup_bitmap_history\n \n-test_expect_success 'setup writing roaring bitmaps during repack' '\n-\tgit config repack.writeBitmaps true &&\n-\tgit config pack.useRoaringBitmap true\n+test_expect_success JGIT,SHA1 'jgit can read our bitmaps (lookup table)' '\n+\tgit clone --bare . compat-us.git &&\n+\t(\n+\t\tcd compat-us.git &&\n+\t\tgit config pack.writeBitmapLookupTable true &&\n+\t\tgit repack -adb &&\n+\t\t# jgit gc will barf if it does not like our bitmaps\n+\t\tjgit gc\n+\t)\n '\n \n-test_expect_success 'full repack creates roaring bitmaps' '\n-\tGIT_TRACE2_EVENT=\"$(pwd)/trace6\" \\\n-\t\tgit repack -ad &&\n-\tgrep \"\\\"label\\\":\\\"write-roaring-bitmap\\\"\" trace6\n+test_bitmap_cases 'pack.useRoaringBitmap'\n+\n+test_expect_success 'verify writing roaring bitmaps when enabled' '\n+\tGIT_TRACE2_EVENT=\"$(pwd)/trace5\" \\\n+\t\tgit repack -adb &&\n+\tgrep \"\\\"label\\\":\\\"write-roaring-bitmap\\\"\" trace5\n '\n \n test_done\n-- \ngitgitgadget\n"},{"id":"463218","messageId":"97a8eb90-06c2-f79e-fc9b-940ae89b88af@github.com","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-09-19T18:18:11Z","receivedAt":"2022-09-19T18:18:24Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 9/19/2022 1:47 PM, Abhradeep Chakraborty via GitGitGadget wrote:\n> Git currently uses ewah bitmaps ( which are based on run-length encoding) to\n> compress bitmaps. Ewah bitmaps stores bitmaps in the form of run-length\n> words i.e. instead of storing each and every bit, it tries to find\n> consecutive bits (having same value) and replace them with the value bit and\n> the range upto which the bit is present. It is simple and efficient. But one\n> downside of this approach is that we have to decompress the whole bitmap in\n> order to find the bit of a certain position.\n> \n> For small (or medium sized) bitmaps, this is not an issue. But it can be an\n> issue for large (or extra large) bitmaps. In that case roaring bitmaps are\n> generally more efficient[1] than ewah itself. Some benchmarks suggests that\n> roaring bitmaps give more performance benefits than ewah or any other\n> similar compression technique.\n> \n> This patch series is currently in RFC state and it aims to let Git use\n> roaring bitmaps. As this is an RFC patch series (for now), the code are not\n> fully accurate (i.e. some tests are failing). But it is backward-compatible\n> (tests related to ewah bitmaps are passing). Some commit messages might need\n> more explanation and some commits may need a split (specially the one that\n> implement writing roaring bitmaps). Overall, the structure and code are near\n> to ready to make the series a formal patch series.\n> \n> I am submitting it as an RFC (after discussions with mentors) because the\n> GSoC coding period is about to end. I will continue to work on the patch\n> series.\n\nI look forward to your next version. I hope to see some information about\nthe performance characteristics across the two versions. Specifically:\n\n1. How do various test in t/perf/ change between the two formats?\n2. For certain test repos (git/git, torvalds/linux, etc.) how much does\n   the .bitmap file change in size across the formats?\n \n>  Makefile                   |     3 +\n>  bitmap.c                   |   225 +\n>  bitmap.h                   |    33 +\n...\n>  ewah/bitmap.c              |    61 +-\n>  ewah/ewok.h                |    37 +-\n...\n>  roaring/roaring.c          | 20047 +++++++++++++++++++++++++++++++++++\n>  roaring/roaring.h          |  1028 ++\n\nI wonder if there is value in modifying the structure of these files\ninto a bitmap/ directory and then perhaps ewah/ and roaring/ within\neach? Just a thought.\n\nThanks,\n-Stolee\n\n"},{"id":"463219","messageId":"b727c25c-469f-ca56-bbd6-82f82c762523@github.com","threadId":"58458","inReplyTo":"38ec2360f4fbfe65fa2d9f1e9cfb7d4944d1714f.1663609659.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-09-19T18:33:51Z","receivedAt":"2022-09-19T18:34:03Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 9/19/2022 1:47 PM, Abhradeep Chakraborty via GitGitGadget wrote:\n> From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n> \n> Though the Roaring library is introduced in previous commit, the library\n> cannot be used as is. One reason is that the library doesn't support Big\n> endian machines. Besides, Git specific file related functions does use\n> `hashwrite()` (or similar). So there is a need to modify the library.\n\nThere are a few refactorings happening in this single patch, so it\nmight be good to split them out for easier spot-checking from the\nreviewer's perspective. I'll try to list the ones I see.\n \n\n>  int32_t array_container_write(const array_container_t *container, char *buf);\n> +\n> +int array_container_network_write(const array_container_t *container,\n> +\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n> +\t\t\t\t  void *data);\n\nShould we make write_fn a defined type? I'm not sure I've seen this\nimplicit type within a function declaration before.\n\n>  /**\n>   * Reads the instance from buf, outputs how many bytes were read.\n>   * This is meant to be byte-by-byte compatible with the Java and Go versions of\n> @@ -1801,6 +1805,9 @@ int32_t array_container_write(const array_container_t *container, char *buf);\n>  int32_t array_container_read(int32_t cardinality, array_container_t *container,\n>                               const char *buf);\n>  \n> +int32_t array_container_network_read(int32_t cardinality, array_container_t *container,\n> +                        \t     const char *buf);\n> +\n\nBoth of these functions are creating new implementations instead\nof modifying the existing implementations. Is there any reason\nwhy we should keep both of these in perpetuity? They are likely\nto drift if we do that.\n\n> +static int container_network_write(const container_t *c, uint8_t typecode,\n> +\t\t\t\t   int (*write_fn) (void *, const void *, size_t),\n> +\t\t\t\t   void *data)\n> +{\n> +\tc = container_unwrap_shared(c, &typecode);\n> +\tswitch (typecode) {\n> +\t\tcase BITSET_CONTAINER_TYPE:\n> +\t\t\treturn bitset_container_network_write(const_CAST_bitset(c), write_fn, data);\n> +\t\tcase ARRAY_CONTAINER_TYPE:\n> +\t\t\treturn array_container_network_write(const_CAST_array(c), write_fn, data);\n> +\t\tcase RUN_CONTAINER_TYPE:\n> +\t\t\treturn run_container_network_write(const_CAST_run(c), write_fn, data);\n> +\t}\n> +\tassert(false);\n> +\t__builtin_unreachable();\n> +\treturn 0;\n> +}\n> +\n\nThis similarly is a copy of an existing function. Instead we\nshould probably make all writers/readers expect network byte\norder (for all multi-word integers).\n\n> +static size_t ra_portable_network_size_in_bytes(const roaring_array_t *ra)\n> +{\n> +\tsize_t count = ra_portable_network_header_size(ra);\n> +\n> +\tfor (int32_t k = 0; k < ra->size; ++k)\n\nWe have not loosened the restriction on defining iterator variables\nwithin the for and instead would need this in the outer block. One\npossible refactoring would be to move these definitions everywhere\nwithin roaring.c.\n\n> @@ -8603,16 +8981,16 @@ extern inline void roaring_bitmap_remove_range(roaring_bitmap_t *r, uint64_t min\n>  void roaring_bitmap_printf(const roaring_bitmap_t *r) {\n>      const roaring_array_t *ra = &r->high_low_container;\n>  \n> -    printf(\"{\");\n> +    fprintf(stderr, \"{\");\n>      for (int i = 0; i < ra->size; ++i) {\n>          container_printf_as_uint32_array(ra->containers[i], ra->typecodes[i],\n>                                           ((uint32_t)ra->keys[i]) << 16);\n>  \n>          if (i + 1 < ra->size) {\n> -            printf(\",\");\n> +            fprintf(stderr, \",\");\n>          }\n>      }\n> -    printf(\"}\");\n> +    fprintf(stderr, \"}\");\n>  }\n\nThis change is confusing to me. I epxect the printf() to print to\nstdout, and this might be used in a test helper or something. If\nyou really want this to go somewhere other than stdout, then the\nmethod should be changed to take an arbitrary FILE*.\n\n> +void roaring_bitmap_free_safe(roaring_bitmap_t **r)\n> +{\n> +\tif (*r) {\n> +\t\troaring_bitmap_free((const roaring_bitmap_t *)*r);\n> +\t\tr = NULL;\n\nI think you want \"*r = NULL\" here, if you are intending to free\nand NULL the given address.\n\nThis method seems separate from the network-byte-order changes.\n\n> +\t}\n> +}\n> +\n  \n> +size_t roaring_bitmap_network_portable_size_in_bytes(const roaring_bitmap_t *r)\n> +{\n> +\treturn ra_portable_network_size_in_bytes(&r->high_low_container);\n> +}\n\nDoes network order change the potential size of the bitmap?\n\n> +roaring_bitmap_t *roaring_bitmap_portable_network_deserialize_safe(const char *buf, size_t maxbytes)\n> +{\n> +\troaring_bitmap_t *ans =\n> +\t\t(roaring_bitmap_t *)roaring_malloc(sizeof(roaring_bitmap_t));\n> +\tif (ans == NULL) {\n> +\t\treturn NULL;\n> +\t}\n\nnit: Lose braces around single-line blocks.\n\n> +\tsize_t bytesread;\n> +\tbool is_ok = ra_portable_network_deserialize(&ans->high_low_container, buf, maxbytes, &bytesread);\n\nDeclare all variables before your logic. I think this will fail if\nyou run \"make DEVELOPER=1\".\n\n> +\tif(is_ok) assert(bytesread <= maxbytes);\n\nnit: break lines for if bodies.\n\n> +\troaring_bitmap_set_copy_on_write(ans, false);\n> +\tif (!is_ok) {\n> +\t\troaring_free(ans);\n> +\t\treturn NULL;\n> +\t}\n> +\treturn ans;\n> +}\n> +\n\n>  size_t roaring_bitmap_portable_serialize(const roaring_bitmap_t *r,\n>                                           char *buf) {\n>      return ra_portable_serialize(&r->high_low_container, buf);\n>  }\n>  \n> +int roaring_bitmap_portable_network_serialize(roaring_bitmap_t *rb,\n> +\t\t\t\t     int (*write_fn) (void *, const void *, size_t),\n> +\t\t\t\t     void *data)\n> +{\n> +\treturn ra_portable_network_serialize(&rb->high_low_container, write_fn, data);\n> +}\n\nI'm not sure why these methods are created as wrappers instead of\nrenaming the base methods.\n\n\n>  roaring_bitmap_t *roaring_bitmap_deserialize(const void *buf) {\n>      const char *bufaschar = (const char *)buf;\n>      if (*(const unsigned char *)buf == CROARING_SERIALIZATION_ARRAY_UINT32) {\n> @@ -13827,9 +14247,9 @@ void array_container_printf_as_uint32_array(const array_container_t *v,\n>      if (v->cardinality == 0) {\n>          return;\n>      }\n> -    printf(\"%u\", v->array[0] + base);\n> +    fprintf(stderr, \"%u\", v->array[0] + base);\n>      for (int i = 1; i < v->cardinality; ++i) {\n> -        printf(\",%u\", v->array[i] + base);\n> +        fprintf(stderr, \",%u\", v->array[i] + base);\n\nHere's another printf to fprintf situation that is unclear to me.\n\n> @@ -15208,13 +15659,13 @@ void run_container_printf_as_uint32_array(const run_container_t *cont,\n>      {\n>          uint32_t run_start = base + cont->runs[0].value;\n>          uint16_t le = cont->runs[0].length;\n> -        printf(\"%u\", run_start);\n> -        for (uint32_t j = 1; j <= le; ++j) printf(\",%u\", run_start + j);\n> +        fprintf(stderr, \"%u\", run_start);\n> +        for (uint32_t j = 1; j <= le; ++j) fprintf(stderr, \",%u\", run_start + j);\n\nDitto here. I see we are inheriting off-style code from the original.\n\n> +/**\n> + * Frees the memory if exists\n> + */\n> +void roaring_bitmap_free_safe(roaring_bitmap_t **r);\n\nAnd nullifies the pointer, don't forget!\n\nIn general, I think this change would be a lot smaller if you took\nthe existing implementation and inserted the proper ntohl() and\nhtonl() conversions. Git will never call the other versions, so\nwhy keep them in the tree? Why require re-checking all of the format\nlogic here instead of only the places where we write multi-byte\nwords?\n\nThanks,\n-Stolee\n"},{"id":"463237","messageId":"xmqqr10781lx.fsf@gitster.g","threadId":"58458","inReplyTo":"b727c25c-469f-ca56-bbd6-82f82c762523@github.com","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-19T22:02:02Z","receivedAt":"2022-09-19T22:02:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <derrickstolee@github.com> writes:\n\n>>  int32_t array_container_write(const array_container_t *container, char *buf);\n>> +\n>> +int array_container_network_write(const array_container_t *container,\n>> +\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n>> +\t\t\t\t  void *data);\n>\n> Should we make write_fn a defined type? I'm not sure I've seen this\n> implicit type within a function declaration before.\n\nUnless we can point out why having a named type is a good idea\n(e.g. we add such a function pointer as a member of a struct, or we\nkeep a variable of that type somewhere), I actually would prefer to\ndo without them.\n\nPerhaps there are some more important reasons I am missing why we\noften come up with explicit types for callback function pointers in\nmany parts of our API, but if there aren't, my preference actually\nis to lose them, not add more of them.\n\nHmph.... could \"a typedef can become a place to give definitive\ndocumentation for the class of callback functions\" be a good reason\nwhy we would want one?  I dunno.\n\nIn the posted patch, readers cannot tell what kind of three\nparameters they are supposed to give to write_fn().\n\nThanks.\n"},{"id":"463300","messageId":"990f84f9-fdd9-0d0a-4fc0-d0dbd19ee5a9@github.com","threadId":"58458","inReplyTo":"xmqqr10781lx.fsf@gitster.g","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-09-20T12:19:14Z","receivedAt":"2022-09-20T12:19:21Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 9/19/2022 6:02 PM, Junio C Hamano wrote:\n> Derrick Stolee <derrickstolee@github.com> writes:\n> \n>>>  int32_t array_container_write(const array_container_t *container, char *buf);\n>>> +\n>>> +int array_container_network_write(const array_container_t *container,\n>>> +\t\t\t\t  int (*write_fn) (void *, const void *, size_t),\n>>> +\t\t\t\t  void *data);\n>>\n>> Should we make write_fn a defined type? I'm not sure I've seen this\n>> implicit type within a function declaration before.\n> \n> Unless we can point out why having a named type is a good idea\n> (e.g. we add such a function pointer as a member of a struct, or we\n> keep a variable of that type somewhere), I actually would prefer to\n> do without them.\n> \n> Perhaps there are some more important reasons I am missing why we\n> often come up with explicit types for callback function pointers in\n> many parts of our API, but if there aren't, my preference actually\n> is to lose them, not add more of them.\n> \n> Hmph.... could \"a typedef can become a place to give definitive\n> documentation for the class of callback functions\" be a good reason\n> why we would want one?  I dunno.\n> \n> In the posted patch, readers cannot tell what kind of three\n> parameters they are supposed to give to write_fn().\n\nThis is exactly my reasoning. Having a clear definition gives us an\nopportunity to document what each parameter is for, even if it is\njust a variable name.\n\nThis anonymous type is used in multiple places, so it can be helpful\nto know that the type is connected across call sites or a stack of\nmethod calls.\n\nIn the unlikely event that we needed to modify this callback\nsignature, changing it in one place makes it clear that we cover\nall connected uses instead of tracking all of these anonymous\nfunctions across multiple methods.\n\nThanks,\n-Stolee\n"},{"id":"463304","messageId":"CAPOJW5wkXrV8eOysz6aJ5jN2u_u-iTX_3om3tSDKw+EmfCJBEw@mail.gmail.com","threadId":"58458","inReplyTo":"97a8eb90-06c2-f79e-fc9b-940ae89b88af@github.com","subject":"Re: [PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-09-20T14:05:38Z","receivedAt":"2022-09-20T14:06:32Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"Hi Derrick,\n\nOn Mon, Sep 19, 2022 at 11:48 PM Derrick Stolee\n<derrickstolee@github.com> wrote:\n> I look forward to your next version. I hope to see some information about\n> the performance characteristics across the two versions. Specifically:\n>\n> 1. How do various test in t/perf/ change between the two formats?\n> 2. For certain test repos (git/git, torvalds/linux, etc.) how much does\n>    the .bitmap file change in size across the formats?\n\nYeah, sure. I will be including the performance test result in the\nnext version :)\n\n> >  Makefile                   |     3 +\n> >  bitmap.c                   |   225 +\n> >  bitmap.h                   |    33 +\n> ...\n> >  ewah/bitmap.c              |    61 +-\n> >  ewah/ewok.h                |    37 +-\n> ...\n> >  roaring/roaring.c          | 20047 +++++++++++++++++++++++++++++++++++\n> >  roaring/roaring.h          |  1028 ++\n>\n> I wonder if there is value in modifying the structure of these files\n> into a bitmap/ directory and then perhaps ewah/ and roaring/ within\n> each? Just a thought.\n\nGreat idea! Thanks! Will change it in the next version..\n\nThanks :)\n"},{"id":"463307","messageId":"CAPOJW5zxoaF2NWtNiYZT3ve_boR40yvg=-3WC7dkjy63a=tVjw@mail.gmail.com","threadId":"58458","inReplyTo":"b727c25c-469f-ca56-bbd6-82f82c762523@github.com","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-09-20T14:46:33Z","receivedAt":"2022-09-20T14:47:09Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Tue, Sep 20, 2022 at 12:03 AM Derrick Stolee\n<derrickstolee@github.com> wrote:\n>\n> On 9/19/2022 1:47 PM, Abhradeep Chakraborty via GitGitGadget wrote:\n> > From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n> >\n> > Though the Roaring library is introduced in previous commit, the library\n> > cannot be used as is. One reason is that the library doesn't support Big\n> > endian machines. Besides, Git specific file related functions does use\n> > `hashwrite()` (or similar). So there is a need to modify the library.\n>\n> There are a few refactorings happening in this single patch, so it\n> might be good to split them out for easier spot-checking from the\n> reviewer's perspective. I'll try to list the ones I see.\n\nTrue, I will split this commit into two or three parts (as I mentioned\nin the cover letter). I forgot to commit changes one by one while\nimplementing this part. That's why all changes are packed in one\ncommit.\n\n> >  int32_t array_container_write(const array_container_t *container, char *buf);\n> > +\n> > +int array_container_network_write(const array_container_t *container,\n> > +                               int (*write_fn) (void *, const void *, size_t),\n> > +                               void *data);\n>\n> Should we make write_fn a defined type? I'm not sure I've seen this\n> implicit type within a function declaration before.\n\nI am not sure about that. This function is highly inspired by ewah's\n`ewah_serialize_to` function which also has the same kind of\ndeclaration. I have no problem if we make write_fn a defined type\nthough.\n\n\n> >  /**\n> >   * Reads the instance from buf, outputs how many bytes were read.\n> >   * This is meant to be byte-by-byte compatible with the Java and Go versions of\n> > @@ -1801,6 +1805,9 @@ int32_t array_container_write(const array_container_t *container, char *buf);\n> >  int32_t array_container_read(int32_t cardinality, array_container_t *container,\n> >                               const char *buf);\n> >\n> > +int32_t array_container_network_read(int32_t cardinality, array_container_t *container,\n> > +                                  const char *buf);\n> > +\n>\n> Both of these functions are creating new implementations instead\n> of modifying the existing implementations. Is there any reason\n> why we should keep both of these in perpetuity? They are likely\n> to drift if we do that.\n\nNo, there is no reason behind this. I thought it might be a good idea\nto have the existing implementations. But it's just my thought.\n\n> > +static int container_network_write(const container_t *c, uint8_t typecode,\n> > +                                int (*write_fn) (void *, const void *, size_t),\n> > +                                void *data)\n> > +{\n> > +     c = container_unwrap_shared(c, &typecode);\n> > +     switch (typecode) {\n> > +             case BITSET_CONTAINER_TYPE:\n> > +                     return bitset_container_network_write(const_CAST_bitset(c), write_fn, data);\n> > +             case ARRAY_CONTAINER_TYPE:\n> > +                     return array_container_network_write(const_CAST_array(c), write_fn, data);\n> > +             case RUN_CONTAINER_TYPE:\n> > +                     return run_container_network_write(const_CAST_run(c), write_fn, data);\n> > +     }\n> > +     assert(false);\n> > +     __builtin_unreachable();\n> > +     return 0;\n> > +}\n> > +\n>\n> This similarly is a copy of an existing function. Instead we\n> should probably make all writers/readers expect network byte\n> order (for all multi-word integers).\n\nOk, sure.\n\n> > +static size_t ra_portable_network_size_in_bytes(const roaring_array_t *ra)\n> > +{\n> > +     size_t count = ra_portable_network_header_size(ra);\n> > +\n> > +     for (int32_t k = 0; k < ra->size; ++k)\n>\n> We have not loosened the restriction on defining iterator variables\n> within the for and instead would need this in the outer block. One\n> possible refactoring would be to move these definitions everywhere\n> within roaring.c.\n\nThe problem I faced with roaring.c is that it doesn't follow any kind\nof style convention. E.g. in many functions, variables are declared in\nrandom positions (instead of initial lines). This is causing errors\nlike \"forbids mixed declarations and code\", \"git log --check failed\"\netc.\n\n> > @@ -8603,16 +8981,16 @@ extern inline void roaring_bitmap_remove_range(roaring_bitmap_t *r, uint64_t min\n> >  void roaring_bitmap_printf(const roaring_bitmap_t *r) {\n> >      const roaring_array_t *ra = &r->high_low_container;\n> >\n> > -    printf(\"{\");\n> > +    fprintf(stderr, \"{\");\n> >      for (int i = 0; i < ra->size; ++i) {\n> >          container_printf_as_uint32_array(ra->containers[i], ra->typecodes[i],\n> >                                           ((uint32_t)ra->keys[i]) << 16);\n> >\n> >          if (i + 1 < ra->size) {\n> > -            printf(\",\");\n> > +            fprintf(stderr, \",\");\n> >          }\n> >      }\n> > -    printf(\"}\");\n> > +    fprintf(stderr, \"}\");\n> >  }\n>\n> This change is confusing to me. I epxect the printf() to print to\n> stdout, and this might be used in a test helper or something. If\n> you really want this to go somewhere other than stdout, then the\n> method should be changed to take an arbitrary FILE*.\n\nI think it's better to undo the changes. I was using it for debugging.\n\n>\n> > +void roaring_bitmap_free_safe(roaring_bitmap_t **r)\n> > +{\n> > +     if (*r) {\n> > +             roaring_bitmap_free((const roaring_bitmap_t *)*r);\n> > +             r = NULL;\n>\n> I think you want \"*r = NULL\" here, if you are intending to free\n> and NULL the given address.\n\nThanks for pointing this out!\n\n> This method seems separate from the network-byte-order changes.\n\nYeah, I will split them in the next version.\n\n> > +size_t roaring_bitmap_network_portable_size_in_bytes(const roaring_bitmap_t *r)\n> > +{\n> > +     return ra_portable_network_size_in_bytes(&r->high_low_container);\n> > +}\n>\n> Does network order change the potential size of the bitmap?\n\nYeah, network order bitmap size is 4 byte shorter than its non-network\nordered bitmap counterpart.\n\n>\n> > +     size_t bytesread;\n> > +     bool is_ok = ra_portable_network_deserialize(&ans->high_low_container, buf, maxbytes, &bytesread);\n>\n> Declare all variables before your logic. I think this will fail if\n> you run \"make DEVELOPER=1\".\n>\n> > +     if(is_ok) assert(bytesread <= maxbytes);\n>\n> nit: break lines for if bodies.\n\nI copied it from the original one and as I said before the coding\nstyles are bad. Anyways, I will modify the original ones.\n\n>\n> > +/**\n> > + * Frees the memory if exists\n> > + */\n> > +void roaring_bitmap_free_safe(roaring_bitmap_t **r);\n>\n> And nullifies the pointer, don't forget!\n\nOh, thanks!\n\n> In general, I think this change would be a lot smaller if you took\n> the existing implementation and inserted the proper ntohl() and\n> htonl() conversions. Git will never call the other versions, so\n> why keep them in the tree? Why require re-checking all of the format\n> logic here instead of only the places where we write multi-byte\n> words?\n\nGot it. Thanks :)\n"},{"id":"463309","messageId":"CAPOJW5yeg-+5F-Tabdt2PbmBg=qebF-7F38rFaGKJ7zROrSRqQ@mail.gmail.com","threadId":"58458","inReplyTo":"990f84f9-fdd9-0d0a-4fc0-d0dbd19ee5a9@github.com","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-09-20T15:09:34Z","receivedAt":"2022-09-20T15:10:00Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Tue, Sep 20, 2022 at 5:49 PM Derrick Stolee <derrickstolee@github.com> wrote:\n>\n> On 9/19/2022 6:02 PM, Junio C Hamano wrote:\n> > Derrick Stolee <derrickstolee@github.com> writes:\n> >\n> >>>  int32_t array_container_write(const array_container_t *container, char *buf);\n> >>> +\n> >>> +int array_container_network_write(const array_container_t *container,\n> >>> +                             int (*write_fn) (void *, const void *, size_t),\n> >>> +                             void *data);\n> >>\n> >> Should we make write_fn a defined type? I'm not sure I've seen this\n> >> implicit type within a function declaration before.\n> >\n> > Unless we can point out why having a named type is a good idea\n> > (e.g. we add such a function pointer as a member of a struct, or we\n> > keep a variable of that type somewhere), I actually would prefer to\n> > do without them.\n> >\n> > Perhaps there are some more important reasons I am missing why we\n> > often come up with explicit types for callback function pointers in\n> > many parts of our API, but if there aren't, my preference actually\n> > is to lose them, not add more of them.\n> >\n> > Hmph.... could \"a typedef can become a place to give definitive\n> > documentation for the class of callback functions\" be a good reason\n> > why we would want one?  I dunno.\n> >\n> > In the posted patch, readers cannot tell what kind of three\n> > parameters they are supposed to give to write_fn().\n>\n> This is exactly my reasoning. Having a clear definition gives us an\n> opportunity to document what each parameter is for, even if it is\n> just a variable name.\n\nAgreed.\n\n> This anonymous type is used in multiple places, so it can be helpful\n> to know that the type is connected across call sites or a stack of\n> method calls.\n>\n> In the unlikely event that we needed to modify this callback\n> signature, changing it in one place makes it clear that we cover\n> all connected uses instead of tracking all of these anonymous\n> functions across multiple methods.\n\nGot it. Thanks!\n"},{"id":"463352","messageId":"Yyo3pMlGkw1TWLDQ@nand.local","threadId":"58458","inReplyTo":"pull.1357.git.1663609659.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-09-20T21:59:00Z","receivedAt":"2022-09-20T21:59:06Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Mon, Sep 19, 2022 at 05:47:34PM +0000, Abhradeep Chakraborty via GitGitGadget wrote:\n> This patch series is currently in RFC state and it aims to let Git use\n> roaring bitmaps. As this is an RFC patch series (for now), the code are not\n> fully accurate (i.e. some tests are failing). But it is backward-compatible\n> (tests related to ewah bitmaps are passing). Some commit messages might need\n> more explanation and some commits may need a split (specially the one that\n> implement writing roaring bitmaps). Overall, the structure and code are near\n> to ready to make the series a formal patch series.\n\nExtremely exciting. Congratulations on all of your work so far. I'm\nhopeful that you'll continue working on this after GSoC is over (for\nthose playing along at home, Abhradeep's coding period was extended by a\ncouple of weeks).\n\nBut even if you don't, this is a great artifact to leave around on the\nlist for somebody else who is interested in this area to pick up in the\nfuture, and benefit from all of the work that you've done so far.\n\nI am still working through my post-Git Merge backlog, but I'm looking\nforward to reading these patches soon. I'm glad that other reviewers\nhave already started to dive in :-).\n\nWell done!\n\n\nThanks,\nTaylor\n"},{"id":"463369","messageId":"CAPOJW5zJ=5RiV+bVg_0pgJ=cZKV0TC0=RgRdH4r7D-D8Q9vnkw@mail.gmail.com","threadId":"58458","inReplyTo":"Yyo3pMlGkw1TWLDQ@nand.local","subject":"Re: [PATCH 0/5] [RFC] introduce Roaring bitmaps to Git","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-09-21T15:27:07Z","receivedAt":"2022-09-21T15:31:51Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Wed, Sep 21, 2022 at 3:29 AM Taylor Blau <me@ttaylorr.com> wrote:\n>\n> On Mon, Sep 19, 2022 at 05:47:34PM +0000, Abhradeep Chakraborty via GitGitGadget wrote:\n> > This patch series is currently in RFC state and it aims to let Git use\n> > roaring bitmaps. As this is an RFC patch series (for now), the code are not\n> > fully accurate (i.e. some tests are failing). But it is backward-compatible\n> > (tests related to ewah bitmaps are passing). Some commit messages might need\n> > more explanation and some commits may need a split (specially the one that\n> > implement writing roaring bitmaps). Overall, the structure and code are near\n> > to ready to make the series a formal patch series.\n>\n> Extremely exciting. Congratulations on all of your work so far. I'm\n> hopeful that you'll continue working on this after GSoC is over (for\n> those playing along at home, Abhradeep's coding period was extended by a\n> couple of weeks).\n\nYeah, I will continue (or better to say I am continuing) my work. I\nhope that I can submit the next version in the upcoming few days.\n\nThanks for supporting and guiding me throughout the GSoC period. I\nhave learned a lot of new things during this period.\n\n> I am still working through my post-Git Merge backlog, but I'm looking\n> forward to reading these patches soon. I'm glad that other reviewers\n> have already started to dive in :-).\n\nNo problem, I am doing some improvements by this time.\nBy the way, I am very excited to see the Youtube Git-Merge recordings ;)\n\nThanks :)\n"},{"id":"463376","messageId":"xmqq5yhg64vn.fsf@gitster.g","threadId":"58458","inReplyTo":"990f84f9-fdd9-0d0a-4fc0-d0dbd19ee5a9@github.com","subject":"Re: [PATCH 2/5] roaring.[ch]: apply Git specific changes to the roaring API","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-21T16:58:52Z","receivedAt":"2022-09-21T16:59:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <derrickstolee@github.com> writes:\n\n>> Hmph.... could \"a typedef can become a place to give definitive\n>> documentation for the class of callback functions\" be a good reason\n>> why we would want one?  I dunno.\n>> \n>> In the posted patch, readers cannot tell what kind of three\n>> parameters they are supposed to give to write_fn().\n>\n> This is exactly my reasoning. Having a clear definition gives us an\n> opportunity to document what each parameter is for, even if it is\n> just a variable name.\n\nYeah, I completely agree with you on that line of reasoning.\n\n> In the unlikely event that we needed to modify this callback\n> signature, changing it in one place makes it clear that we cover\n> all connected uses instead of tracking all of these anonymous\n> functions across multiple methods.\n\nWell, the compiler will help flagging a caller that forgot to\nconvert, even if there is no typedef.  \n\nThe parameter list may not have to be updated for a function that\ntakes a callback function of this type as its parameter if you did\nnot use a typedef, but where it in turn makes a call to that\ncallback function, the compiler will notice the argument mismatch,\nwhich you have to adjust to the new calling convention anyway.\n\nWhat is somewhat sad is that even with typedef, the implementation\nof a callback function cannot use the type, but the longhand that\nunderlies the typedef, to define it, but that is not something we\ncan fix, or are interested in fixing ;-).\n\nThanks.\n"},{"id":"463928","messageId":"xmqqczbdl6wl.fsf@gitster.g","threadId":"58458","inReplyTo":"4364224f9bddc8f1e40875ebc540b28225317176.1663609659.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-09-30T06:20:58Z","receivedAt":"2022-09-30T06:21:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Abhradeep Chakraborty via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n>\n> Roaring bitmaps are said to be more efficient (most of the time) than\n> ewah bitmaps. So Git might gain some optimization if it support roaring\n> bitmaps. As Roaring library has all the changes it needed to implement\n> roaring bitmaps in Git, Git can learn to write roaring bitmaps. However,\n> all the changes are backward-compatible.\n>\n> Teach Git to write roaring bitmaps.\n\nThat is way underexplained.   At least cover what the plans are, so\nthat readers do not have to ask these questions:\n\n * When is the choice of bitmap type is made?  Is it fixed at\n   repository initialization time and once chosen other kinds cannot\n   be used?\n\n * Is the bitmap file self describing?  How does a reader know\n   between ewah and roaring codepaths to use to read a given bitmap\n   file?  Is there enough room for extending the set of bitmap\n   formats, or we cannot add other formats easily?\n\n> Mentored-by: Taylor Blau <me@ttaylorr.com>\n> Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>\n> Signed-off-by: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n> ---\n>  Makefile                |   1 +\n>  bitmap.c                | 225 +++++++++++++++++++++++++++\n>  bitmap.h                |  33 ++++\n>  builtin/diff.c          |  10 +-\n>  ewah/bitmap.c           |  61 +++++---\n>  ewah/ewok.h             |  37 ++---\n>  pack-bitmap-write.c     | 326 ++++++++++++++++++++++++++++++----------\n>  pack-bitmap.c           | 114 +++++++-------\n>  pack-bitmap.h           |  22 ++-\n>  t/t5310-pack-bitmaps.sh |  17 +++\n>  10 files changed, 664 insertions(+), 182 deletions(-)\n>  create mode 100644 bitmap.c\n>  create mode 100644 bitmap.h\n>\n> diff --git a/Makefile b/Makefile\n> index e9537951105..9ca19b3ca8d 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -900,6 +900,7 @@ LIB_OBJS += archive.o\n>  LIB_OBJS += attr.o\n>  LIB_OBJS += base85.o\n>  LIB_OBJS += bisect.o\n> +LIB_OBJS += bitmap.o\n>  LIB_OBJS += blame.o\n>  LIB_OBJS += blob.o\n>  LIB_OBJS += bloom.o\n> diff --git a/bitmap.c b/bitmap.c\n> new file mode 100644\n> index 00000000000..7d547eb9f53\n> --- /dev/null\n> +++ b/bitmap.c\n> @@ -0,0 +1,225 @@\n> +#include \"bitmap.h\"\n> +#include \"cache.h\"\n> +\n> +static enum bitmap_type bitmap_type = INIT_BITMAP_TYPE;\n\n\"INIT\" is a strange name for \"UNINITIALIZED\".  Especially ...\n\n> +void *roaring_or_ewah_bitmap_init(void)\n> +{\n> +\tswitch (bitmap_type)\n> +\t{\n\n(Style)\n\n> +\tcase EWAH:\n> +\t\treturn ewah_new();\n> +\tcase ROARING:\n> +\t\treturn roaring_bitmap_create();\n> +\tdefault:\n\n... here, you use it to mean exactly that.\n\n> +\t\terror(_(\"bitmap type not initialized\\n\"));\n> +\t\treturn NULL;\n\nDo you really need the global variable that holds the bitmap type?\n\nWouldn't it be easier to write code that needs to deal with both\ntypes (e.g. in a repository with existing ewah bitmap, you want to\ndo a repack and index the result using the roaring bitmap) if you\npassed the type through the callchain as a parameter?\n\nIt may be that the codepath that reads from an existing bitmap file\nsays \"ah, the file given to us seems to be in format X (either EWAH\nor ROARING or perhaps something else), so let's call bitmap_init(X)\nto obtain the in-core data structure to deal with that file\".  When\nthat happens, you may probably need to have two cases in the default:\narm of this switch statement, i.e. one to diagnose a BUG() to pass\nan uninitialized bitmap type to the codepath, and the other to\ndiagnose a runtime error() to have read a bitmap file whose format\nthis version of Git does not understand.\n\n> +void *roaring_or_raw_bitmap_copy(void *bitmap)\n> +{\n> +\tswitch (bitmap_type)\n> +\t{\n> +\tcase EWAH:\n> ...\n> +int roaring_or_ewah_bitmap_set(void *bitmap, uint32_t i)\n> +{\n> +\tswitch (bitmap_type) {\n> +\tcase EWAH:\n> +...\n> +void roaring_or_raw_bitmap_set(void *bitmap, uint32_t i)\n> +{\n> +\tswitch (bitmap_type)\n> +\t{\n> +\tcase EWAH:\n> +...\n> +void roaring_or_raw_bitmap_unset(void *bitmap, uint32_t i)\n> +{\n> +\tswitch (bitmap_type)\n> +\t{\n> +\tcase EWAH:\n> +...\n\nThese repetitive patterns makes me wonder if void *bitmap\nis a good type to be passing around.  Shouldn't it be a struct with\nits first member being a bitmap_type, and another member being what\nthese functions are passing to the underlying bitmap format specific\nfunctions as \"bitmap\"?  E.g.\n\n    void bitmap_unset(struct bitmap *bm, uint32_t i)\n    {\n\tswitch (bm->type) {\n\tcase EWAH:\n\t\tewah_bitmap_remove(bm->u.ewah, i);\n\t\tbreak;\n\t...\n\n\n> \\ No newline at end of file\n\nCareful.\n\n> diff --git a/bitmap.h b/bitmap.h\n> new file mode 100644\n> index 00000000000..d75400922cc\n> --- /dev/null\n> +++ b/bitmap.h\n> @@ -0,0 +1,33 @@\n> +#ifndef __BITMAP_H__\n> +#define __BITMAP_H__\n> +\n> +\n> +#include \"git-compat-util.h\"\n> +#include \"ewah/ewok.h\"\n> +#include \"roaring/roaring.h\"\n> +\n> +enum bitmap_type {\n> +\tINIT_BITMAP_TYPE = 0,\n\n\"UNINITIALIZED_BITMAP_TYPE\", probably.\n\n> +void *roaring_or_ewah_bitmap_init(void);\n\nI would strongly suggest reconsider these names.  What if you later\nwant to add the third variant?  roaring_or_ewah_or_xyzzy_bitmap_init()?\n\nInstead just use the most generic name, like \"bitmap_init\", perhaps\nsomething along the lines of ...\n\n    struct bitmap {\n\tenum bitmap_type type;\n\tunion {\n\t    struct ewah_bitmap *ewah;\n\t    struct roaring_bitmap *roaring;\n\t} u;\t\n    };\n\n    struct bitmap *bitmap_new(enum bitmap_type type)\n    {\n\tstruct bitmap *bm = xmalloc(sizeof(*bm));\n\n\tbm->type = type;\n\tswitch (bm->type) {\n\tcase EWAH:\n\t    bm->u.ewah = ewah_new();\n\t    break;\n\tcase ROARING:\n\t    bm->u.roaring = roaring_bitmap_create();\n\t    break;\n        default:\n\t    die(_(\"unknown bitmap type %d\"), (int)type);\n\t}\n\treturn bm;\n    }\n\nI dunno.\n\n"},{"id":"463960","messageId":"CAPOJW5yxRETdVk014gQYFud9_Nrt+OQGSVNQ8Pw2wDEMMFMm1Q@mail.gmail.com","threadId":"58458","inReplyTo":"xmqqczbdl6wl.fsf@gitster.g","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-09-30T16:23:45Z","receivedAt":"2022-09-30T16:24:03Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Fri, Sep 30, 2022 at 11:51 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Abhradeep Chakraborty via GitGitGadget\" <gitgitgadget@gmail.com>\n> writes:\n>\n> > From: Abhradeep Chakraborty <chakrabortyabhradeep79@gmail.com>\n> >\n> > Roaring bitmaps are said to be more efficient (most of the time) than\n> > ewah bitmaps. So Git might gain some optimization if it support roaring\n> > bitmaps. As Roaring library has all the changes it needed to implement\n> > roaring bitmaps in Git, Git can learn to write roaring bitmaps. However,\n> > all the changes are backward-compatible.\n> >\n> > Teach Git to write roaring bitmaps.\n>\n> That is way underexplained.   At least cover what the plans are, so\n> that readers do not have to ask these questions:\n>\n>  * When is the choice of bitmap type is made?  Is it fixed at\n>    repository initialization time and once chosen other kinds cannot\n>    be used?\n>\n>  * Is the bitmap file self describing?  How does a reader know\n>    between ewah and roaring codepaths to use to read a given bitmap\n>    file?  Is there enough room for extending the set of bitmap\n>    formats, or we cannot add other formats easily?\n\nHey Junio,\n\nFirst of all, sorry that the next version is taking so much time to\nland. We have a festival (\"Durga Puja\"; it is the biggest festival for\nBengalis) going on here now. So I am not that active.\n\nI will explain briefly in the next version.\n\n>\n> Do you really need the global variable that holds the bitmap type?\n>\n> Wouldn't it be easier to write code that needs to deal with both\n> types (e.g. in a repository with existing ewah bitmap, you want to\n> do a repack and index the result using the roaring bitmap) if you\n> passed the type through the callchain as a parameter?\n\nI didn't want to go for \"passing the type through the callchain as a\nparameter\" because that would cause changes to every affected function\ndefinition. I found the \"global variable\" approach simpler for this\nreason. Here we have to initialize the type once and the affected\nfunctions will work accordingly.\n\nIf you like the \"callchain\" approach, I have no problem to implement it.\n\n> It may be that the codepath that reads from an existing bitmap file\n> says \"ah, the file given to us seems to be in format X (either EWAH\n> or ROARING or perhaps something else), so let's call bitmap_init(X)\n> to obtain the in-core data structure to deal with that file\".  When\n> that happens, you may probably need to have two cases in the default:\n> arm of this switch statement, i.e. one to diagnose a BUG() to pass\n> an uninitialized bitmap type to the codepath, and the other to\n> diagnose a runtime error() to have read a bitmap file whose format\n> this version of Git does not understand.\n\nOk, understood. Thanks.\n\n> These repetitive patterns makes me wonder if void *bitmap\n> is a good type to be passing around.  Shouldn't it be a struct with\n> its first member being a bitmap_type, and another member being what\n> these functions are passing to the underlying bitmap format specific\n> functions as \"bitmap\"?  E.g.\n>\n>     void bitmap_unset(struct bitmap *bm, uint32_t i)\n>     {\n>         switch (bm->type) {\n>         case EWAH:\n>                 ewah_bitmap_remove(bm->u.ewah, i);\n>                 break;\n>         ...\n\nGood idea! Thanks.\n\n> > +\n> > +enum bitmap_type {\n> > +     INIT_BITMAP_TYPE = 0,\n>\n> \"UNINITIALIZED_BITMAP_TYPE\", probably.\n\nOk.\n\n> > +void *roaring_or_ewah_bitmap_init(void);\n>\n> I would strongly suggest reconsider these names.  What if you later\n> want to add the third variant?  roaring_or_ewah_or_xyzzy_bitmap_init()?\n>\n> Instead just use the most generic name, like \"bitmap_init\", perhaps\n> something along the lines of ...\n>\n>     struct bitmap {\n>         enum bitmap_type type;\n>         union {\n>             struct ewah_bitmap *ewah;\n>             struct roaring_bitmap *roaring;\n>         } u;\n>     };\n>\n>     struct bitmap *bitmap_new(enum bitmap_type type)\n>     {\n>         struct bitmap *bm = xmalloc(sizeof(*bm));\n>\n>         bm->type = type;\n>         switch (bm->type) {\n>         case EWAH:\n>             bm->u.ewah = ewah_new();\n>             break;\n>         case ROARING:\n>             bm->u.roaring = roaring_bitmap_create();\n>             break;\n>         default:\n>             die(_(\"unknown bitmap type %d\"), (int)type);\n>         }\n>         return bm;\n>     }\n\nGot it. It seems a better option than the current one.\n\nThanks )\n"},{"id":"466041","messageId":"CAPOJW5z_ZRChNo8PGBmJu=vvjTL2cYL8oTdVwoDRh-UHt2Dy4w@mail.gmail.com","threadId":"58458","inReplyTo":"CAPOJW5yxRETdVk014gQYFud9_Nrt+OQGSVNQ8Pw2wDEMMFMm1Q@mail.gmail.com","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-10-30T06:35:45Z","receivedAt":"2022-10-30T06:48:38Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"Hello all,\n\nIt has been a month since I didn't get involved in any open source\ncontributions (including Git). This is due to the fact that I was\nfocusing more on mastering theories and also that it was a festive\nmonth. So, I am now resuming my work. There are many things I have to\ncover (including this patch series).\nBut before that I want to ask you a question - As you have noticed\nalready, the Roaring library has a lot of styling issues (Moreover it\nis using C11). So Should I fix all these issues? or Should I make a\nnew library (using Git's compatibility library \"git-compat-util.h\") by\ntaking CRoaring as a reference? The pros are that it would be easier\nto format the bitmap library specific files and it can use Git\ncompatible functions.\n\nI would love to hear your opinions. Thanks :)\n"},{"id":"466071","messageId":"58841dcd-e732-416f-5ab0-fd5a5d8de4c7@github.com","threadId":"58458","inReplyTo":"CAPOJW5z_ZRChNo8PGBmJu=vvjTL2cYL8oTdVwoDRh-UHt2Dy4w@mail.gmail.com","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-10-30T19:46:20Z","receivedAt":"2022-10-30T19:46:25Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 10/30/2022 2:35 AM, Abhradeep Chakraborty wrote:\n> Hello all,\n> \n> It has been a month since I didn't get involved in any open source\n> contributions (including Git). This is due to the fact that I was\n> focusing more on mastering theories and also that it was a festive\n> month. So, I am now resuming my work. There are many things I have to\n> cover (including this patch series).\n> But before that I want to ask you a question - As you have noticed\n> already, the Roaring library has a lot of styling issues (Moreover it\n> is using C11). So Should I fix all these issues? or Should I make a\n> new library (using Git's compatibility library \"git-compat-util.h\") by\n> taking CRoaring as a reference? The pros are that it would be easier\n> to format the bitmap library specific files and it can use Git\n> compatible functions.\n> \n> I would love to hear your opinions. Thanks :)\n\nI HAVE OPINIONS! :D\n\nMostly, there are two things I'd like for you to keep in mind:\n\n1. Using the library as-is is a great way to prototype and dig in on\n   the performance measurement side. Can you construct or clone enough\n   interesting repositories to get a feeling of the effect of the\n   roaring format compared to the EWAH format? If there is no benefit\n   to switching, then we can save everyone a lot of work by marking\n   that as an incorrect road. However, if there is sufficient evidence\n   that it's working well, then we have established a baseline that\n   the full implementation should match (at least, if not do better).\n\n2. Once deciding to do the work, we can think about the reasons to use\n   the existing library over writing our own. The most basic reason is\n   that the library is extensively tested, so we gain all of those\n   benefits. Can we incorporate their test suite into our own? The\n   next main benefit is that we can take any changes from their version\n   into our code with minimal fuss. How often do you think that they\n   have bug fixes or enhancements in the repo? How would those changes\n   translate into our mailing list workflow? If we restyled the library,\n   then we are unlikely to get easy benefits from taking upstream\n   changes, but we could recreate them with manual effort.\n\n3. After carefully considering the benefits/drawbacks of using the\n   existing library, consider the same for writing one from scratch.\n   The most important thing I will say here is that the core idea is\n   rather simple. There may even be ways that we can take advantage\n   of the format and its data structures with the expectations we have\n   in Git repositories that are not always possible for generic\n   databases. We should be able to build a much smaller library that's\n   limited to our needs and customized to our use case. However, we\n   would need to test it carefully, both for correctness and for\n   performance, and that is not a small undertaking.\n\nHopefully this gives you something to chew on. Investigating each of\nthese directions should help you come to a conclusion that you can\nbring to the community as the expert, then we can examine your\nfindings to see if we agree.\n\nRemember that code speaks. If you're willing to build it one way,\nthen that concrete implementation is already worth more than a\nhypothetical alternative in many regards. That can be a starting\npoint to move forward.\n\nThanks,\n-Stolee\n"},{"id":"466093","messageId":"CAPOJW5yEa9MZBPFRiKbaQXw3cv7NM6i4sbVh35CVhZ4JN_q8gw@mail.gmail.com","threadId":"58458","inReplyTo":"58841dcd-e732-416f-5ab0-fd5a5d8de4c7@github.com","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-10-31T14:30:00Z","receivedAt":"2022-10-31T14:30:19Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Mon, Oct 31, 2022 at 1:16 AM Derrick Stolee <derrickstolee@github.com> wrote:\n>\n> On 10/30/2022 2:35 AM, Abhradeep Chakraborty wrote:\n> > Hello all,\n> >\n> > It has been a month since I didn't get involved in any open source\n> > contributions (including Git). This is due to the fact that I was\n> > focusing more on mastering theories and also that it was a festive\n> > month. So, I am now resuming my work. There are many things I have to\n> > cover (including this patch series).\n> > But before that I want to ask you a question - As you have noticed\n> > already, the Roaring library has a lot of styling issues (Moreover it\n> > is using C11). So Should I fix all these issues? or Should I make a\n> > new library (using Git's compatibility library \"git-compat-util.h\") by\n> > taking CRoaring as a reference? The pros are that it would be easier\n> > to format the bitmap library specific files and it can use Git\n> > compatible functions.\n> >\n> > I would love to hear your opinions. Thanks :)\n>\n> I HAVE OPINIONS! :D\n>\n> Mostly, there are two things I'd like for you to keep in mind:\n>\n> 1. Using the library as-is is a great way to prototype and dig in on\n>    the performance measurement side. Can you construct or clone enough\n>    interesting repositories to get a feeling of the effect of the\n>    roaring format compared to the EWAH format? If there is no benefit\n>    to switching, then we can save everyone a lot of work by marking\n>    that as an incorrect road. However, if there is sufficient evidence\n>    that it's working well, then we have established a baseline that\n>    the full implementation should match (at least, if not do better).\n\nGot it. Yeah, I can do it.\n\nNow I am very much clear about how to proceed with it ;-)\nThanks for your reply!!\n"},{"id":"466099","messageId":"xmqqcza8dlkn.fsf@gitster.g","threadId":"58458","inReplyTo":"58841dcd-e732-416f-5ab0-fd5a5d8de4c7@github.com","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-10-31T16:06:32Z","receivedAt":"2022-10-31T16:06:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <derrickstolee@github.com> writes:\n\n> I HAVE OPINIONS! :D\n>\n> Mostly, there are two things I'd like for you to keep in mind:\n\nNicely summarised.\n\nStepping back a bit, we do not care about how the sources to some\npieces of software we depend on, say OpenSSL, match our style guide.\nIt is because we do not even have to see them while working on Git,\nbut also because we do not have to maintain it.\n\nSo a third-option could be to fill pieces missing from the upstream\n(e.g. big endian support) and contribute them back, and after that\ntreat them as just one of the external dependencies, just like we\nhappen to have a copy of sha1dc code for convenience but have an\noption to use the upstream code as a submodule.\n\nAssuming that such a \"they are just one of our external\ndependencies, just like OpenSSL or cURL libraries\" happens, I would\nnot worry too much about C11, as long as use of roaring bitmaps can\nbe made an optional feature that can be disabled at compile time.\nBitmaps are used only for local optimization and never transferred\nacross repositories, so you having only ewah would not prevent you\nfrom talking with other people with both ewah and roaring.\n\n"},{"id":"466104","messageId":"221031.86cza77tvl.gmgdl@evledraar.gmail.com","threadId":"58458","inReplyTo":"xmqqcza8dlkn.fsf@gitster.g","subject":"C99 -> C11 or C17? (was: [PATCH 3/5] roaring: teach Git to write roaring bitmaps)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-10-31T17:51:04Z","receivedAt":"2022-10-31T18:04:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Oct 31 2022, Junio C Hamano wrote:\n\n> Derrick Stolee <derrickstolee@github.com> writes:\n>\n>> I HAVE OPINIONS! :D\n>>\n>> Mostly, there are two things I'd like for you to keep in mind:\n>\n> Nicely summarised.\n>\n> Stepping back a bit, we do not care about how the sources to some\n> pieces of software we depend on, say OpenSSL, match our style guide.\n> It is because we do not even have to see them while working on Git,\n> but also because we do not have to maintain it.\n>\n> So a third-option could be to fill pieces missing from the upstream\n> (e.g. big endian support) and contribute them back, and after that\n> treat them as just one of the external dependencies, just like we\n> happen to have a copy of sha1dc code for convenience but have an\n> option to use the upstream code as a submodule.\n>\n> Assuming that such a \"they are just one of our external\n> dependencies, just like OpenSSL or cURL libraries\" happens, I would\n> not worry too much about C11, as long as use of roaring bitmaps can\n> be made an optional feature that can be disabled at compile time.\n> Bitmaps are used only for local optimization and never transferred\n> across repositories, so you having only ewah would not prevent you\n> from talking with other people with both ewah and roaring.\n\nAs an aside: We might think about just requiring C11 or C17 sooner than\nlater.\n\nFor the longest time we couldn't, because of MSVC, but it now supports\nit.\n\nPer[1] we've now ended up with a bit of an odd scenario, where on MSCV\nwe ask to compile with C11, but everywhere else with C99, even though\n\"everywhere else\" is likely to support at least C11 by now.\n\nThis is because MSVC doesn't and hasn't ever supported C99, they jumped\nstraight from C89 to C11/C17 (I'm not certain it was in one go, but\nthat's my understanding of [1]).\n\nOf course there may be platforms, compilers etc. that have C99 support,\nbut not C11. So we'd need to tread carefully. I haven't e.g. tested on\nthe usual older RHEL versions we tend to care about.\n\n1. 7bc341e21b5 (git-compat-util: add a test balloon for C99 support, 2021-12-01)\n"},{"id":"466120","messageId":"004601d8ed6b$13a2f580$3ae8e080$@nexbridge.com","threadId":"58458","inReplyTo":"221031.86cza77tvl.gmgdl@evledraar.gmail.com","subject":"RE: C99 -> C11 or C17? (was: [PATCH 3/5] roaring: teach Git to write roaring bitmaps)","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2022-10-31T20:55:09Z","receivedAt":"2022-10-31T20:55:46Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On October 31, 2022 1:51 PM, Ævar Arnfjörð Bjarmason wrote:\n>On Mon, Oct 31 2022, Junio C Hamano wrote:\n>\n>> Derrick Stolee <derrickstolee@github.com> writes:\n>>\n>>> I HAVE OPINIONS! :D\n>>>\n>>> Mostly, there are two things I'd like for you to keep in mind:\n>>\n>> Nicely summarised.\n>>\n>> Stepping back a bit, we do not care about how the sources to some\n>> pieces of software we depend on, say OpenSSL, match our style guide.\n>> It is because we do not even have to see them while working on Git,\n>> but also because we do not have to maintain it.\n>>\n>> So a third-option could be to fill pieces missing from the upstream\n>> (e.g. big endian support) and contribute them back, and after that\n>> treat them as just one of the external dependencies, just like we\n>> happen to have a copy of sha1dc code for convenience but have an\n>> option to use the upstream code as a submodule.\n>>\n>> Assuming that such a \"they are just one of our external dependencies,\n>> just like OpenSSL or cURL libraries\" happens, I would not worry too\n>> much about C11, as long as use of roaring bitmaps can be made an\n>> optional feature that can be disabled at compile time.\n>> Bitmaps are used only for local optimization and never transferred\n>> across repositories, so you having only ewah would not prevent you\n>> from talking with other people with both ewah and roaring.\n>\n>As an aside: We might think about just requiring C11 or C17 sooner than later.\n\nAs a request, there is no C11 or C17 on NonStop Itanium, which does not go off support until at least mid-2025. Requiring C11 or C17 will cut git updates off for that platform variant. NonStop x86 does not yet have C17, although it might in future (and I can report when it does - nonetheless, C11 will be required for some supported revisions of the operating system until 2030). Please do not do this.\n\nC99 is the maximum guaranteed version available on all supported NonStop OS versions as of today. Git has thousands of NonStop users who would potentially be impacted and stuck without being able to obtain fixes for CVEs. In my opinion, and for many other Open-Source projects on which I maintain the platform, C11 and C17 do not contain a significant set of constructs that justify moving past C99, but it is obviously the git team's choice. This decision would leave us in the cold and would have to patch C99 code in to make git work.\n\nSincerely,\nRandall\n\n"},{"id":"466179","messageId":"CAPOJW5xvrESGNQSMcVTwPt+fF0t7V-iB6ufjqjqYxn85_xt+bg@mail.gmail.com","threadId":"58458","inReplyTo":"xmqqcza8dlkn.fsf@gitster.g","subject":"Re: [PATCH 3/5] roaring: teach Git to write roaring bitmaps","fromName":"Abhradeep Chakraborty","fromEmail":"chakrabortyabhradeep79@gmail.com","sentAt":"2022-11-01T06:58:47Z","receivedAt":"2022-11-01T06:59:23Z","isPatch":true,"sender":{"key":"chakrabortyabhradeep79@gmail.com","avatar":"https://avatars.githubusercontent.com/u/75240995?v=4"},"body":"On Mon, Oct 31, 2022 at 9:36 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Derrick Stolee <derrickstolee@github.com> writes:\n>\n> > I HAVE OPINIONS! :D\n> >\n> > Mostly, there are two things I'd like for you to keep in mind:\n>\n> Nicely summarised.\n>\n> Stepping back a bit, we do not care about how the sources to some\n> pieces of software we depend on, say OpenSSL, match our style guide.\n> It is because we do not even have to see them while working on Git,\n> but also because we do not have to maintain it.\n>\n> So a third-option could be to fill pieces missing from the upstream\n> (e.g. big endian support) and contribute them back, and after that\n> treat them as just one of the external dependencies, just like we\n> happen to have a copy of sha1dc code for convenience but have an\n> option to use the upstream code as a submodule.\n>\n> Assuming that such a \"they are just one of our external\n> dependencies, just like OpenSSL or cURL libraries\" happens, I would\n> not worry too much about C11, as long as use of roaring bitmaps can\n> be made an optional feature that can be disabled at compile time.\n> Bitmaps are used only for local optimization and never transferred\n> across repositories, so you having only ewah would not prevent you\n> from talking with other people with both ewah and roaring.\n\nSeems a good option to me. Thanks for the info :)\n"}]}