import as C++

This commit is contained in:
Gregory Burd 2024-04-02 20:41:55 -04:00
commit b6ccffe809
13 changed files with 4062 additions and 0 deletions

197
.clang-format Normal file
View file

@ -0,0 +1,197 @@
# Basic .clang-format
---
BasedOnStyle: WebKit
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: WebKit
BreakBeforeTernaryOperators: false
# TODO: BreakStringLiterals can cause very strange formatting so turn it off?
BreakStringLiterals: false
# Prefer:
# some_var = function(arg1,
# arg2)
# over:
# some_var =
# function(arg1, arg2)
PenaltyBreakAssignment: 100
# Prefer:
# some_long_function(arg1, arg2
# arg3)
# over:
# some_long_function(
# arg1, arg2, arg3)
PenaltyBreakBeforeFirstCallParameter: 100
CompactNamespaces: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros:
- ARB_ARRFOREACH
- ARB_ARRFOREACH_REVWCOND
- ARB_ARRFOREACH_REVERSE
- ARB_FOREACH
- ARB_FOREACH_FROM
- ARB_FOREACH_SAFE
- ARB_FOREACH_REVERSE
- ARB_FOREACH_REVERSE_FROM
- ARB_FOREACH_REVERSE_SAFE
- BIT_FOREACH_ISCLR
- BIT_FOREACH_ISSET
- CPU_FOREACH
- CPU_FOREACH_ISCLR
- CPU_FOREACH_ISSET
- FOREACH_THREAD_IN_PROC
- FOREACH_PROC_IN_SYSTEM
- FOREACH_PRISON_CHILD
- FOREACH_PRISON_DESCENDANT
- FOREACH_PRISON_DESCENDANT_LOCKED
- FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL
- MNT_VNODE_FOREACH_ALL
- MNT_VNODE_FOREACH_ACTIVE
- RB_FOREACH
- RB_FOREACH_FROM
- RB_FOREACH_SAFE
- RB_FOREACH_REVERSE
- RB_FOREACH_REVERSE_FROM
- RB_FOREACH_REVERSE_SAFE
- SLIST_FOREACH
- SLIST_FOREACH_FROM
- SLIST_FOREACH_FROM_SAFE
- SLIST_FOREACH_SAFE
- SLIST_FOREACH_PREVPTR
- SPLAY_FOREACH
- LIST_FOREACH
- LIST_FOREACH_FROM
- LIST_FOREACH_FROM_SAFE
- LIST_FOREACH_SAFE
- STAILQ_FOREACH
- STAILQ_FOREACH_FROM
- STAILQ_FOREACH_FROM_SAFE
- STAILQ_FOREACH_SAFE
- TAILQ_FOREACH
- TAILQ_FOREACH_FROM
- TAILQ_FOREACH_FROM_SAFE
- TAILQ_FOREACH_REVERSE
- TAILQ_FOREACH_REVERSE_FROM
- TAILQ_FOREACH_REVERSE_FROM_SAFE
- TAILQ_FOREACH_REVERSE_SAFE
- TAILQ_FOREACH_SAFE
- VM_MAP_ENTRY_FOREACH
- VM_PAGE_DUMP_FOREACH
IndentCaseLabels: false
IndentPPDirectives: None
Language: Cpp
NamespaceIndentation: None
PointerAlignment: Right
ContinuationIndentWidth: 4
IndentWidth: 4
TabWidth: 4
ColumnLimit: 160
UseTab: Never
SpaceAfterCStyleCast: false
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^\"opt_.*\.h\"'
Priority: 1
SortPriority: 10
- Regex: '^<sys/cdefs\.h>'
Priority: 2
SortPriority: 20
- Regex: '^<sys/types\.h>'
Priority: 2
SortPriority: 21
- Regex: '^<sys/param\.h>'
Priority: 2
SortPriority: 22
- Regex: '^<sys/systm\.h>'
Priority: 2
SortPriority: 23
- Regex: '^<sys.*/'
Priority: 2
SortPriority: 24
- Regex: '^<vm/vm\.h>'
Priority: 3
SortPriority: 30
- Regex: '^<vm/'
Priority: 3
SortPriority: 31
- Regex: '^<machine/'
Priority: 4
SortPriority: 40
- Regex: '^<(x86|amd64|i386|xen)/'
Priority: 5
SortPriority: 50
- Regex: '^<dev/'
Priority: 6
SortPriority: 60
- Regex: '^<net.*/'
Priority: 7
SortPriority: 70
- Regex: '^<protocols/'
Priority: 7
SortPriority: 71
- Regex: '^<(fs|nfs(|client|server)|ufs)/'
Priority: 8
SortPriority: 80
- Regex: '^<[^/].*\.h'
Priority: 9
SortPriority: 90
- Regex: '^\".*\.h\"'
Priority: 10
SortPriority: 100
# LLVM's header include ordering style is almost the exact opposite of ours.
# Unfortunately, they have hard-coded their preferences into clang-format.
# Clobbering this regular expression to avoid matching prevents non-system
# headers from being forcibly moved to the top of the include list.
# http://llvm.org/docs/CodingStandards.html#include-style
IncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING'
SortIncludes: true
KeepEmptyLinesAtTheStartOfBlocks: true
TypenameMacros:
- ARB_ELMTYPE
- ARB_HEAD
- ARB8_HEAD
- ARB16_HEAD
- ARB32_HEAD
- ARB_ENTRY
- ARB8_ENTRY
- ARB16_ENTRY
- ARB32_ENTRY
- LIST_CLASS_ENTRY
- LIST_CLASS_HEAD
- LIST_ENTRY
- LIST_HEAD
- QUEUE_TYPEOF
- RB_ENTRY
- RB_HEAD
- SLIST_CLASS_HEAD
- SLIST_CLASS_ENTRY
- SLIST_HEAD
- SLIST_ENTRY
- SMR_POINTER
- SPLAY_ENTRY
- SPLAY_HEAD
- STAILQ_CLASS_ENTRY
- STAILQ_CLASS_HEAD
- STAILQ_ENTRY
- STAILQ_HEAD
- TAILQ_CLASS_ENTRY
- TAILQ_CLASS_HEAD
- TAILQ_ENTRY
- TAILQ_HEAD

