/src/croaring/src/containers/array.c
Line | Count | Source |
1 | | /* |
2 | | * array.c |
3 | | * |
4 | | */ |
5 | | |
6 | | #include <assert.h> |
7 | | #include <stdio.h> |
8 | | #include <stdlib.h> |
9 | | |
10 | | #include <roaring/containers/array.h> |
11 | | #include <roaring/memory.h> |
12 | | |
13 | | #if CROARING_IS_X64 |
14 | | #ifndef CROARING_COMPILER_SUPPORTS_AVX512 |
15 | | #error "CROARING_COMPILER_SUPPORTS_AVX512 needs to be defined." |
16 | | #endif // CROARING_COMPILER_SUPPORTS_AVX512 |
17 | | #endif |
18 | | |
19 | | #ifdef __cplusplus |
20 | | extern "C" { |
21 | | namespace roaring { |
22 | | namespace internal { |
23 | | #endif |
24 | | |
25 | | extern inline uint16_t array_container_minimum(const array_container_t *arr); |
26 | | extern inline uint16_t array_container_maximum(const array_container_t *arr); |
27 | | extern inline int array_container_index_equalorlarger( |
28 | | const array_container_t *arr, uint16_t x); |
29 | | |
30 | | extern inline int array_container_rank(const array_container_t *arr, |
31 | | uint16_t x); |
32 | | extern inline uint32_t array_container_rank_many(const array_container_t *arr, |
33 | | uint64_t start_rank, |
34 | | const uint32_t *begin, |
35 | | const uint32_t *end, |
36 | | uint64_t *ans); |
37 | | extern inline int array_container_get_index(const array_container_t *arr, |
38 | | uint16_t x); |
39 | | extern inline bool array_container_contains(const array_container_t *arr, |
40 | | uint16_t pos); |
41 | | extern inline int array_container_cardinality(const array_container_t *array); |
42 | | extern inline bool array_container_nonzero_cardinality( |
43 | | const array_container_t *array); |
44 | | extern inline int32_t array_container_serialized_size_in_bytes(int32_t card); |
45 | | extern inline bool array_container_empty(const array_container_t *array); |
46 | | extern inline bool array_container_full(const array_container_t *array); |
47 | | |
48 | | /* Create a new array with capacity size. Return NULL in case of failure. */ |
49 | 149k | array_container_t *array_container_create_given_capacity(int32_t size) { |
50 | 149k | array_container_t *container; |
51 | | |
52 | 149k | if ((container = (array_container_t *)roaring_malloc( |
53 | 149k | sizeof(array_container_t))) == NULL) { |
54 | 0 | return NULL; |
55 | 0 | } |
56 | | |
57 | 149k | if (size <= 0) { // we don't want to rely on malloc(0) |
58 | 88.4k | container->array = NULL; |
59 | 88.4k | } else if ((container->array = (uint16_t *)roaring_malloc(sizeof(uint16_t) * |
60 | 61.5k | size)) == NULL) { |
61 | 0 | roaring_free(container); |
62 | 0 | return NULL; |
63 | 0 | } |
64 | | |
65 | 149k | container->capacity = size; |
66 | 149k | container->cardinality = 0; |
67 | | |
68 | 149k | return container; |
69 | 149k | } |
70 | | |
71 | | /* Create a new array. Return NULL in case of failure. */ |
72 | 28.9k | array_container_t *array_container_create(void) { |
73 | 28.9k | return array_container_create_given_capacity(ARRAY_DEFAULT_INIT_SIZE); |
74 | 28.9k | } |
75 | | |
76 | | /* Create a new array containing all values in [min,max). */ |
77 | 124 | array_container_t *array_container_create_range(uint32_t min, uint32_t max) { |
78 | 124 | array_container_t *answer = |
79 | 124 | array_container_create_given_capacity(max - min + 1); |
80 | 124 | if (answer == NULL) return answer; |
81 | 124 | answer->cardinality = 0; |
82 | 248 | for (uint32_t k = min; k < max; k++) { |
83 | 124 | answer->array[answer->cardinality++] = k; |
84 | 124 | } |
85 | 124 | return answer; |
86 | 124 | } |
87 | | |
88 | | /* Duplicate container */ |
89 | | CROARING_ALLOW_UNALIGNED |
90 | 23.5k | array_container_t *array_container_clone(const array_container_t *src) { |
91 | 23.5k | array_container_t *newcontainer = |
92 | 23.5k | array_container_create_given_capacity(src->capacity); |
93 | 23.5k | if (newcontainer == NULL) return NULL; |
94 | | |
95 | 23.5k | newcontainer->cardinality = src->cardinality; |
96 | | |
97 | 23.5k | memcpy(newcontainer->array, src->array, |
98 | 23.5k | src->cardinality * sizeof(uint16_t)); |
99 | | |
100 | 23.5k | return newcontainer; |
101 | 23.5k | } |
102 | | |
103 | | void array_container_offset(const array_container_t *c, container_t **loc, |
104 | 0 | container_t **hic, uint16_t offset) { |
105 | 0 | array_container_t *lo = NULL, *hi = NULL; |
106 | 0 | int top, lo_cap, hi_cap; |
107 | |
|
108 | 0 | top = (1 << 16) - offset; |
109 | |
|
110 | 0 | const uint16_t *array = c->array; |
111 | 0 | lo_cap = count_less(array, c->cardinality, top); |
112 | 0 | if (loc && lo_cap) { |
113 | 0 | lo = array_container_create_given_capacity(lo_cap); |
114 | 0 | uint16_t *out = lo->array; |
115 | 0 | for (int i = 0; i < lo_cap; ++i) { |
116 | 0 | out[i] = array[i] + offset; |
117 | 0 | } |
118 | 0 | lo->cardinality = lo_cap; |
119 | 0 | *loc = (container_t *)lo; |
120 | 0 | } |
121 | |
|
122 | 0 | hi_cap = c->cardinality - lo_cap; |
123 | 0 | if (hic && hi_cap) { |
124 | 0 | hi = array_container_create_given_capacity(hi_cap); |
125 | 0 | uint16_t *out = hi->array; |
126 | 0 | for (int i = 0; i < hi_cap; ++i) { |
127 | 0 | out[i] = array[lo_cap + i] + offset; |
128 | 0 | } |
129 | 0 | hi->cardinality = hi_cap; |
130 | 0 | *hic = (container_t *)hi; |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | 4.76k | int array_container_shrink_to_fit(array_container_t *src) { |
135 | 4.76k | if (src->cardinality == src->capacity) return 0; // nothing to do |
136 | 4.21k | int savings = src->capacity - src->cardinality; |
137 | 4.21k | src->capacity = src->cardinality; |
138 | 4.21k | if (src->capacity == |
139 | 4.21k | 0) { // we do not want to rely on realloc for zero allocs |
140 | 0 | roaring_free(src->array); |
141 | 0 | src->array = NULL; |
142 | 4.21k | } else { |
143 | 4.21k | uint16_t *oldarray = src->array; |
144 | 4.21k | src->array = (uint16_t *)roaring_realloc( |
145 | 4.21k | oldarray, src->capacity * sizeof(uint16_t)); |
146 | 4.21k | if (src->array == NULL) roaring_free(oldarray); // should never happen? |
147 | 4.21k | } |
148 | 4.21k | return savings; |
149 | 4.76k | } |
150 | | |
151 | | /* Free memory. */ |
152 | 149k | void array_container_free(array_container_t *arr) { |
153 | 149k | if (arr == NULL) return; |
154 | 149k | roaring_free(arr->array); |
155 | 149k | roaring_free(arr); |
156 | 149k | } |
157 | | |
158 | 127k | static inline int32_t grow_capacity(int32_t capacity) { |
159 | 127k | return (capacity <= 0) ? ARRAY_DEFAULT_INIT_SIZE |
160 | 127k | : capacity < 64 ? capacity * 2 |
161 | 98.3k | : capacity < 1024 ? capacity * 3 / 2 |
162 | 23.9k | : capacity * 5 / 4; |
163 | 127k | } |
164 | | |
165 | 127k | static inline int32_t clamp(int32_t val, int32_t min, int32_t max) { |
166 | 127k | return ((val < min) ? min : (val > max) ? max : val); |
167 | 127k | } |
168 | | |
169 | | void array_container_grow(array_container_t *container, int32_t min, |
170 | 127k | bool preserve) { |
171 | 127k | int32_t max = (min <= DEFAULT_MAX_SIZE ? DEFAULT_MAX_SIZE : 65536); |
172 | 127k | int32_t new_capacity = clamp(grow_capacity(container->capacity), min, max); |
173 | | |
174 | 127k | container->capacity = new_capacity; |
175 | 127k | uint16_t *array = container->array; |
176 | | |
177 | 127k | if (preserve) { |
178 | 115k | container->array = |
179 | 115k | (uint16_t *)roaring_realloc(array, new_capacity * sizeof(uint16_t)); |
180 | 115k | if (container->array == NULL) roaring_free(array); |
181 | 115k | } else { |
182 | 11.6k | roaring_free(array); |
183 | 11.6k | container->array = |
184 | 11.6k | (uint16_t *)roaring_malloc(new_capacity * sizeof(uint16_t)); |
185 | 11.6k | } |
186 | | |
187 | | // if realloc fails, we have container->array == NULL. |
188 | 127k | } |
189 | | |
190 | | /* Copy one container into another. We assume that they are distinct. */ |
191 | | void array_container_copy(const array_container_t *src, |
192 | 16 | array_container_t *dst) { |
193 | 16 | const int32_t cardinality = src->cardinality; |
194 | 16 | if (cardinality > dst->capacity) { |
195 | 16 | array_container_grow(dst, cardinality, false); |
196 | 16 | } |
197 | | |
198 | 16 | dst->cardinality = cardinality; |
199 | 16 | memcpy(dst->array, src->array, cardinality * sizeof(uint16_t)); |
200 | 16 | } |
201 | | |
202 | | void array_container_add_from_range(array_container_t *arr, uint32_t min, |
203 | 0 | uint32_t max, uint16_t step) { |
204 | 0 | for (uint32_t value = min; value < max; value += step) { |
205 | 0 | array_container_append(arr, value); |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | /* Computes the union of array1 and array2 and write the result to arrayout. |
210 | | * It is assumed that arrayout is distinct from both array1 and array2. |
211 | | */ |
212 | | void array_container_union(const array_container_t *array_1, |
213 | | const array_container_t *array_2, |
214 | 6.89k | array_container_t *out) { |
215 | 6.89k | const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; |
216 | 6.89k | const int32_t max_cardinality = card_1 + card_2; |
217 | | |
218 | 6.89k | if (out->capacity < max_cardinality) { |
219 | 0 | array_container_grow(out, max_cardinality, false); |
220 | 0 | } |
221 | 6.89k | out->cardinality = (int32_t)fast_union_uint16( |
222 | 6.89k | array_1->array, card_1, array_2->array, card_2, out->array); |
223 | 6.89k | } |
224 | | |
225 | | /* Computes the difference of array1 and array2 and write the result |
226 | | * to array out. |
227 | | * Array out does not need to be distinct from array_1 |
228 | | */ |
229 | | void array_container_andnot(const array_container_t *array_1, |
230 | | const array_container_t *array_2, |
231 | 7.92k | array_container_t *out) { |
232 | 7.92k | if (out->capacity < array_1->cardinality) |
233 | 3.45k | array_container_grow(out, array_1->cardinality, false); |
234 | 7.92k | #if CROARING_IS_X64 |
235 | | // difference_vector16 only vectorizes while 8 values remain on each |
236 | | // side, and loses to the galloping scalar code when one side is |
237 | | // much shorter. |
238 | 7.92k | const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; |
239 | 7.92k | const int32_t card_min = card_1 < card_2 ? card_1 : card_2; |
240 | 7.92k | const int32_t card_max = card_1 < card_2 ? card_2 : card_1; |
241 | 7.92k | const bool skewed = card_min < 8 || card_min * 128 < card_max; |
242 | 7.92k | if (!skewed && (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) && |
243 | 5.03k | (out != array_1) && (out != array_2)) { |
244 | 2.18k | out->cardinality = difference_vector16( |
245 | 2.18k | array_1->array, array_1->cardinality, array_2->array, |
246 | 2.18k | array_2->cardinality, out->array); |
247 | 5.74k | } else { |
248 | 5.74k | out->cardinality = |
249 | 5.74k | difference_uint16(array_1->array, array_1->cardinality, |
250 | 5.74k | array_2->array, array_2->cardinality, out->array); |
251 | 5.74k | } |
252 | | #else |
253 | | out->cardinality = |
254 | | difference_uint16(array_1->array, array_1->cardinality, array_2->array, |
255 | | array_2->cardinality, out->array); |
256 | | #endif |
257 | 7.92k | } |
258 | | |
259 | | /* Computes the symmetric difference of array1 and array2 and write the |
260 | | * result |
261 | | * to arrayout. |
262 | | * It is assumed that arrayout is distinct from both array1 and array2. |
263 | | */ |
264 | | void array_container_xor(const array_container_t *array_1, |
265 | | const array_container_t *array_2, |
266 | 9.28k | array_container_t *out) { |
267 | 9.28k | const int32_t card_1 = array_1->cardinality, card_2 = array_2->cardinality; |
268 | 9.28k | const int32_t max_cardinality = card_1 + card_2; |
269 | 9.28k | if (out->capacity < max_cardinality) { |
270 | 0 | array_container_grow(out, max_cardinality, false); |
271 | 0 | } |
272 | | |
273 | 9.28k | #if CROARING_IS_X64 |
274 | 9.28k | if (!(card_1 * CROARING_SKEWED_VECTOR_MERGE_RATIO < card_2 || |
275 | 8.60k | card_2 * CROARING_SKEWED_VECTOR_MERGE_RATIO < card_1) && |
276 | 8.29k | (croaring_hardware_support() & ROARING_SUPPORTS_AVX2)) { |
277 | 8.29k | out->cardinality = |
278 | 8.29k | xor_vector16(array_1->array, array_1->cardinality, array_2->array, |
279 | 8.29k | array_2->cardinality, out->array); |
280 | 8.29k | } else { |
281 | 992 | out->cardinality = |
282 | 992 | xor_uint16(array_1->array, array_1->cardinality, array_2->array, |
283 | 992 | array_2->cardinality, out->array); |
284 | 992 | } |
285 | | #else |
286 | | out->cardinality = |
287 | | xor_uint16(array_1->array, array_1->cardinality, array_2->array, |
288 | | array_2->cardinality, out->array); |
289 | | #endif |
290 | 9.28k | } |
291 | | |
292 | 3.45k | static inline int32_t minimum_int32(int32_t a, int32_t b) { |
293 | 3.45k | return (a < b) ? a : b; |
294 | 3.45k | } |
295 | | |
296 | | /* computes the intersection of array1 and array2 and write the result to |
297 | | * arrayout. |
298 | | * It is assumed that arrayout is distinct from both array1 and array2. |
299 | | * */ |
300 | | void array_container_intersection(const array_container_t *array1, |
301 | | const array_container_t *array2, |
302 | 3.45k | array_container_t *out) { |
303 | 3.45k | int32_t card_1 = array1->cardinality, card_2 = array2->cardinality, |
304 | 3.45k | min_card = minimum_int32(card_1, card_2); |
305 | 3.45k | const int threshold = 64; // subject to tuning |
306 | 3.45k | #if CROARING_IS_X64 |
307 | 3.45k | if (out->capacity < min_card) { |
308 | 3.45k | array_container_grow(out, min_card + sizeof(__m128i) / sizeof(uint16_t), |
309 | 3.45k | false); |
310 | 3.45k | } |
311 | | #else |
312 | | if (out->capacity < min_card) { |
313 | | array_container_grow(out, min_card, false); |
314 | | } |
315 | | #endif |
316 | | |
317 | 3.45k | if (card_1 * threshold < card_2) { |
318 | 314 | out->cardinality = intersect_skewed_uint16( |
319 | 314 | array1->array, card_1, array2->array, card_2, out->array); |
320 | 3.14k | } else if (card_2 * threshold < card_1) { |
321 | 192 | out->cardinality = intersect_skewed_uint16( |
322 | 192 | array2->array, card_2, array1->array, card_1, out->array); |
323 | 2.94k | } else { |
324 | 2.94k | #if CROARING_IS_X64 |
325 | 2.94k | if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { |
326 | 2.94k | out->cardinality = intersect_vector16( |
327 | 2.94k | array1->array, card_1, array2->array, card_2, out->array); |
328 | 2.94k | } else { |
329 | 0 | out->cardinality = intersect_uint16( |
330 | 0 | array1->array, card_1, array2->array, card_2, out->array); |
331 | 0 | } |
332 | | #else |
333 | | out->cardinality = intersect_uint16(array1->array, card_1, |
334 | | array2->array, card_2, out->array); |
335 | | #endif |
336 | 2.94k | } |
337 | 3.45k | } |
338 | | |
339 | | /* computes the size of the intersection of array1 and array2 |
340 | | * */ |
341 | | int array_container_intersection_cardinality(const array_container_t *array1, |
342 | 13.8k | const array_container_t *array2) { |
343 | 13.8k | int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; |
344 | 13.8k | const int threshold = 64; // subject to tuning |
345 | 13.8k | if (card_1 * threshold < card_2) { |
346 | 1.25k | return intersect_skewed_uint16_cardinality(array1->array, card_1, |
347 | 1.25k | array2->array, card_2); |
348 | 12.5k | } else if (card_2 * threshold < card_1) { |
349 | 768 | return intersect_skewed_uint16_cardinality(array2->array, card_2, |
350 | 768 | array1->array, card_1); |
351 | 11.7k | } else { |
352 | 11.7k | #if CROARING_IS_X64 |
353 | 11.7k | if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { |
354 | 11.7k | return intersect_vector16_cardinality(array1->array, card_1, |
355 | 11.7k | array2->array, card_2); |
356 | 11.7k | } else { |
357 | 0 | return intersect_uint16_cardinality(array1->array, card_1, |
358 | 0 | array2->array, card_2); |
359 | 0 | } |
360 | | #else |
361 | | return intersect_uint16_cardinality(array1->array, card_1, |
362 | | array2->array, card_2); |
363 | | #endif |
364 | 11.7k | } |
365 | 13.8k | } |
366 | | |
367 | | bool array_container_intersect(const array_container_t *array1, |
368 | 3.45k | const array_container_t *array2) { |
369 | 3.45k | int32_t card_1 = array1->cardinality, card_2 = array2->cardinality; |
370 | 3.45k | const int threshold = 64; // subject to tuning |
371 | 3.45k | if (card_1 * threshold < card_2) { |
372 | 314 | return intersect_skewed_uint16_nonempty(array1->array, card_1, |
373 | 314 | array2->array, card_2); |
374 | 3.14k | } else if (card_2 * threshold < card_1) { |
375 | 192 | return intersect_skewed_uint16_nonempty(array2->array, card_2, |
376 | 192 | array1->array, card_1); |
377 | 2.94k | } else { |
378 | | // we do not bother vectorizing |
379 | 2.94k | return intersect_uint16_nonempty(array1->array, card_1, array2->array, |
380 | 2.94k | card_2); |
381 | 2.94k | } |
382 | 3.45k | } |
383 | | |
384 | | /* computes the intersection of array1 and array2 and write the result to |
385 | | * array1. |
386 | | * */ |
387 | | void array_container_intersection_inplace(array_container_t *src_1, |
388 | 5.67k | const array_container_t *src_2) { |
389 | 5.67k | int32_t card_1 = src_1->cardinality, card_2 = src_2->cardinality; |
390 | 5.67k | const int threshold = 64; // subject to tuning |
391 | 5.67k | if (card_1 * threshold < card_2) { |
392 | 0 | src_1->cardinality = intersect_skewed_uint16( |
393 | 0 | src_1->array, card_1, src_2->array, card_2, src_1->array); |
394 | 5.67k | } else if (card_2 * threshold < card_1) { |
395 | 198 | src_1->cardinality = intersect_skewed_uint16( |
396 | 198 | src_2->array, card_2, src_1->array, card_1, src_1->array); |
397 | 5.47k | } else { |
398 | 5.47k | #if CROARING_IS_X64 |
399 | 5.47k | if (croaring_hardware_support() & ROARING_SUPPORTS_AVX2) { |
400 | 5.47k | src_1->cardinality = intersect_vector16_inplace( |
401 | 5.47k | src_1->array, card_1, src_2->array, card_2); |
402 | 5.47k | } else { |
403 | 0 | src_1->cardinality = intersect_uint16( |
404 | 0 | src_1->array, card_1, src_2->array, card_2, src_1->array); |
405 | 0 | } |
406 | | #else |
407 | | src_1->cardinality = intersect_uint16( |
408 | | src_1->array, card_1, src_2->array, card_2, src_1->array); |
409 | | #endif |
410 | 5.47k | } |
411 | 5.67k | } |
412 | | |
413 | | /* Constant n: one or two vector operations on every compiler. */ |
414 | | CROARING_ALLOW_UNALIGNED |
415 | | static inline void array_put(uint32_t *out, const uint16_t *array, |
416 | 0 | uint32_t base, size_t n) { |
417 | 0 | for (size_t k = 0; k < n; ++k) { |
418 | 0 | out[k] = base + array[k]; |
419 | 0 | } |
420 | 0 | } |
421 | | |
422 | | CROARING_ALLOW_UNALIGNED |
423 | | int array_container_to_uint32_array(void *vout, const array_container_t *cont, |
424 | 6.59k | uint32_t base) { |
425 | 6.59k | #if CROARING_IS_X64 |
426 | 6.59k | int support = croaring_hardware_support(); |
427 | 6.59k | #if CROARING_COMPILER_SUPPORTS_AVX512 |
428 | 6.59k | if (support & ROARING_SUPPORTS_AVX512) { |
429 | 0 | return avx512_array_container_to_uint32_array(vout, cont->array, |
430 | 0 | cont->cardinality, base); |
431 | 0 | } |
432 | 6.59k | #endif |
433 | 6.59k | if (support & ROARING_SUPPORTS_AVX2) { |
434 | 6.59k | return array_container_to_uint32_array_vector16( |
435 | 6.59k | vout, cont->array, cont->cardinality, base); |
436 | 6.59k | } |
437 | 0 | #endif // CROARING_IS_X64 |
438 | 0 | const size_t cardinality = (size_t)cont->cardinality; |
439 | 0 | const uint16_t *array = cont->array; |
440 | | // Typed stores vectorize on every compiler; all callers but |
441 | | // roaring_bitmap_serialize pass an aligned vout. |
442 | 0 | if (cardinality >= 4 && ((uintptr_t)vout & 3) == 0) { |
443 | 0 | uint32_t *out = (uint32_t *)vout; |
444 | 0 | uint32_t *out_end = out + cardinality; |
445 | 0 | const uint16_t *array_end = array + cardinality; |
446 | 0 | if (cardinality < 8) { // overlapping writes end at cardinality |
447 | 0 | array_put(out, array, base, 4); |
448 | 0 | array_put(out_end - 4, array_end - 4, base, 4); |
449 | 0 | return (int)cardinality; |
450 | 0 | } |
451 | 0 | const size_t full = cardinality & ~(size_t)15; |
452 | 0 | for (size_t i = 0; i < full; i += 16) { |
453 | 0 | array_put(out + i, array + i, base, 16); |
454 | 0 | } |
455 | 0 | if (cardinality - full > 8) { |
456 | 0 | array_put(out + full, array + full, base, 8); |
457 | 0 | } |
458 | 0 | if (full != cardinality) { |
459 | 0 | array_put(out_end - 8, array_end - 8, base, 8); |
460 | 0 | } |
461 | 0 | return (int)cardinality; |
462 | 0 | } |
463 | 0 | int outpos = 0; |
464 | 0 | uint32_t *out = (uint32_t *)vout; |
465 | 0 | size_t i = 0; |
466 | 0 | for (; i < (size_t)cont->cardinality; ++i) { |
467 | 0 | const uint32_t val = base + cont->array[i]; |
468 | 0 | memcpy(out + outpos, &val, |
469 | 0 | sizeof(uint32_t)); // should be compiled as a MOV on x64 |
470 | 0 | outpos++; |
471 | 0 | } |
472 | 0 | return outpos; |
473 | 0 | } |
474 | | |
475 | 0 | void array_container_printf(const array_container_t *v) { |
476 | 0 | if (v->cardinality == 0) { |
477 | 0 | printf("{}"); |
478 | 0 | return; |
479 | 0 | } |
480 | 0 | printf("{"); |
481 | 0 | printf("%d", v->array[0]); |
482 | 0 | for (int i = 1; i < v->cardinality; ++i) { |
483 | 0 | printf(",%d", v->array[i]); |
484 | 0 | } |
485 | 0 | printf("}"); |
486 | 0 | } |
487 | | |
488 | | void array_container_printf_as_uint32_array(const array_container_t *v, |
489 | 0 | uint32_t base) { |
490 | 0 | if (v->cardinality == 0) { |
491 | 0 | return; |
492 | 0 | } |
493 | 0 | printf("%u", v->array[0] + base); |
494 | 0 | for (int i = 1; i < v->cardinality; ++i) { |
495 | 0 | printf(",%u", v->array[i] + base); |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | | /* |
500 | | * Validate the container. Returns true if valid. |
501 | | */ |
502 | | CROARING_ALLOW_UNALIGNED |
503 | 0 | bool array_container_validate(const array_container_t *v, const char **reason) { |
504 | 0 | if (v->capacity < 0) { |
505 | 0 | *reason = "negative capacity"; |
506 | 0 | return false; |
507 | 0 | } |
508 | 0 | if (v->cardinality < 0) { |
509 | 0 | *reason = "negative cardinality"; |
510 | 0 | return false; |
511 | 0 | } |
512 | 0 | if (v->cardinality > v->capacity) { |
513 | 0 | *reason = "cardinality exceeds capacity"; |
514 | 0 | return false; |
515 | 0 | } |
516 | 0 | if (v->cardinality > DEFAULT_MAX_SIZE) { |
517 | 0 | *reason = "cardinality exceeds DEFAULT_MAX_SIZE"; |
518 | 0 | return false; |
519 | 0 | } |
520 | 0 | if (v->cardinality == 0) { |
521 | 0 | *reason = "zero cardinality"; |
522 | 0 | return false; |
523 | 0 | } |
524 | | |
525 | 0 | if (v->array == NULL) { |
526 | 0 | *reason = "NULL array pointer"; |
527 | 0 | return false; |
528 | 0 | } |
529 | 0 | uint16_t prev = v->array[0]; |
530 | 0 | for (int i = 1; i < v->cardinality; ++i) { |
531 | 0 | if (v->array[i] <= prev) { |
532 | 0 | *reason = "array elements not strictly increasing"; |
533 | 0 | return false; |
534 | 0 | } |
535 | 0 | prev = v->array[i]; |
536 | 0 | } |
537 | | |
538 | 0 | return true; |
539 | 0 | } |
540 | | |
541 | | /* Compute the number of runs */ |
542 | 14.3k | int32_t array_container_number_of_runs(const array_container_t *ac) { |
543 | 14.3k | const uint16_t *array = ac->array; |
544 | 14.3k | const int32_t card = ac->cardinality; |
545 | 14.3k | if (card == 0) return 0; |
546 | | // Every value starts a run unless it follows its predecessor. The values |
547 | | // are strictly increasing, so array[i - 1] + 1 cannot wrap around to a |
548 | | // match. The loop has no branch on the data and compilers vectorize it. |
549 | 14.3k | int32_t continuing = 0; |
550 | 1.54M | for (int32_t i = 1; i < card; ++i) { |
551 | 1.53M | continuing += (array[i] == (uint16_t)(array[i - 1] + 1)); |
552 | 1.53M | } |
553 | 14.3k | return card - continuing; |
554 | 14.3k | } |
555 | | |
556 | | /** |
557 | | * Writes the underlying array to buf, outputs how many bytes were written. |
558 | | * The number of bytes written should be |
559 | | * array_container_size_in_bytes(container). |
560 | | * |
561 | | */ |
562 | 7.66k | int32_t array_container_write(const array_container_t *container, char *buf) { |
563 | | #if CROARING_IS_BIG_ENDIAN |
564 | | for (int32_t i = 0; i < container->cardinality; ++i) { |
565 | | uint16_t v_le = croaring_htole16(container->array[i]); |
566 | | memcpy(buf + i * sizeof(uint16_t), &v_le, sizeof(uint16_t)); |
567 | | } |
568 | | #else |
569 | 7.66k | memcpy(buf, container->array, container->cardinality * sizeof(uint16_t)); |
570 | 7.66k | #endif |
571 | 7.66k | return array_container_size_in_bytes(container); |
572 | 7.66k | } |
573 | | |
574 | | CROARING_ALLOW_UNALIGNED |
575 | | bool array_container_is_subset(const array_container_t *container1, |
576 | 7.18k | const array_container_t *container2) { |
577 | 7.18k | if (container1->cardinality > container2->cardinality) { |
578 | 1.20k | return false; |
579 | 1.20k | } |
580 | 5.98k | int i1 = 0, i2 = 0; |
581 | 45.6k | while (i1 < container1->cardinality && i2 < container2->cardinality) { |
582 | 45.5k | if (container1->array[i1] == container2->array[i2]) { |
583 | 0 | i1++; |
584 | 0 | i2++; |
585 | 45.5k | } else if (container1->array[i1] > container2->array[i2]) { |
586 | 39.6k | i2++; |
587 | 39.6k | } else { // container1->array[i1] < container2->array[i2] |
588 | 5.95k | return false; |
589 | 5.95k | } |
590 | 45.5k | } |
591 | 30 | if (i1 == container1->cardinality) { |
592 | 0 | return true; |
593 | 30 | } else { |
594 | 30 | return false; |
595 | 30 | } |
596 | 30 | } |
597 | | |
598 | | int32_t array_container_read(int32_t cardinality, array_container_t *container, |
599 | 7.71k | const char *buf) { |
600 | 7.71k | if (container->capacity < cardinality) { |
601 | 0 | array_container_grow(container, cardinality, false); |
602 | 0 | } |
603 | 7.71k | container->cardinality = cardinality; |
604 | | #if CROARING_IS_BIG_ENDIAN |
605 | | for (int32_t i = 0; i < cardinality; ++i) { |
606 | | uint16_t v_le; |
607 | | memcpy(&v_le, buf + i * sizeof(uint16_t), sizeof(uint16_t)); |
608 | | container->array[i] = croaring_letoh16(v_le); |
609 | | } |
610 | | #else |
611 | 7.71k | memcpy(container->array, buf, container->cardinality * sizeof(uint16_t)); |
612 | 7.71k | #endif |
613 | | |
614 | 7.71k | return array_container_size_in_bytes(container); |
615 | 7.71k | } |
616 | | |
617 | | CROARING_ALLOW_UNALIGNED |
618 | | bool array_container_iterate(const array_container_t *cont, uint32_t base, |
619 | 7.26k | roaring_iterator iterator, void *ptr) { |
620 | 2.37M | for (int i = 0; i < cont->cardinality; i++) |
621 | 2.36M | if (!iterator(cont->array[i] + base, ptr)) return false; |
622 | 7.26k | return true; |
623 | 7.26k | } |
624 | | |
625 | | CROARING_ALLOW_UNALIGNED |
626 | | bool array_container_iterate64(const array_container_t *cont, uint32_t base, |
627 | | roaring_iterator64 iterator, uint64_t high_bits, |
628 | 0 | void *ptr) { |
629 | 0 | for (int i = 0; i < cont->cardinality; i++) |
630 | 0 | if (!iterator(high_bits | (uint64_t)(cont->array[i] + base), ptr)) |
631 | 0 | return false; |
632 | 0 | return true; |
633 | 0 | } |
634 | | |
635 | | #ifdef __cplusplus |
636 | | } |
637 | | } |
638 | | } // extern "C" { namespace roaring { namespace internal { |
639 | | #endif |