import as C++
This commit is contained in:
commit
b6ccffe809
13 changed files with 4062 additions and 0 deletions
197
.clang-format
Normal file
197
.clang-format
Normal 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
25
.clang-tidy
Normal 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
5
.envrc
Normal 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
120
.gitignore
vendored
Normal 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
21
LICENSE
Normal 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
90
README.md
Normal 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
205
examples/main.c
Normal 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
51
flake.nix
Normal 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
72
include/popcount.h
Normal 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
0
include/sparsemap.h
Normal file
BIN
main
Executable file
BIN
main
Executable file
Binary file not shown.
1021
src/sparsemap.c
Normal file
1021
src/sparsemap.c
Normal file
File diff suppressed because it is too large
Load diff
2255
tests/munit.c
Normal file
2255
tests/munit.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue