Packages
rocksdb
2.6.2
3.1.2
3.1.1
3.1.0
3.0.0
2.6.2
2.6.1
retired
2.6.0
retired
2.5.0
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.9.0
1.8.0
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.1
1.1.0
1.0.0
0.26.2
0.26.1
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
RocksDB for Erlang
Current section
Files
Jump to
Current section
Files
deps/CRoaring/src/containers/run.c
#include <stdio.h>
#include <stdlib.h>
#include <roaring/containers/run.h>
#include <roaring/memory.h>
#include <roaring/portability.h>
#if CROARING_IS_X64
#ifndef CROARING_COMPILER_SUPPORTS_AVX512
#error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined."
#endif // CROARING_COMPILER_SUPPORTS_AVX512
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#ifdef __cplusplus
extern "C" {
namespace roaring {
namespace internal {
#endif
extern inline uint16_t run_container_minimum(const run_container_t *run);
extern inline uint16_t run_container_maximum(const run_container_t *run);
extern inline int32_t interleavedBinarySearch(const rle16_t *array,
int32_t lenarray, uint16_t ikey);
extern inline bool run_container_contains(const run_container_t *run,
uint16_t pos);
extern inline int run_container_index_equalorlarger(const run_container_t *arr,
uint16_t x);
extern inline bool run_container_is_full(const run_container_t *run);
extern inline bool run_container_nonzero_cardinality(const run_container_t *rc);
extern inline int32_t run_container_serialized_size_in_bytes(int32_t num_runs);
extern inline run_container_t *run_container_create_range(uint32_t start,
uint32_t stop);
extern inline int run_container_cardinality(const run_container_t *run);
bool run_container_add(run_container_t *run, uint16_t pos) {
int32_t index = interleavedBinarySearch(run->runs, run->n_runs, pos);
if (index >= 0) return false; // already there
index = -index - 2; // points to preceding value, possibly -1
if (index >= 0) { // possible match
int32_t offset = pos - run->runs[index].value;
int32_t le = run->runs[index].length;
if (offset <= le) return false; // already there
if (offset == le + 1) {
// we may need to fuse
if (index + 1 < run->n_runs) {
if (run->runs[index + 1].value == pos + 1) {
// indeed fusion is needed
run->runs[index].length = run->runs[index + 1].value +
run->runs[index + 1].length -
run->runs[index].value;
recoverRoomAtIndex(run, (uint16_t)(index + 1));
return true;
}
}
run->runs[index].length++;
return true;
}
if (index + 1 < run->n_runs) {
// we may need to fuse
if (run->runs[index + 1].value == pos + 1) {
// indeed fusion is needed
run->runs[index + 1].value = pos;
run->runs[index + 1].length = run->runs[index + 1].length + 1;
return true;
}
}
}
if (index == -1) {
// we may need to extend the first run
if (0 < run->n_runs) {
if (run->runs[0].value == pos + 1) {
run->runs[0].length++;
run->runs[0].value--;
return true;
}
}
}
makeRoomAtIndex(run, (uint16_t)(index + 1));
run->runs[index + 1].value = pos;
run->runs[index + 1].length = 0;
return true;
}
/* Create a new run container. Return NULL in case of failure. */
run_container_t *run_container_create_given_capacity(int32_t size) {
run_container_t *run;
/* Allocate the run container itself. */
if ((run = (run_container_t *)roaring_malloc(sizeof(run_container_t))) ==
NULL) {
return NULL;
}
if (size <= 0) { // we don't want to rely on malloc(0)
run->runs = NULL;
} else if ((run->runs = (rle16_t *)roaring_malloc(sizeof(rle16_t) *
size)) == NULL) {
roaring_free(run);
return NULL;
}
run->capacity = size;
run->n_runs = 0;
return run;
}
int run_container_shrink_to_fit(run_container_t *src) {
if (src->n_runs == src->capacity) return 0; // nothing to do
int savings = src->capacity - src->n_runs;
src->capacity = src->n_runs;
rle16_t *oldruns = src->runs;
src->runs =
(rle16_t *)roaring_realloc(oldruns, src->capacity * sizeof(rle16_t));
if (src->runs == NULL) roaring_free(oldruns); // should never happen?
return savings;
}
/* Create a new run container. Return NULL in case of failure. */
run_container_t *run_container_create(void) {
return run_container_create_given_capacity(RUN_DEFAULT_INIT_SIZE);
}
CROARING_ALLOW_UNALIGNED
run_container_t *run_container_clone(const run_container_t *src) {
run_container_t *run = run_container_create_given_capacity(src->capacity);
if (run == NULL) return NULL;
run->capacity = src->capacity;
run->n_runs = src->n_runs;
memcpy(run->runs, src->runs, src->n_runs * sizeof(rle16_t));
return run;
}
void run_container_offset(const run_container_t *c, container_t **loc,
container_t **hic, uint16_t offset) {
run_container_t *lo = NULL, *hi = NULL;
bool split;
unsigned int lo_cap, hi_cap;
int top, pivot;
top = (1 << 16) - offset;
pivot = run_container_index_equalorlarger(c, top);
// pivot is the index of the first run that is >= top or -1 if no such run
if (pivot >= 0) {
split = c->runs[pivot].value < top;
lo_cap = pivot + (split ? 1 : 0);
hi_cap = c->n_runs - pivot;
} else {
// here pivot < 0
split = false;
lo_cap = c->n_runs;
hi_cap = 0;
}
if (loc && lo_cap) {
lo = run_container_create_given_capacity(lo_cap);
memcpy(lo->runs, c->runs, lo_cap * sizeof(rle16_t));
lo->n_runs = lo_cap;
for (unsigned int i = 0; i < lo_cap; ++i) {
lo->runs[i].value += offset;
}
*loc = (container_t *)lo;
}
if (hic && hi_cap) {
hi = run_container_create_given_capacity(hi_cap);
memcpy(hi->runs, c->runs + pivot, hi_cap * sizeof(rle16_t));
hi->n_runs = hi_cap;
for (unsigned int i = 0; i < hi_cap; ++i) {
hi->runs[i].value += offset;
}
*hic = (container_t *)hi;
}
// Fix the split.
if (split) {
if (lo != NULL) {
// Add the missing run to 'lo', exhausting length.
lo->runs[lo->n_runs - 1].length =
(1 << 16) - lo->runs[lo->n_runs - 1].value - 1;
}
if (hi != NULL) {
// Fix the first run in 'hi'.
hi->runs[0].length -= UINT16_MAX - hi->runs[0].value + 1;
hi->runs[0].value = 0;
}
}
}
/* Free memory. */
void run_container_free(run_container_t *run) {
if (run == NULL) return;
roaring_free(run->runs);
roaring_free(run);
}
void run_container_grow(run_container_t *run, int32_t min, bool copy) {
int32_t newCapacity = (run->capacity == 0) ? RUN_DEFAULT_INIT_SIZE
: run->capacity < 64 ? run->capacity * 2
: run->capacity < 1024 ? run->capacity * 3 / 2
: run->capacity * 5 / 4;
if (newCapacity < min) newCapacity = min;
run->capacity = newCapacity;
assert(run->capacity >= min);
if (copy) {
rle16_t *oldruns = run->runs;
run->runs = (rle16_t *)roaring_realloc(oldruns,
run->capacity * sizeof(rle16_t));
if (run->runs == NULL) roaring_free(oldruns);
} else {
roaring_free(run->runs);
run->runs = (rle16_t *)roaring_malloc(run->capacity * sizeof(rle16_t));
}
// We may have run->runs == NULL.
}
/* copy one container into another */
void run_container_copy(const run_container_t *src, run_container_t *dst) {
const int32_t n_runs = src->n_runs;
if (src->n_runs > dst->capacity) {
run_container_grow(dst, n_runs, false);
}
dst->n_runs = n_runs;
memcpy(dst->runs, src->runs, sizeof(rle16_t) * n_runs);
}
/* Compute the union of `src_1' and `src_2' and write the result to `dst'
* It is assumed that `dst' is distinct from both `src_1' and `src_2'. */
void run_container_union(const run_container_t *src_1,
const run_container_t *src_2, run_container_t *dst) {
// TODO: this could be a lot more efficient
// we start out with inexpensive checks
const bool if1 = run_container_is_full(src_1);
const bool if2 = run_container_is_full(src_2);
if (if1 || if2) {
if (if1) {
run_container_copy(src_1, dst);
return;
}
if (if2) {
run_container_copy(src_2, dst);
return;
}
}
const int32_t neededcapacity = src_1->n_runs + src_2->n_runs;
if (dst->capacity < neededcapacity)
run_container_grow(dst, neededcapacity, false);
dst->n_runs = 0;
int32_t rlepos = 0;
int32_t xrlepos = 0;
rle16_t previousrle;
if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) {
previousrle = run_container_append_first(dst, src_1->runs[rlepos]);
rlepos++;
} else {
previousrle = run_container_append_first(dst, src_2->runs[xrlepos]);
xrlepos++;
}
while ((xrlepos < src_2->n_runs) && (rlepos < src_1->n_runs)) {
rle16_t newrl;
if (src_1->runs[rlepos].value <= src_2->runs[xrlepos].value) {
newrl = src_1->runs[rlepos];
rlepos++;
} else {
newrl = src_2->runs[xrlepos];
xrlepos++;
}
run_container_append(dst, newrl, &previousrle);
}
while (xrlepos < src_2->n_runs) {
run_container_append(dst, src_2->runs[xrlepos], &previousrle);
xrlepos++;
}
while (rlepos < src_1->n_runs) {
run_container_append(dst, src_1->runs[rlepos], &previousrle);
rlepos++;
}
}
/* Compute the union of `src_1' and `src_2' and write the result to `src_1'
*/
void run_container_union_inplace(run_container_t *src_1,
const run_container_t *src_2) {
// TODO: this could be a lot more efficient
// we start out with inexpensive checks
const bool if1 = run_container_is_full(src_1);
const bool if2 = run_container_is_full(src_2);
if (if1 || if2) {
if (if1) {
return;
}
if (if2) {
run_container_copy(src_2, src_1);
return;
}
}
// we move the data to the end of the current array
const int32_t maxoutput = src_1->n_runs + src_2->n_runs;
const int32_t neededcapacity = maxoutput + src_1->n_runs;
if (src_1->capacity < neededcapacity)
run_container_grow(src_1, neededcapacity, true);
memmove(src_1->runs + maxoutput, src_1->runs,
src_1->n_runs * sizeof(rle16_t));
rle16_t *inputsrc1 = src_1->runs + maxoutput;
const int32_t input1nruns = src_1->n_runs;
src_1->n_runs = 0;
int32_t rlepos = 0;
int32_t xrlepos = 0;
rle16_t previousrle;
if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) {
previousrle = run_container_append_first(src_1, inputsrc1[rlepos]);
rlepos++;
} else {
previousrle = run_container_append_first(src_1, src_2->runs[xrlepos]);
xrlepos++;
}
while ((xrlepos < src_2->n_runs) && (rlepos < input1nruns)) {
rle16_t newrl;
if (inputsrc1[rlepos].value <= src_2->runs[xrlepos].value) {
newrl = inputsrc1[rlepos];
rlepos++;
} else {
newrl = src_2->runs[xrlepos];
xrlepos++;
}
run_container_append(src_1, newrl, &previousrle);
}
while (xrlepos < src_2->n_runs) {
run_container_append(src_1, src_2->runs[xrlepos], &previousrle);
xrlepos++;
}
while (rlepos < input1nruns) {
run_container_append(src_1, inputsrc1[rlepos], &previousrle);
rlepos++;
}
}
/* Compute the symmetric difference of `src_1' and `src_2' and write the result
* to `dst'
* It is assumed that `dst' is distinct from both `src_1' and `src_2'. */
void run_container_xor(const run_container_t *src_1,
const run_container_t *src_2, run_container_t *dst) {
// don't bother to convert xor with full range into negation
// since negation is implemented similarly
const int32_t neededcapacity = src_1->n_runs + src_2->n_runs;
if (dst->capacity < neededcapacity)
run_container_grow(dst, neededcapacity, false);
int32_t pos1 = 0;
int32_t pos2 = 0;
dst->n_runs = 0;
while ((pos1 < src_1->n_runs) && (pos2 < src_2->n_runs)) {
if (src_1->runs[pos1].value <= src_2->runs[pos2].value) {
run_container_smart_append_exclusive(dst, src_1->runs[pos1].value,
src_1->runs[pos1].length);
pos1++;
} else {
run_container_smart_append_exclusive(dst, src_2->runs[pos2].value,
src_2->runs[pos2].length);
pos2++;
}
}
while (pos1 < src_1->n_runs) {
run_container_smart_append_exclusive(dst, src_1->runs[pos1].value,
src_1->runs[pos1].length);
pos1++;
}
while (pos2 < src_2->n_runs) {
run_container_smart_append_exclusive(dst, src_2->runs[pos2].value,
src_2->runs[pos2].length);
pos2++;
}
}
/* Compute the intersection of src_1 and src_2 and write the result to
* dst. It is assumed that dst is distinct from both src_1 and src_2. */
void run_container_intersection(const run_container_t *src_1,
const run_container_t *src_2,
run_container_t *dst) {
const bool if1 = run_container_is_full(src_1);
const bool if2 = run_container_is_full(src_2);
if (if1 || if2) {
if (if1) {
run_container_copy(src_2, dst);
return;
}
if (if2) {
run_container_copy(src_1, dst);
return;
}
}
// TODO: this could be a lot more efficient, could use SIMD optimizations
const int32_t neededcapacity = src_1->n_runs + src_2->n_runs;
if (dst->capacity < neededcapacity)
run_container_grow(dst, neededcapacity, false);
dst->n_runs = 0;
int32_t rlepos = 0;
int32_t xrlepos = 0;
int32_t start = src_1->runs[rlepos].value;
int32_t end = start + src_1->runs[rlepos].length + 1;
int32_t xstart = src_2->runs[xrlepos].value;
int32_t xend = xstart + src_2->runs[xrlepos].length + 1;
while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) {
if (end <= xstart) {
++rlepos;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
} else if (xend <= start) {
++xrlepos;
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
} else { // they overlap
const int32_t lateststart = start > xstart ? start : xstart;
int32_t earliestend;
if (end == xend) { // improbable
earliestend = end;
rlepos++;
xrlepos++;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
} else if (end < xend) {
earliestend = end;
rlepos++;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
} else { // end > xend
earliestend = xend;
xrlepos++;
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
}
dst->runs[dst->n_runs].value = (uint16_t)lateststart;
dst->runs[dst->n_runs].length =
(uint16_t)(earliestend - lateststart - 1);
dst->n_runs++;
}
}
}
/* Compute the size of the intersection of src_1 and src_2 . */
int run_container_intersection_cardinality(const run_container_t *src_1,
const run_container_t *src_2) {
const bool if1 = run_container_is_full(src_1);
const bool if2 = run_container_is_full(src_2);
if (if1 || if2) {
if (if1) {
return run_container_cardinality(src_2);
}
if (if2) {
return run_container_cardinality(src_1);
}
}
int answer = 0;
int32_t rlepos = 0;
int32_t xrlepos = 0;
int32_t start = src_1->runs[rlepos].value;
int32_t end = start + src_1->runs[rlepos].length + 1;
int32_t xstart = src_2->runs[xrlepos].value;
int32_t xend = xstart + src_2->runs[xrlepos].length + 1;
while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) {
if (end <= xstart) {
++rlepos;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
} else if (xend <= start) {
++xrlepos;
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
} else { // they overlap
const int32_t lateststart = start > xstart ? start : xstart;
int32_t earliestend;
if (end == xend) { // improbable
earliestend = end;
rlepos++;
xrlepos++;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
} else if (end < xend) {
earliestend = end;
rlepos++;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
} else { // end > xend
earliestend = xend;
xrlepos++;
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
}
answer += earliestend - lateststart;
}
}
return answer;
}
bool run_container_intersect(const run_container_t *src_1,
const run_container_t *src_2) {
const bool if1 = run_container_is_full(src_1);
const bool if2 = run_container_is_full(src_2);
if (if1 || if2) {
if (if1) {
return !run_container_empty(src_2);
}
if (if2) {
return !run_container_empty(src_1);
}
}
int32_t rlepos = 0;
int32_t xrlepos = 0;
int32_t start = src_1->runs[rlepos].value;
int32_t end = start + src_1->runs[rlepos].length + 1;
int32_t xstart = src_2->runs[xrlepos].value;
int32_t xend = xstart + src_2->runs[xrlepos].length + 1;
while ((rlepos < src_1->n_runs) && (xrlepos < src_2->n_runs)) {
if (end <= xstart) {
++rlepos;
if (rlepos < src_1->n_runs) {
start = src_1->runs[rlepos].value;
end = start + src_1->runs[rlepos].length + 1;
}
} else if (xend <= start) {
++xrlepos;
if (xrlepos < src_2->n_runs) {
xstart = src_2->runs[xrlepos].value;
xend = xstart + src_2->runs[xrlepos].length + 1;
}
} else { // they overlap
return true;
}
}
return false;
}
/* Compute the difference of src_1 and src_2 and write the result to
* dst. It is assumed that dst is distinct from both src_1 and src_2. */
void run_container_andnot(const run_container_t *src_1,
const run_container_t *src_2, run_container_t *dst) {
// following Java implementation as of June 2016
if (dst->capacity < src_1->n_runs + src_2->n_runs)
run_container_grow(dst, src_1->n_runs + src_2->n_runs, false);
dst->n_runs = 0;
int rlepos1 = 0;
int rlepos2 = 0;
int32_t start = src_1->runs[rlepos1].value;
int32_t end = start + src_1->runs[rlepos1].length + 1;
int32_t start2 = src_2->runs[rlepos2].value;
int32_t end2 = start2 + src_2->runs[rlepos2].length + 1;
while ((rlepos1 < src_1->n_runs) && (rlepos2 < src_2->n_runs)) {
if (end <= start2) {
// output the first run
dst->runs[dst->n_runs++] =
CROARING_MAKE_RLE16(start, end - start - 1);
rlepos1++;
if (rlepos1 < src_1->n_runs) {
start = src_1->runs[rlepos1].value;
end = start + src_1->runs[rlepos1].length + 1;
}
} else if (end2 <= start) {
// exit the second run
rlepos2++;
if (rlepos2 < src_2->n_runs) {
start2 = src_2->runs[rlepos2].value;
end2 = start2 + src_2->runs[rlepos2].length + 1;
}
} else {
if (start < start2) {
dst->runs[dst->n_runs++] =
CROARING_MAKE_RLE16(start, start2 - start - 1);
}
if (end2 < end) {
start = end2;
} else {
rlepos1++;
if (rlepos1 < src_1->n_runs) {
start = src_1->runs[rlepos1].value;
end = start + src_1->runs[rlepos1].length + 1;
}
}
}
}
if (rlepos1 < src_1->n_runs) {
dst->runs[dst->n_runs++] = CROARING_MAKE_RLE16(start, end - start - 1);
rlepos1++;
if (rlepos1 < src_1->n_runs) {
memcpy(dst->runs + dst->n_runs, src_1->runs + rlepos1,
sizeof(rle16_t) * (src_1->n_runs - rlepos1));
dst->n_runs += src_1->n_runs - rlepos1;
}
}
}
/*
* Print this container using printf (useful for debugging).
*/
void run_container_printf(const run_container_t *cont) {
for (int i = 0; i < cont->n_runs; ++i) {
uint16_t run_start = cont->runs[i].value;
uint16_t le = cont->runs[i].length;
printf("[%d,%d]", run_start, run_start + le);
}
}
/*
* Print this container using printf as a comma-separated list of 32-bit
* integers starting at base.
*/
void run_container_printf_as_uint32_array(const run_container_t *cont,
uint32_t base) {
if (cont->n_runs == 0) return;
{
uint32_t run_start = base + cont->runs[0].value;
uint16_t le = cont->runs[0].length;
printf("%u", run_start);
for (uint32_t j = 1; j <= le; ++j) printf(",%u", run_start + j);
}
for (int32_t i = 1; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
for (uint32_t j = 0; j <= le; ++j) printf(",%u", run_start + j);
}
}
/*
* Validate the container. Returns true if valid.
*/
bool run_container_validate(const run_container_t *run, const char **reason) {
if (run->n_runs < 0) {
*reason = "negative run count";
return false;
}
if (run->capacity < 0) {
*reason = "negative run capacity";
return false;
}
if (run->capacity < run->n_runs) {
*reason = "capacity less than run count";
return false;
}
if (run->n_runs == 0) {
*reason = "zero run count";
return false;
}
if (run->runs == NULL) {
*reason = "NULL runs";
return false;
}
// Use uint32_t to avoid overflow issues on ranges that contain UINT16_MAX.
uint32_t last_end = 0;
for (int i = 0; i < run->n_runs; ++i) {
uint32_t start = run->runs[i].value;
uint32_t end = start + run->runs[i].length + 1;
if (end <= start) {
*reason = "run start + length overflow";
return false;
}
if (end > (1 << 16)) {
*reason = "run start + length too large";
return false;
}
if (start < last_end) {
*reason = "run start less than last end";
return false;
}
if (start == last_end && last_end != 0) {
*reason = "run start equal to last end, should have combined";
return false;
}
last_end = end;
}
return true;
}
int32_t run_container_write(const run_container_t *container, char *buf) {
uint16_t cast_16 = container->n_runs;
memcpy(buf, &cast_16, sizeof(uint16_t));
memcpy(buf + sizeof(uint16_t), container->runs,
container->n_runs * sizeof(rle16_t));
return run_container_size_in_bytes(container);
}
int32_t run_container_read(int32_t cardinality, run_container_t *container,
const char *buf) {
(void)cardinality;
uint16_t cast_16;
memcpy(&cast_16, buf, sizeof(uint16_t));
container->n_runs = cast_16;
if (container->n_runs > container->capacity)
run_container_grow(container, container->n_runs, false);
if (container->n_runs > 0) {
memcpy(container->runs, buf + sizeof(uint16_t),
container->n_runs * sizeof(rle16_t));
}
return run_container_size_in_bytes(container);
}
bool run_container_iterate(const run_container_t *cont, uint32_t base,
roaring_iterator iterator, void *ptr) {
for (int i = 0; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
for (int j = 0; j <= le; ++j)
if (!iterator(run_start + j, ptr)) return false;
}
return true;
}
bool run_container_iterate64(const run_container_t *cont, uint32_t base,
roaring_iterator64 iterator, uint64_t high_bits,
void *ptr) {
for (int i = 0; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
for (int j = 0; j <= le; ++j)
if (!iterator(high_bits | (uint64_t)(run_start + j), ptr))
return false;
}
return true;
}
bool run_container_is_subset(const run_container_t *container1,
const run_container_t *container2) {
int i1 = 0, i2 = 0;
while (i1 < container1->n_runs && i2 < container2->n_runs) {
int start1 = container1->runs[i1].value;
int stop1 = start1 + container1->runs[i1].length;
int start2 = container2->runs[i2].value;
int stop2 = start2 + container2->runs[i2].length;
if (start1 < start2) {
return false;
} else { // start1 >= start2
if (stop1 < stop2) {
i1++;
} else if (stop1 == stop2) {
i1++;
i2++;
} else { // stop1 > stop2
i2++;
}
}
}
if (i1 == container1->n_runs) {
return true;
} else {
return false;
}
}
// TODO: write smart_append_exclusive version to match the overloaded 1 param
// Java version (or is it even used?)
// follows the Java implementation closely
// length is the rle-value. Ie, run [10,12) uses a length value 1.
void run_container_smart_append_exclusive(run_container_t *src,
const uint16_t start,
const uint16_t length) {
int old_end;
rle16_t *last_run = src->n_runs ? src->runs + (src->n_runs - 1) : NULL;
rle16_t *appended_last_run = src->runs + src->n_runs;
if (!src->n_runs ||
(start > (old_end = last_run->value + last_run->length + 1))) {
*appended_last_run = CROARING_MAKE_RLE16(start, length);
src->n_runs++;
return;
}
if (old_end == start) {
// we merge
last_run->length += (length + 1);
return;
}
int new_end = start + length + 1;
if (start == last_run->value) {
// wipe out previous
if (new_end < old_end) {
*last_run = CROARING_MAKE_RLE16(new_end, old_end - new_end - 1);
return;
} else if (new_end > old_end) {
*last_run = CROARING_MAKE_RLE16(old_end, new_end - old_end - 1);
return;
} else {
src->n_runs--;
return;
}
}
last_run->length = start - last_run->value - 1;
if (new_end < old_end) {
*appended_last_run =
CROARING_MAKE_RLE16(new_end, old_end - new_end - 1);
src->n_runs++;
} else if (new_end > old_end) {
*appended_last_run =
CROARING_MAKE_RLE16(old_end, new_end - old_end - 1);
src->n_runs++;
}
}
bool run_container_select(const run_container_t *container,
uint32_t *start_rank, uint32_t rank,
uint32_t *element) {
for (int i = 0; i < container->n_runs; i++) {
uint16_t length = container->runs[i].length;
if (rank <= *start_rank + length) {
uint16_t value = container->runs[i].value;
*element = value + rank - (*start_rank);
return true;
} else
*start_rank += length + 1;
}
return false;
}
int run_container_rank(const run_container_t *container, uint16_t x) {
int sum = 0;
uint32_t x32 = x;
for (int i = 0; i < container->n_runs; i++) {
uint32_t startpoint = container->runs[i].value;
uint32_t length = container->runs[i].length;
uint32_t endpoint = length + startpoint;
if (x <= endpoint) {
if (x < startpoint) break;
return sum + (x32 - startpoint) + 1;
} else {
sum += length + 1;
}
}
return sum;
}
uint32_t run_container_rank_many(const run_container_t *container,
uint64_t start_rank, const uint32_t *begin,
const uint32_t *end, uint64_t *ans) {
const uint16_t high = (uint16_t)((*begin) >> 16);
const uint32_t *iter = begin;
int sum = 0;
int i = 0;
for (; iter != end; iter++) {
uint32_t x = *iter;
uint16_t xhigh = (uint16_t)(x >> 16);
if (xhigh != high) return iter - begin; // stop at next container
uint32_t x32 = x & 0xFFFF;
while (i < container->n_runs) {
uint32_t startpoint = container->runs[i].value;
uint32_t length = container->runs[i].length;
uint32_t endpoint = length + startpoint;
if (x32 <= endpoint) {
if (x32 < startpoint) {
*(ans++) = start_rank + sum;
} else {
*(ans++) = start_rank + sum + (x32 - startpoint) + 1;
}
break;
} else {
sum += length + 1;
i++;
}
}
if (i >= container->n_runs) *(ans++) = start_rank + sum;
}
return iter - begin;
}
int run_container_get_index(const run_container_t *container, uint16_t x) {
if (run_container_contains(container, x)) {
int sum = 0;
uint32_t x32 = x;
for (int i = 0; i < container->n_runs; i++) {
uint32_t startpoint = container->runs[i].value;
uint32_t length = container->runs[i].length;
uint32_t endpoint = length + startpoint;
if (x <= endpoint) {
if (x < startpoint) break;
return sum + (x32 - startpoint);
} else {
sum += length + 1;
}
}
return sum - 1;
} else {
return -1;
}
}
#if defined(CROARING_IS_X64) && CROARING_COMPILER_SUPPORTS_AVX512
CROARING_TARGET_AVX512
CROARING_ALLOW_UNALIGNED
/* Get the cardinality of `run'. Requires an actual computation. */
static inline int _avx512_run_container_cardinality(
const run_container_t *run) {
const int32_t n_runs = run->n_runs;
const rle16_t *runs = run->runs;
int32_t k = 0;
const int32_t step512 = sizeof(__m512i) / sizeof(rle16_t);
__m512i total = _mm512_setzero_si512();
for (; k + step512 <= n_runs; k += step512) {
__m512i ymm1 = _mm512_loadu_si512((const __m512i *)(runs + k));
__m512i justlengths = _mm512_srli_epi32(ymm1, 16);
total = _mm512_add_epi32(total, justlengths);
}
if (k < n_runs) {
__m512i ymm1 = _mm512_maskz_loadu_epi32((1 << (n_runs - k)) - 1,
(const __m512i *)(runs + k));
__m512i justlengths = _mm512_srli_epi32(ymm1, 16);
total = _mm512_add_epi32(total, justlengths);
}
return _mm512_reduce_add_epi32(total) + n_runs;
}
CROARING_UNTARGET_AVX512
CROARING_TARGET_AVX2
CROARING_ALLOW_UNALIGNED
/* Get the cardinality of `run'. Requires an actual computation. */
static inline int _avx2_run_container_cardinality(const run_container_t *run) {
const int32_t n_runs = run->n_runs;
const rle16_t *runs = run->runs;
/* by initializing with n_runs, we omit counting the +1 for each pair. */
int sum = n_runs;
int32_t k = 0;
const int32_t step = sizeof(__m256i) / sizeof(rle16_t);
if (n_runs > step) {
__m256i total = _mm256_setzero_si256();
for (; k + step <= n_runs; k += step) {
__m256i ymm1 = _mm256_lddqu_si256((const __m256i *)(runs + k));
__m256i justlengths = _mm256_srli_epi32(ymm1, 16);
total = _mm256_add_epi32(total, justlengths);
}
// a store might be faster than extract?
uint32_t buffer[sizeof(__m256i) / sizeof(rle16_t)];
_mm256_storeu_si256((__m256i *)buffer, total);
sum += (buffer[0] + buffer[1]) + (buffer[2] + buffer[3]) +
(buffer[4] + buffer[5]) + (buffer[6] + buffer[7]);
}
for (; k < n_runs; ++k) {
sum += runs[k].length;
}
return sum;
}
CROARING_ALLOW_UNALIGNED
int _avx2_run_container_to_uint32_array(void *vout, const run_container_t *cont,
uint32_t base) {
int outpos = 0;
uint32_t *out = (uint32_t *)vout;
for (int i = 0; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
if (le < 8) {
for (int j = 0; j <= le; ++j) {
uint32_t val = run_start + j;
memcpy(out + outpos, &val,
sizeof(uint32_t)); // should be compiled as a MOV on x64
outpos++;
}
} else {
int j = 0;
__m256i run_start_v = _mm256_set1_epi32(run_start);
// [8,8,8,8....]
__m256i inc = _mm256_set1_epi32(8);
// used for generate sequence:
// [0, 1, 2, 3...], [8, 9, 10,...]
__m256i delta = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
for (j = 0; j + 8 <= le; j += 8) {
__m256i val_v = _mm256_add_epi32(run_start_v, delta);
_mm256_storeu_si256((__m256i *)(out + outpos), val_v);
delta = _mm256_add_epi32(inc, delta);
outpos += 8;
}
for (; j <= le; ++j) {
uint32_t val = run_start + j;
memcpy(out + outpos, &val,
sizeof(uint32_t)); // should be compiled as a MOV on x64
outpos++;
}
}
}
return outpos;
}
CROARING_UNTARGET_AVX2
/* Get the cardinality of `run'. Requires an actual computation. */
static inline int _scalar_run_container_cardinality(
const run_container_t *run) {
const int32_t n_runs = run->n_runs;
const rle16_t *runs = run->runs;
/* by initializing with n_runs, we omit counting the +1 for each pair. */
int sum = n_runs;
for (int k = 0; k < n_runs; ++k) {
sum += runs[k].length;
}
return sum;
}
int run_container_cardinality(const run_container_t *run) {
// Empirically AVX-512 is not always faster than AVX2
#define CROARING_ENABLE_AVX512_RUN_CONTAINER_CARDINALITY 0
#if CROARING_COMPILER_SUPPORTS_AVX512 && \
CROARING_ENABLE_AVX512_RUN_CONTAINER_CARDINALITY
if (croaring_hardware_support() & ROARING_SUPPORTS_AVX512) {
return _avx512_run_container_cardinality(run);
} else
#endif
if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) {
return _avx2_run_container_cardinality(run);
} else {
return _scalar_run_container_cardinality(run);
}
}
int _scalar_run_container_to_uint32_array(void *vout,
const run_container_t *cont,
uint32_t base) {
int outpos = 0;
uint32_t *out = (uint32_t *)vout;
for (int i = 0; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
for (int j = 0; j <= le; ++j) {
uint32_t val = run_start + j;
memcpy(out + outpos, &val,
sizeof(uint32_t)); // should be compiled as a MOV on x64
outpos++;
}
}
return outpos;
}
int run_container_to_uint32_array(void *vout, const run_container_t *cont,
uint32_t base) {
if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) {
return _avx2_run_container_to_uint32_array(vout, cont, base);
} else {
return _scalar_run_container_to_uint32_array(vout, cont, base);
}
}
#else
/* Get the cardinality of `run'. Requires an actual computation. */
CROARING_ALLOW_UNALIGNED
int run_container_cardinality(const run_container_t *run) {
const int32_t n_runs = run->n_runs;
const rle16_t *runs = run->runs;
/* by initializing with n_runs, we omit counting the +1 for each pair. */
int sum = n_runs;
for (int k = 0; k < n_runs; ++k) {
sum += runs[k].length;
}
return sum;
}
CROARING_ALLOW_UNALIGNED
int run_container_to_uint32_array(void *vout, const run_container_t *cont,
uint32_t base) {
int outpos = 0;
uint32_t *out = (uint32_t *)vout;
for (int i = 0; i < cont->n_runs; ++i) {
uint32_t run_start = base + cont->runs[i].value;
uint16_t le = cont->runs[i].length;
for (int j = 0; j <= le; ++j) {
uint32_t val = run_start + j;
memcpy(out + outpos, &val,
sizeof(uint32_t)); // should be compiled as a MOV on x64
outpos++;
}
}
return outpos;
}
#endif
#ifdef __cplusplus
}
}
} // extern "C" { namespace roaring { namespace internal {
#endif
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif