1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
|
#include <stdio.h>
#include <roaring/bitset_util.h>
#include <roaring/containers/containers.h>
#include <roaring/containers/convert.h>
#include <roaring/containers/perfparameters.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
#ifdef __cplusplus
extern "C" { namespace roaring { namespace internal {
#endif
// file contains grubby stuff that must know impl. details of all container
// types.
bitset_container_t *bitset_container_from_array(const array_container_t *ac) {
bitset_container_t *ans = bitset_container_create();
int limit = array_container_cardinality(ac);
for (int i = 0; i < limit; ++i) bitset_container_set(ans, ac->array[i]);
return ans;
}
bitset_container_t *bitset_container_from_run(const run_container_t *arr) {
int card = run_container_cardinality(arr);
bitset_container_t *answer = bitset_container_create();
for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) {
rle16_t vl = arr->runs[rlepos];
bitset_set_lenrange(answer->words, vl.value, vl.length);
}
answer->cardinality = card;
return answer;
}
array_container_t *array_container_from_run(const run_container_t *arr) {
array_container_t *answer =
array_container_create_given_capacity(run_container_cardinality(arr));
answer->cardinality = 0;
for (int rlepos = 0; rlepos < arr->n_runs; ++rlepos) {
int run_start = arr->runs[rlepos].value;
int run_end = run_start + arr->runs[rlepos].length;
for (int run_value = run_start; run_value <= run_end; ++run_value) {
answer->array[answer->cardinality++] = (uint16_t)run_value;
}
}
return answer;
}
array_container_t *array_container_from_bitset(const bitset_container_t *bits) {
array_container_t *result =
array_container_create_given_capacity(bits->cardinality);
result->cardinality = bits->cardinality;
#if CROARING_IS_X64
#if CROARING_COMPILER_SUPPORTS_AVX512
if( croaring_hardware_support() & ROARING_SUPPORTS_AVX512 ) {
bitset_extract_setbits_avx512_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS,
result->array, bits->cardinality , 0);
} else
#endif
{
// sse version ends up being slower here
// (bitset_extract_setbits_sse_uint16)
// because of the sparsity of the data
bitset_extract_setbits_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS,
result->array, 0);
}
#else
// If the system is not x64, then we have no accelerated function.
bitset_extract_setbits_uint16(bits->words, BITSET_CONTAINER_SIZE_IN_WORDS,
result->array, 0);
#endif
return result;
}
/* assumes that container has adequate space. Run from [s,e] (inclusive) */
static void add_run(run_container_t *rc, int s, int e) {
rc->runs[rc->n_runs].value = s;
rc->runs[rc->n_runs].length = e - s;
rc->n_runs++;
}
run_container_t *run_container_from_array(const array_container_t *c) {
int32_t n_runs = array_container_number_of_runs(c);
run_container_t *answer = run_container_create_given_capacity(n_runs);
int prev = -2;
int run_start = -1;
int32_t card = c->cardinality;
if (card == 0) return answer;
for (int i = 0; i < card; ++i) {
const uint16_t cur_val = c->array[i];
if (cur_val != prev + 1) {
// new run starts; flush old one, if any
if (run_start != -1) add_run(answer, run_start, prev);
run_start = cur_val;
}
prev = c->array[i];
}
// now prev is the last seen value
add_run(answer, run_start, prev);
// assert(run_container_cardinality(answer) == c->cardinality);
return answer;
}
/**
* Convert the runcontainer to either a Bitmap or an Array Container, depending
* on the cardinality. Frees the container.
* Allocates and returns new container, which caller is responsible for freeing.
* It does not free the run container.
*/
container_t *convert_to_bitset_or_array_container(
run_container_t *rc, int32_t card,
uint8_t *resulttype
){
if (card <= DEFAULT_MAX_SIZE) {
array_container_t *answer = array_container_create_given_capacity(card);
answer->cardinality = 0;
for (int rlepos = 0; rlepos < rc->n_runs; ++rlepos) {
uint16_t run_start = rc->runs[rlepos].value;
uint16_t run_end = run_start + rc->runs[rlepos].length;
for (uint16_t run_value = run_start; run_value < run_end;
++run_value) {
answer->array[answer->cardinality++] = run_value;
}
answer->array[answer->cardinality++] = run_end;
}
assert(card == answer->cardinality);
*resulttype = ARRAY_CONTAINER_TYPE;
//run_container_free(r);
return answer;
}
bitset_container_t *answer = bitset_container_create();
for (int rlepos = 0; rlepos < rc->n_runs; ++rlepos) {
uint16_t run_start = rc->runs[rlepos].value;
bitset_set_lenrange(answer->words, run_start, rc->runs[rlepos].length);
}
answer->cardinality = card;
*resulttype = BITSET_CONTAINER_TYPE;
//run_container_free(r);
return answer;
}
/* Converts a run container to either an array or a bitset, IF it saves space.
*/
/* If a conversion occurs, the caller is responsible to free the original
* container and
* he becomes responsible to free the new one. */
container_t *convert_run_to_efficient_container(
run_container_t *c,
uint8_t *typecode_after
){
int32_t size_as_run_container =
run_container_serialized_size_in_bytes(c->n_runs);
int32_t size_as_bitset_container =
bitset_container_serialized_size_in_bytes();
int32_t card = run_container_cardinality(c);
int32_t size_as_array_container =
array_container_serialized_size_in_bytes(card);
int32_t min_size_non_run =
size_as_bitset_container < size_as_array_container
? size_as_bitset_container
: size_as_array_container;
if (size_as_run_container <= min_size_non_run) { // no conversion
*typecode_after = RUN_CONTAINER_TYPE;
return c;
}
if (card <= DEFAULT_MAX_SIZE) {
// to array
array_container_t *answer = array_container_create_given_capacity(card);
answer->cardinality = 0;
for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) {
int run_start = c->runs[rlepos].value;
int run_end = run_start + c->runs[rlepos].length;
for (int run_value = run_start; run_value <= run_end; ++run_value) {
answer->array[answer->cardinality++] = (uint16_t)run_value;
}
}
*typecode_after = ARRAY_CONTAINER_TYPE;
return answer;
}
// else to bitset
bitset_container_t *answer = bitset_container_create();
for (int rlepos = 0; rlepos < c->n_runs; ++rlepos) {
int start = c->runs[rlepos].value;
int end = start + c->runs[rlepos].length;
bitset_set_range(answer->words, start, end + 1);
}
answer->cardinality = card;
*typecode_after = BITSET_CONTAINER_TYPE;
return answer;
}
// like convert_run_to_efficient_container but frees the old result if needed
container_t *convert_run_to_efficient_container_and_free(
run_container_t *c,
uint8_t *typecode_after
){
container_t *answer = convert_run_to_efficient_container(c, typecode_after);
if (answer != c) run_container_free(c);
return answer;
}
/* once converted, the original container is disposed here, rather than
in roaring_array
*/
// TODO: split into run- array- and bitset- subfunctions for sanity;
// a few function calls won't really matter.
container_t *convert_run_optimize(
container_t *c, uint8_t typecode_original,
uint8_t *typecode_after
){
if (typecode_original == RUN_CONTAINER_TYPE) {
container_t *newc = convert_run_to_efficient_container(
CAST_run(c), typecode_after);
if (newc != c) {
container_free(c, typecode_original);
}
return newc;
} else if (typecode_original == ARRAY_CONTAINER_TYPE) {
// it might need to be converted to a run container.
array_container_t *c_qua_array = CAST_array(c);
int32_t n_runs = array_container_number_of_runs(c_qua_array);
int32_t size_as_run_container =
run_container_serialized_size_in_bytes(n_runs);
int32_t card = array_container_cardinality(c_qua_array);
int32_t size_as_array_container =
array_container_serialized_size_in_bytes(card);
if (size_as_run_container >= size_as_array_container) {
*typecode_after = ARRAY_CONTAINER_TYPE;
return c;
}
// else convert array to run container
run_container_t *answer = run_container_create_given_capacity(n_runs);
int prev = -2;
int run_start = -1;
assert(card > 0);
for (int i = 0; i < card; ++i) {
uint16_t cur_val = c_qua_array->array[i];
if (cur_val != prev + 1) {
// new run starts; flush old one, if any
if (run_start != -1) add_run(answer, run_start, prev);
run_start = cur_val;
}
prev = c_qua_array->array[i];
}
assert(run_start >= 0);
// now prev is the last seen value
add_run(answer, run_start, prev);
*typecode_after = RUN_CONTAINER_TYPE;
array_container_free(c_qua_array);
return answer;
} else if (typecode_original ==
BITSET_CONTAINER_TYPE) { // run conversions on bitset
// does bitset need conversion to run?
bitset_container_t *c_qua_bitset = CAST_bitset(c);
int32_t n_runs = bitset_container_number_of_runs(c_qua_bitset);
int32_t size_as_run_container =
run_container_serialized_size_in_bytes(n_runs);
int32_t size_as_bitset_container =
bitset_container_serialized_size_in_bytes();
if (size_as_bitset_container <= size_as_run_container) {
// no conversion needed.
*typecode_after = BITSET_CONTAINER_TYPE;
return c;
}
// bitset to runcontainer (ported from Java RunContainer(
// BitmapContainer bc, int nbrRuns))
assert(n_runs > 0); // no empty bitmaps
run_container_t *answer = run_container_create_given_capacity(n_runs);
int long_ctr = 0;
uint64_t cur_word = c_qua_bitset->words[0];
while (true) {
while (cur_word == UINT64_C(0) &&
long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1)
cur_word = c_qua_bitset->words[++long_ctr];
if (cur_word == UINT64_C(0)) {
bitset_container_free(c_qua_bitset);
*typecode_after = RUN_CONTAINER_TYPE;
return answer;
}
int local_run_start = roaring_trailing_zeroes(cur_word);
int run_start = local_run_start + 64 * long_ctr;
uint64_t cur_word_with_1s = cur_word | (cur_word - 1);
int run_end = 0;
while (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF) &&
long_ctr < BITSET_CONTAINER_SIZE_IN_WORDS - 1)
cur_word_with_1s = c_qua_bitset->words[++long_ctr];
if (cur_word_with_1s == UINT64_C(0xFFFFFFFFFFFFFFFF)) {
run_end = 64 + long_ctr * 64; // exclusive, I guess
add_run(answer, run_start, run_end - 1);
bitset_container_free(c_qua_bitset);
*typecode_after = RUN_CONTAINER_TYPE;
return answer;
}
int local_run_end = roaring_trailing_zeroes(~cur_word_with_1s);
run_end = local_run_end + long_ctr * 64;
add_run(answer, run_start, run_end - 1);
cur_word = cur_word_with_1s & (cur_word_with_1s + 1);
}
return answer;
} else {
assert(false);
roaring_unreachable;
return NULL;
}
}
container_t *container_from_run_range(
const run_container_t *run,
uint32_t min, uint32_t max, uint8_t *typecode_after
){
// We expect most of the time to end up with a bitset container
bitset_container_t *bitset = bitset_container_create();
*typecode_after = BITSET_CONTAINER_TYPE;
int32_t union_cardinality = 0;
for (int32_t i = 0; i < run->n_runs; ++i) {
uint32_t rle_min = run->runs[i].value;
uint32_t rle_max = rle_min + run->runs[i].length;
bitset_set_lenrange(bitset->words, rle_min, rle_max - rle_min);
union_cardinality += run->runs[i].length + 1;
}
union_cardinality += max - min + 1;
union_cardinality -= bitset_lenrange_cardinality(bitset->words, min, max-min);
bitset_set_lenrange(bitset->words, min, max - min);
bitset->cardinality = union_cardinality;
if(bitset->cardinality <= DEFAULT_MAX_SIZE) {
// we need to convert to an array container
array_container_t * array = array_container_from_bitset(bitset);
*typecode_after = ARRAY_CONTAINER_TYPE;
bitset_container_free(bitset);
return array;
}
return bitset;
}
#ifdef __cplusplus
} } } // extern "C" { namespace roaring { namespace internal {
#endif
|