25
.clang-tidy Normal file
View file

@ -0,0 +1,25 @@
---
Checks: >
bugprone-*,
clang-analyzer-*,
google-*,
misc-*,
modernize-*,
performance-*,
portability-*,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-not-null-terminated-result,
-bugprone-reserved-identifier,
-bugprone-sizeof-expression,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
-clang-analyzer-security.insecureAPI.strcpy,
-google-readability-todo,
-misc-unused-parameters,
-misc-no-recursion,
-performance-no-int-to-ptr,
-bugprone-assignment-in-if-condition,
...

5
.envrc Normal file
View file

@ -0,0 +1,5 @@
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
fi
watch_file devShell.nix shell.nix flake.nix
use flake || use nix

120
.gitignore vendored Normal file
View file

@ -0,0 +1,120 @@
libsparesemap.a
libsparesemap.so
**/*.o
tests/test
examples/main
.cache
hints.txt
tmp/
git.diff
.direnv
.vscode/.ropeproject
.vscode/ipch
.codelite/
.cmaketools.json
*.tags
*.dll
build/
cmake-build*
.cmake_dirty
Makefile
Testing/
compile_commands.json
.tern*
*.iml
*.dat
*.fsm
*.db
# Created by https://www.gitignore.io/api/jetbrains
# Edit at https://www.gitignore.io/?templates=jetbrains
### JetBrains ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/**/sonarlint/
# SonarQube Plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator/
# End of https://www.gitignore.io/api/jetbrains

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Christoph Rupp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

90
README.md Normal file
View file

@ -0,0 +1,90 @@
`sparsemap` is a sparse, compressed bitmap. In best case, it can store 2048
bits in just 8 bytes. In worst case, it stores the 2048 bits uncompressed and
requires additional 8 bytes of overhead.
The "best" case happens when large consecutive sequences of the bits are
either set ("1") or not set ("0"). If your numbers are consecutive 64bit
integers then sparsemap can compress up to 16kb in just 8 bytes.
## How does it work?
On the lowest level, bits are stored in BitVectors (a uint32_t or uint64_t).
Each BitVector has an additional descriptor (2 bits). All descriptors are
stored in a single Word which is prepended to the BitVectors. (The descriptor
Word and the BitVectors have the same size.) The descriptor of a BitVector
specifies whether the BitVector consists only of set bits ("1"), unset
bits ("0") or has a mixed payload. In the first and second case the
BitVector is not stored.
An example shows a sequence of 4 x 16 bits (here, each BitVector and the
Descriptor word has 16 bits):
Descriptor:
00 00 00 00 11 00 11 10
^^ ^^ ^^ ^^-- BitVector #0 - #3 are "0000000000000000"
^^-- BitVector #4 is "1111111111111111"
^^-- BitVector #5 is "0000000000000000"
^^-- BitVector #7 is "1111111111111111"
^^-- BitVector #7 is "0110010101111001"
Since the first 7 BitVectors are either all "1" or "0" they are not stored.
The actual memory sequence looks like this:
0000000011001110 0110010101111001
Instead of storing 8 Words (16 bytes), we only store 2 Words (2 bytes): one
for the Descriptor, one for last BitVector #7.
Since such a construct (it's called a MiniMap) has a limited capacity, another
structure is created on top of it, a `Sparsemap`. The Sparsemap stores a list
of MiniMaps, and for each MiniMap it stores the absolute address. I.e. if
the user sets bit 0 and bit 10000, and the MiniMap capacity is 2048, the
Sparsemap creates two MiniMaps. The first starts at offset 0, the second starts
at offset 8192.
# Usage instructions
The file `main.cc` has example code. Here is a small excerpt:
// we need a buffer to store the SparseMap
unsigned char buffer[1024];
sparsemap::SparseMap<uint32_t, uint64_t> sm;
sm.create(buffer, sizeof(buffer));
// after initialization, the used size is just 4 bytes (sizeof(uint32_t))
assert(sm.get_size() == sizeof(uint32_t));
// set the first bit
sm.set(0, true);
// check that the first bit was set
assert(sm.is_set(0) == true);
// unset the first bit
assert(sm.is_set(1) == false);
// check that the first bit is now no longer set
sm.set(0, false);
## Final words
This bitmap implementation has very efficient compression when using on long
sequences of set (or unset) bits. I.e. with a word size of 64bit, and a
payload of consecutive numbers without gaps, the payload of 2048 x
sizeof(uint64_t) = 16kb can be stored in just 8 bytes!
However, if the sequence is not consecutive and has gaps, it's possible that
the compression is completely inefficient, and the size basically is identical
to an uncompressed bitvector (even higher because a few bytes are required for
metadata). In such cases, other compression schemes are more efficient (i.e.
http://lemire.me/blog/archives/2008/08/20/the-mythical-bitmap-index/).
This library was originally created for hamsterdb [http://hamsterdb.com] in
order to compress the key indices. For several technical reasons this turned
out to be impossible, though. (If you're curious then feel free to drop
me a mail.) I'm releasing it as open source, hoping that others can make good
use of it.

205
examples/main.c Normal file
View file

@ -0,0 +1,205 @@
#include <sparsemap.h>
#include <assert.h>
#include <stdio.h>
using namespace sparsemap;
//
// this code serves as a sample but also as a unittest.
//
int main() {
int size = 4;
setbuf(stdout, 0); // disable printf() buffering
printf("Please wait a moment.");
#if 1
uint8_t buffer[1024];
uint8_t buffer2[1024];
SparseMap<uint32_t, uint64_t> sm;
sm.create(buffer, sizeof(buffer));
assert(sm.get_size() == size);
sm.set(0, true);
assert(sm.get_size() == size + 4 + 8 + 8);
assert(sm.is_set(0) == true);
assert(sm.get_size() == size + 4 + 8 + 8);
assert(sm.is_set(1) == false);
sm.set(0, false);
assert(sm.get_size() == size);
sm.clear();
sm.set(64, true);
assert(sm.is_set(64) == true);
assert(sm.get_size() == size + 4 + 8 + 8);
sm.clear();
printf(".");
// set [0..100000]
for (int i = 0; i < 100000; i++) {
assert(sm.is_set(i) == false);
sm.set(i, true);
if (i > 5) {
for (int j = i - 5; j <= i; j++)
assert(sm.is_set(j) == true);
}
assert(sm.is_set(i) == true);
}
printf(".");
for (int i = 0; i < 100000; i++)
assert(sm.is_set(i) == true);
// unset [0..10000]
for (int i = 0; i < 10000; i++) {
assert(sm.is_set(i) == true);
sm.set(i, false);
assert(sm.is_set(i) == false);
}
for (int i = 0; i < 10000; i++)
assert(sm.is_set(i) == false);
sm.clear();
printf(".");
// set [10000..0]
for (int i = 10000; i >= 0; i--) {
assert(sm.is_set(i) == false);
sm.set(i, true);
assert(sm.is_set(i) == true);
}
for (int i = 10000; i >= 0; i--)
assert(sm.is_set(i) == true);
printf(".");
// open and compare
SparseMap<uint32_t, uint64_t> sm2;
sm2.open(buffer, sizeof(buffer));
for (int i = 0; i < 10000; i++)
assert(sm2.is_set(i) == sm.is_set(i));
// unset [10000..0]
for (int i = 10000; i >= 0; i--) {
assert(sm.is_set(i) == true);
sm.set(i, false);
assert(sm.is_set(i) == false);
}
for (int i = 10000; i >= 0; i--)
assert(sm.is_set(i) == false);
printf(".");
sm.clear();
sm.set(0, true);
sm.set(2048 * 2 + 1, true);
assert(sm.is_set(0) == true);
assert(sm.is_set(2048 * 2 + 0) == false);
assert(sm.is_set(2048 * 2 + 1) == true);
assert(sm.is_set(2048 * 2 + 2) == false);
sm.set(2048, true);
assert(sm.is_set(0) == true);
assert(sm.is_set(2047) == false);
assert(sm.is_set(2048) == true);
assert(sm.is_set(2049) == false);
assert(sm.is_set(2048 * 2 + 2) == false);
assert(sm.is_set(2048 * 2 + 0) == false);
assert(sm.is_set(2048 * 2 + 1) == true);
assert(sm.is_set(2048 * 2 + 2) == false);
sm.clear();
printf(".");
for (int i = 0; i < 100000; i++)
sm.set(i, true);
for (int i = 0; i < 100000; i++)
assert(sm.select(i) == (unsigned)i);
sm.clear();
printf(".");
for (int i = 1; i < 513; i++)
sm.set(i, true);
for (int i = 1; i < 513; i++)
assert(sm.select(i - 1) == (unsigned)i);
sm.clear();
printf(".");
for (int i = 0; i < 8; i++)
sm.set(i * 10, true);
for (int i = 0; i < 8; i++)
assert(sm.select(i) == (unsigned)i * 10);
// split and move, aligned to MiniMap capacity
sm2.create(buffer2, sizeof(buffer2));
sm.clear();
for (int i = 0; i < 2048 * 2; i++)
sm.set(i, true);
sm.split(2048, &sm2);
for (int i = 0; i < 2048; i++) {
assert(sm.is_set(i) == true);
assert(sm2.is_set(i) == false);
}
for (int i = 2048; i < 2048 * 2; i++) {
assert(sm.is_set(i) == false);
assert(sm2.is_set(i) == true);
}
printf(".");
// split and move, aligned to BitVector capacity
sm2.create(buffer2, sizeof(buffer2));
sm.clear();
for (int i = 0; i < 2048 * 3; i++)
sm.set(i, true);
sm.split(64, &sm2);
for (int i = 0; i < 64; i++) {
assert(sm.is_set(i) == true);
assert(sm2.is_set(i) == false);
}
for (int i = 64; i < 2048 * 3; i++) {
assert(sm.is_set(i) == false);
assert(sm2.is_set(i) == true);
}
printf("ok\n");
#else
//
// This code was used to create the lookup table for
// sparsemap::MiniMap<>::calc_vector_size()
//
printf(" ");
for (unsigned int ch = 0; ch <= 0xff; ch++) {
if (ch > 0 && ch % 16 == 0)
printf("\n ");
/*
// check if value is invalid (contains 2#01)
if ((ch & (0x3 << 0)) >> 0 == 1
|| (ch & (0x3 << 2)) >> 2 == 1
|| (ch & (0x3 << 4)) >> 4 == 1
|| (ch & (0x3 << 6)) >> 6 == 1) {
//printf("%d: -1\n", (int)ch);
printf(" -1,");
continue;
}
*/
// count all occurrences of 2#10
int size = 0;
if ((ch & (0x3 << 0)) >> 0 == 2)
size++;
if ((ch & (0x3 << 2)) >> 2 == 2)
size++;
if ((ch & (0x3 << 4)) >> 4 == 2)
size++;
if ((ch & (0x3 << 6)) >> 6 == 2)
size++;
//printf("%u: %d\n", (unsigned int)ch, size);
printf(" %d,", size);
}
#endif
}

51
flake.nix Normal file
View file

@ -0,0 +1,51 @@
{
description = "A Concurrent Skip List library for key/value pairs.";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs =
{ self
, nixpkgs
, flake-utils
, ...
}:
flake-utils.lib.eachDefaultSystem (system:
let
# pkgs = nixpkgs.legacyPackages.${system};
pkgs = import nixpkgs {
inherit system;
config = { allowUnfree = true; };
};
in
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
autoconf
bashInteractive
clang-tools
ed
gdb
graphviz-nox
meson
python311Packages.rbtools
];
};
buildInputs = with pkgs; [
glibc
];
nativeBuildInputs = with pkgs.buildPackages; [
act
binutils
coreutils
gcc
gettext
libtool
m4
make
perl
pkg-config
ripgrep
];
});
}

72
include/popcount.h Normal file
View file

@ -0,0 +1,72 @@
/*
*
* from https://github.com/efficient/rankselect/popcount.h
*
* licensed under Apache 2
*/
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef _FASTRANK_POPCOUNT_H_
#define _FASTRANK_POPCOUNT_H_
#include <sys/types.h>
#include <stdio.h>
#define L8 0x0101010101010101ULL // Every lowest 8th bit set: 00000001...
#define G2 0xAAAAAAAAAAAAAAAAULL // Every highest 2nd bit: 101010...
#define G4 0x3333333333333333ULL // 00110011 ... used to group the sum of 4 bits.
#define G8 0x0F0F0F0F0F0F0F0FULL
#define H8 0x8080808080808080ULL
#define L9 0x0040201008040201ULL
#define H9 (L9 << 8)
#define L16 0x0001000100010001ULL
#define H16 0x8000800080008000ULL
#define ONES_STEP_4 ( 0x1111111111111111ULL )
#define ONES_STEP_8 ( 0x0101010101010101ULL )
#define ONES_STEP_9 ( 1ULL << 0 | 1ULL << 9 | 1ULL << 18 | 1ULL << 27 | 1ULL << 36 | 1ULL << 45 | 1ULL << 54 )
#define ONES_STEP_16 ( 1ULL << 0 | 1ULL << 16 | 1ULL << 32 | 1ULL << 48 )
#define MSBS_STEP_4 ( 0x8ULL * ONES_STEP_4 )
#define MSBS_STEP_8 ( 0x80ULL * ONES_STEP_8 )
#define MSBS_STEP_9 ( 0x100ULL * ONES_STEP_9 )
#define MSBS_STEP_16 ( 0x8000ULL * ONES_STEP_16 )
#define INCR_STEP_8 ( 0x80ULL << 56 | 0x40ULL << 48 | 0x20ULL << 40 | 0x10ULL << 32 | 0x8ULL << 24 | 0x4ULL << 16 | 0x2ULL << 8 | 0x1 )
#define ONES_STEP_32 ( 0x0000000100000001ULL )
#define MSBS_STEP_32 ( 0x8000000080000000ULL )
#define COMPARE_STEP_8(x,y) ( ( ( ( ( (x) | MSBS_STEP_8 ) - ( (y) & ~MSBS_STEP_8 ) ) ^ (x) ^ ~(y) ) & MSBS_STEP_8 ) >> 7 )
#define LEQ_STEP_8(x,y) ( ( ( ( ( (y) | MSBS_STEP_8 ) - ( (x) & ~MSBS_STEP_8 ) ) ^ (x) ^ (y) ) & MSBS_STEP_8 ) >> 7 )
#define UCOMPARE_STEP_9(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_9 ) - ( (y) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_9 ) >> 8 )
#define UCOMPARE_STEP_16(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_16 ) - ( (y) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_16 ) >> 15 )
#define ULEQ_STEP_9(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_9 ) - ( (x) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_9 ) >> 8 )
#define ULEQ_STEP_16(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_16 ) - ( (x) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_16 ) >> 15 )
#define ZCOMPARE_STEP_8(x) ( ( ( x | ( ( x | MSBS_STEP_8 ) - ONES_STEP_8 ) ) & MSBS_STEP_8 ) >> 7 )
// Population count of a 64 bit integer in SWAR (SIMD within a register) style
// From Sebastiano Vigna, "Broadword Implementation of Rank/Select Queries"
// http://sux.dsi.unimi.it/paper.pdf p4
// This variant uses multiplication for the last summation instead of
// continuing the shift/mask/addition chain.
inline int suxpopcount(uint64_t x) {
// Step 1: 00 - 00 = 0; 01 - 00 = 01; 10 - 01 = 01; 11 - 01 = 10;
x = x - ((x & G2) >> 1);
// step 2: add 2 groups of 2.
x = (x & G4) + ((x >> 2) & G4);
// 2 groups of 4.
x = (x + (x >> 4)) & G8;
// Using a multiply to collect the 8 groups of 8 together.
x = x * L8 >> 56;
return x;
}
// Default to using the GCC builtin popcount. On architectures
// with -march popcnt, this compiles to a single popcnt instruction.
#ifndef popcount
# define popcount __builtin_popcountll
#else
# define popcount suxpopcount
#endif
#endif /* _FASTRANK_POPCOUNT_H_ */

0
include/sparsemap.h Normal file
View file

BIN
main Executable file

Binary file not shown.

1021
src/sparsemap.c Normal file

File diff suppressed because it is too large Load diff

2255
tests/munit.c Normal file

File diff suppressed because it is too large Load diff