Skip to content

Commit f43ab1f

Browse files
lkirkjeromekelleher
authored andcommitted
Optimize two-locus site operations
This PR is a combination of three separate modifications. They are described below and in #3290. Fixes (#3290). * Two-locus malloc optimizations This revision moves all malloc operations out of the hot loop in two-locus statistics, instead providing pre-allocated regions of memory that the two-locus framework will use to perform work. Instead of simply passing each pre-allocated array into each function call, we introduce a simple structure called `two_locus_work_t`, which stores the statistical results, and provides temporary arrays for storing the normalisation constants. Setup and teardown methods for this work structure are provided. Python and C tests are passing and valgrind reports no errors. * Refactor bit array api, rename to bitset. As discussed in #2834, this patch renames tsk_bit_array_t to tsk_bitset_t. Philosophically, we treat these as sets and not arrays, performing intersections, unions, and membership tests. Therefore, it makes sense to alter the API to use set theoretic vocabulary, describing the intent more precisely. Fundamentally, the bitset structure is a list of N independent bitsets. Each operation on two sets must select the row on which to operate. The tsk_bitset_t originally tracked `len` only, which was N, the number of sets. For convenience, we also track the `row_len`, which is the number of unsigned integers per row. If we multiply `row_len` by `TSK_BITSET_BITS`, we get the number of bits that each set (or row) in the list of bitsets will hold. We had also discussed each set theoretic operation accepting a row index instead of a pointer to a row within the bitset object. Now, each operation accepts a row index for each bitset structure passed into the function. This simplifies the consumption of this API considerably, removing the need of storing and tracking many intermediate temporary array pointers. We also see some performance improvements from this cleanup. For DRY purposes, I've created a private macro, `BITSET_DATA_ROW`, which abstracts away the pointer arithmetic for selecting a row out of the list of sets. Because of these changes, `tsk_bit_array_get_row` is no longer needed and has been removed from the API. This change does not change the size of the "chunk", which is the unsigned integer storing bits. It remains a 32 bit unsigned integer, which is most performant for bit counting (popcount). I've streamlined the macros used to determine which integer in the row will be used to store a particular bit. Everything now revolves around the TSK_BITSET_BITS macro, which is simply 32 and bitshift operations have been converted to unsigned integer division. Testing has been refactored to reflect these changes, removing tests that operate on a specific rows. Tests in c and python are passing and valgrind shows no errors. Fixes (#2834). * Precompute A/B Counts and Biallelic Summary Func Precompute A/B counts for each sample set. We were previously computing them redundantly each for each site pair in our results matrix. The precomputation happens in a function called `get_mutation_sample_sets`, which takes our list of sets (`tsk_bitset_t`) for each mutation and intersects the samples with a particular mutation with the sample sets passed in by the user. The result is an expanded list of sets with one set per mutation per sample set. During this operation, we compute the number of samples containing the given allele for each mutation, avoiding the need to perform redundant count operations on the data. In addition to precomputation, we add a non-normalized version of `compute_general_two_site_stat_result` for situations where we're computing stats from biallelic loci. We dispatch the computation of the result based on the number of alleles in the two loci we're comparing. If the number of alleles in both loci is 2, then we simply perform an LD computation on the derived alleles for the two loci. As a result, we remove the need to compute a matrix of LD values, then take a weighted sum. This is much more efficient and means that we only run the full multiallelic LD routine on sites that are multiallelic.
1 parent e956149 commit f43ab1f

File tree

4 files changed

+457
-360
lines changed

4 files changed

+457
-360
lines changed

c/tests/test_core.c

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -531,111 +531,107 @@ test_bit_arrays(void)
531531
{
532532
// NB: This test is only valid for the 32 bit implementation of bit arrays. If we
533533
// were to change the chunk size of a bit array, we'd need to update these tests
534-
tsk_bit_array_t arr;
534+
tsk_bitset_t arr;
535535
tsk_id_t items_truth[64] = { 0 }, items[64] = { 0 };
536536
tsk_size_t n_items = 0, n_items_truth = 0;
537537

538538
// test item retrieval
539-
tsk_bit_array_init(&arr, 90, 1);
540-
tsk_bit_array_get_items(&arr, items, &n_items);
539+
tsk_bitset_init(&arr, 90, 1);
540+
CU_ASSERT_EQUAL_FATAL(arr.len, 1);
541+
CU_ASSERT_EQUAL_FATAL(arr.row_len, 3);
542+
tsk_bitset_get_items(&arr, 0, items, &n_items);
541543
assert_arrays_equal(n_items_truth, items, items_truth);
542544

543-
for (tsk_bit_array_value_t i = 0; i < 20; i++) {
544-
tsk_bit_array_add_bit(&arr, i);
545+
for (tsk_bitset_val_t i = 0; i < 20; i++) {
546+
tsk_bitset_set_bit(&arr, 0, i);
545547
items_truth[n_items_truth] = (tsk_id_t) i;
546548
n_items_truth++;
547549
}
548-
tsk_bit_array_add_bit(&arr, 63);
549-
tsk_bit_array_add_bit(&arr, 65);
550+
tsk_bitset_set_bit(&arr, 0, 63);
551+
tsk_bitset_set_bit(&arr, 0, 65);
550552

551553
// these assertions are only valid for 32-bit values
552554
CU_ASSERT_EQUAL_FATAL(arr.data[0], 1048575);
553555
CU_ASSERT_EQUAL_FATAL(arr.data[1], 2147483648);
554556
CU_ASSERT_EQUAL_FATAL(arr.data[2], 2);
555557

556558
// verify our assumptions about bit array counting
557-
CU_ASSERT_EQUAL_FATAL(tsk_bit_array_count(&arr), 22);
559+
CU_ASSERT_EQUAL_FATAL(tsk_bitset_count(&arr, 0), 22);
558560

559-
tsk_bit_array_get_items(&arr, items, &n_items);
561+
tsk_bitset_get_items(&arr, 0, items, &n_items);
560562
assert_arrays_equal(n_items_truth, items, items_truth);
561563

562564
tsk_memset(items, 0, 64);
563565
tsk_memset(items_truth, 0, 64);
564566
n_items = n_items_truth = 0;
565-
tsk_bit_array_free(&arr);
567+
tsk_bitset_free(&arr);
566568

567-
// create a length-2 array with 64 bit capacity
568-
tsk_bit_array_init(&arr, 64, 2);
569-
tsk_bit_array_t arr_row1, arr_row2;
570-
571-
// select the first and second row
572-
tsk_bit_array_get_row(&arr, 0, &arr_row1);
573-
tsk_bit_array_get_row(&arr, 1, &arr_row2);
569+
// create a length-2 array with 64 bit capacity (two chunks per row)
570+
tsk_bitset_init(&arr, 64, 2);
571+
CU_ASSERT_EQUAL_FATAL(arr.len, 2);
572+
CU_ASSERT_EQUAL_FATAL(arr.row_len, 2);
574573

575574
// fill the first 50 bits of the first row
576-
for (tsk_bit_array_value_t i = 0; i < 50; i++) {
577-
tsk_bit_array_add_bit(&arr_row1, i);
575+
for (tsk_bitset_val_t i = 0; i < 50; i++) {
576+
tsk_bitset_set_bit(&arr, 0, i);
578577
items_truth[n_items_truth] = (tsk_id_t) i;
579578
n_items_truth++;
580579
}
581580

582-
tsk_bit_array_get_items(&arr_row1, items, &n_items);
581+
tsk_bitset_get_items(&arr, 0, items, &n_items);
583582
assert_arrays_equal(n_items_truth, items, items_truth);
584583

585584
tsk_memset(items, 0, 64);
586585
tsk_memset(items_truth, 0, 64);
587586
n_items = n_items_truth = 0;
588587

589588
// fill bits 20-40 of the second row
590-
for (tsk_bit_array_value_t i = 20; i < 40; i++) {
591-
tsk_bit_array_add_bit(&arr_row2, i);
589+
for (tsk_bitset_val_t i = 20; i < 40; i++) {
590+
tsk_bitset_set_bit(&arr, 1, i);
592591
items_truth[n_items_truth] = (tsk_id_t) i;
593592
n_items_truth++;
594593
}
595594

596-
tsk_bit_array_get_items(&arr_row2, items, &n_items);
595+
tsk_bitset_get_items(&arr, 1, items, &n_items);
597596
assert_arrays_equal(n_items_truth, items, items_truth);
598597

599598
tsk_memset(items, 0, 64);
600599
tsk_memset(items_truth, 0, 64);
601600
n_items = n_items_truth = 0;
602601

603602
// verify our assumptions about row selection
604-
CU_ASSERT_EQUAL_FATAL(arr.data[0], 4294967295);
605-
CU_ASSERT_EQUAL_FATAL(arr.data[1], 262143);
606-
CU_ASSERT_EQUAL_FATAL(arr_row1.data[0], 4294967295);
607-
CU_ASSERT_EQUAL_FATAL(arr_row1.data[1], 262143);
608-
609-
CU_ASSERT_EQUAL_FATAL(arr.data[2], 4293918720);
610-
CU_ASSERT_EQUAL_FATAL(arr.data[3], 255);
611-
CU_ASSERT_EQUAL_FATAL(arr_row2.data[0], 4293918720);
612-
CU_ASSERT_EQUAL_FATAL(arr_row2.data[1], 255);
603+
CU_ASSERT_EQUAL_FATAL(arr.data[0], 4294967295); // row1 elem1
604+
CU_ASSERT_EQUAL_FATAL(arr.data[1], 262143); // row1 elem2
605+
CU_ASSERT_EQUAL_FATAL(arr.data[2], 4293918720); // row2 elem1
606+
CU_ASSERT_EQUAL_FATAL(arr.data[3], 255); // row2 elem2
613607

614608
// subtract the second from the first row, store in first
615-
tsk_bit_array_subtract(&arr_row1, &arr_row2);
609+
tsk_bitset_subtract(&arr, 0, &arr, 1);
616610

617611
// verify our assumptions about subtraction
618-
CU_ASSERT_EQUAL_FATAL(arr_row1.data[0], 1048575);
619-
CU_ASSERT_EQUAL_FATAL(arr_row1.data[1], 261888);
612+
CU_ASSERT_EQUAL_FATAL(arr.data[0], 1048575);
613+
CU_ASSERT_EQUAL_FATAL(arr.data[1], 261888);
620614

621-
tsk_bit_array_t int_result;
622-
tsk_bit_array_init(&int_result, 64, 1);
615+
tsk_bitset_t int_result;
616+
tsk_bitset_init(&int_result, 64, 1);
617+
CU_ASSERT_EQUAL_FATAL(int_result.len, 1);
618+
CU_ASSERT_EQUAL_FATAL(int_result.row_len, 2);
623619

624620
// their intersection should be zero
625-
tsk_bit_array_intersect(&arr_row1, &arr_row2, &int_result);
621+
tsk_bitset_intersect(&arr, 0, &arr, 1, &int_result);
626622
CU_ASSERT_EQUAL_FATAL(int_result.data[0], 0);
627623
CU_ASSERT_EQUAL_FATAL(int_result.data[1], 0);
628624

629625
// now, add them back together, storing back in a
630-
tsk_bit_array_add(&arr_row1, &arr_row2);
626+
tsk_bitset_union(&arr, 0, &arr, 1);
631627

632628
// now, their intersection should be the subtracted chunk (20-40)
633-
tsk_bit_array_intersect(&arr_row1, &arr_row2, &int_result);
629+
tsk_bitset_intersect(&arr, 0, &arr, 1, &int_result);
634630
CU_ASSERT_EQUAL_FATAL(int_result.data[0], 4293918720);
635631
CU_ASSERT_EQUAL_FATAL(int_result.data[1], 255);
636632

637-
tsk_bit_array_free(&int_result);
638-
tsk_bit_array_free(&arr);
633+
tsk_bitset_free(&int_result);
634+
tsk_bitset_free(&arr);
639635
}
640636

641637
static void

c/tskit/core.c

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,16 +1260,16 @@ tsk_avl_tree_int_ordered_nodes(const tsk_avl_tree_int_t *self, tsk_avl_node_int_
12601260
}
12611261

12621262
// Bit Array implementation. Allows us to store unsigned integers in a compact manner.
1263-
// Currently implemented as an array of 32-bit unsigned integers for ease of counting.
1263+
// Currently implemented as an array of 32-bit unsigned integers.
12641264

12651265
int
1266-
tsk_bit_array_init(tsk_bit_array_t *self, tsk_size_t num_bits, tsk_size_t length)
1266+
tsk_bitset_init(tsk_bitset_t *self, tsk_size_t num_bits, tsk_size_t length)
12671267
{
12681268
int ret = 0;
12691269

1270-
self->size = (num_bits >> TSK_BIT_ARRAY_CHUNK)
1271-
+ (num_bits % TSK_BIT_ARRAY_NUM_BITS ? 1 : 0);
1272-
self->data = tsk_calloc(self->size * length, sizeof(*self->data));
1270+
self->row_len = (num_bits / TSK_BITSET_BITS) + (num_bits % TSK_BITSET_BITS ? 1 : 0);
1271+
self->len = length;
1272+
self->data = tsk_calloc(self->row_len * length, sizeof(*self->data));
12731273
if (self->data == NULL) {
12741274
ret = tsk_trace_error(TSK_ERR_NO_MEMORY);
12751275
goto out;
@@ -1278,96 +1278,111 @@ tsk_bit_array_init(tsk_bit_array_t *self, tsk_size_t num_bits, tsk_size_t length
12781278
return ret;
12791279
}
12801280

1281-
void
1282-
tsk_bit_array_get_row(const tsk_bit_array_t *self, tsk_size_t row, tsk_bit_array_t *out)
1283-
{
1284-
out->size = self->size;
1285-
out->data = self->data + (row * self->size);
1286-
}
1281+
#define BITSET_DATA_ROW(bs, row) ((bs)->data + (row) * (bs)->row_len)
12871282

12881283
void
1289-
tsk_bit_array_intersect(
1290-
const tsk_bit_array_t *self, const tsk_bit_array_t *other, tsk_bit_array_t *out)
1284+
tsk_bitset_intersect(const tsk_bitset_t *self, tsk_size_t self_row,
1285+
const tsk_bitset_t *other, tsk_size_t other_row, tsk_bitset_t *out)
12911286
{
1292-
for (tsk_size_t i = 0; i < self->size; i++) {
1293-
out->data[i] = self->data[i] & other->data[i];
1287+
const tsk_bitset_val_t *restrict self_d = BITSET_DATA_ROW(self, self_row);
1288+
const tsk_bitset_val_t *restrict other_d = BITSET_DATA_ROW(other, other_row);
1289+
tsk_bitset_val_t *restrict out_d = out->data;
1290+
for (tsk_size_t i = 0; i < self->row_len; i++) {
1291+
out_d[i] = self_d[i] & other_d[i];
12941292
}
12951293
}
12961294

12971295
void
1298-
tsk_bit_array_subtract(tsk_bit_array_t *self, const tsk_bit_array_t *other)
1296+
tsk_bitset_subtract(tsk_bitset_t *self, tsk_size_t self_row, const tsk_bitset_t *other,
1297+
tsk_size_t other_row)
12991298
{
1300-
for (tsk_size_t i = 0; i < self->size; i++) {
1301-
self->data[i] &= ~(other->data[i]);
1299+
tsk_bitset_val_t *restrict self_d = BITSET_DATA_ROW(self, self_row);
1300+
const tsk_bitset_val_t *restrict other_d = BITSET_DATA_ROW(other, other_row);
1301+
for (tsk_size_t i = 0; i < self->row_len; i++) {
1302+
self_d[i] &= ~(other_d[i]);
13021303
}
13031304
}
13041305

13051306
void
1306-
tsk_bit_array_add(tsk_bit_array_t *self, const tsk_bit_array_t *other)
1307+
tsk_bitset_union(tsk_bitset_t *self, tsk_size_t self_row, const tsk_bitset_t *other,
1308+
tsk_size_t other_row)
13071309
{
1308-
for (tsk_size_t i = 0; i < self->size; i++) {
1309-
self->data[i] |= other->data[i];
1310+
tsk_bitset_val_t *restrict self_d = BITSET_DATA_ROW(self, self_row);
1311+
const tsk_bitset_val_t *restrict other_d = BITSET_DATA_ROW(other, other_row);
1312+
for (tsk_size_t i = 0; i < self->row_len; i++) {
1313+
self_d[i] |= other_d[i];
13101314
}
13111315
}
13121316

13131317
void
1314-
tsk_bit_array_add_bit(tsk_bit_array_t *self, const tsk_bit_array_value_t bit)
1318+
tsk_bitset_set_bit(tsk_bitset_t *self, tsk_size_t row, const tsk_bitset_val_t bit)
13151319
{
1316-
tsk_bit_array_value_t i = bit >> TSK_BIT_ARRAY_CHUNK;
1317-
self->data[i] |= (tsk_bit_array_value_t) 1 << (bit - (TSK_BIT_ARRAY_NUM_BITS * i));
1320+
tsk_bitset_val_t i = (bit / TSK_BITSET_BITS);
1321+
*(BITSET_DATA_ROW(self, row) + i) |= (tsk_bitset_val_t) 1
1322+
<< (bit - (TSK_BITSET_BITS * i));
13181323
}
13191324

13201325
bool
1321-
tsk_bit_array_contains(const tsk_bit_array_t *self, const tsk_bit_array_value_t bit)
1326+
tsk_bitset_contains(const tsk_bitset_t *self, tsk_size_t row, const tsk_bitset_val_t bit)
13221327
{
1323-
tsk_bit_array_value_t i = bit >> TSK_BIT_ARRAY_CHUNK;
1324-
return self->data[i]
1325-
& ((tsk_bit_array_value_t) 1 << (bit - (TSK_BIT_ARRAY_NUM_BITS * i)));
1328+
tsk_bitset_val_t i = (bit / TSK_BITSET_BITS);
1329+
return *(BITSET_DATA_ROW(self, row) + i)
1330+
& ((tsk_bitset_val_t) 1 << (bit - (TSK_BITSET_BITS * i)));
13261331
}
13271332

1328-
tsk_size_t
1329-
tsk_bit_array_count(const tsk_bit_array_t *self)
1333+
static inline uint32_t
1334+
popcount(tsk_bitset_val_t v)
13301335
{
1331-
// Utilizes 12 operations per bit array. NB this only works on 32 bit integers.
1336+
// Utilizes 12 operations per chunk. NB this only works on 32 bit integers.
13321337
// Taken from:
13331338
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
13341339
// There's a nice breakdown of this algorithm here:
13351340
// https://stackoverflow.com/a/109025
1336-
// Could probably do better with explicit SIMD (instead of SWAR), but not as
1337-
// portable: https://arxiv.org/pdf/1611.07612.pdf
13381341
//
1339-
// There is one solution to explore further, which uses __builtin_popcountll.
1340-
// This option is relatively simple, but requires a 64 bit bit array and also
1341-
// involves some compiler flag plumbing (-mpopcnt)
1342+
// The gcc/clang compiler flag will -mpopcnt will convert this code to a
1343+
// popcnt instruction (most if not all modern CPUs will support this). The
1344+
// popcnt instruction will yield some speed improvements, which depend on
1345+
// the tree sequence.
1346+
//
1347+
// NB: 32bit counting is typically faster than 64bit counting for this task.
1348+
// (at least on x86-64)
13421349

1343-
tsk_bit_array_value_t tmp;
1344-
tsk_size_t i, count = 0;
1350+
v = v - ((v >> 1) & 0x55555555);
1351+
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
1352+
return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
1353+
}
1354+
1355+
tsk_size_t
1356+
tsk_bitset_count(const tsk_bitset_t *self, tsk_size_t row)
1357+
{
1358+
tsk_size_t i = 0;
1359+
tsk_size_t count = 0;
1360+
const tsk_bitset_val_t *restrict self_d = BITSET_DATA_ROW(self, row);
13451361

1346-
for (i = 0; i < self->size; i++) {
1347-
tmp = self->data[i] - ((self->data[i] >> 1) & 0x55555555);
1348-
tmp = (tmp & 0x33333333) + ((tmp >> 2) & 0x33333333);
1349-
count += (((tmp + (tmp >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
1362+
for (i = 0; i < self->row_len; i++) {
1363+
count += popcount(self_d[i]);
13501364
}
13511365
return count;
13521366
}
13531367

13541368
void
1355-
tsk_bit_array_get_items(
1356-
const tsk_bit_array_t *self, tsk_id_t *items, tsk_size_t *n_items)
1369+
tsk_bitset_get_items(
1370+
const tsk_bitset_t *self, tsk_size_t row, tsk_id_t *items, tsk_size_t *n_items)
13571371
{
13581372
// Get the items stored in the row of a bitset.
1359-
// Uses a de Bruijn sequence lookup table to determine the lowest bit set. See the
1360-
// wikipedia article for more info: https://w.wiki/BYiF
1373+
// Uses a de Bruijn sequence lookup table to determine the lowest bit set.
1374+
// See the wikipedia article for more info: https://w.wiki/BYiF
13611375

13621376
tsk_size_t i, n, off;
1363-
tsk_bit_array_value_t v, lsb; // least significant bit
1377+
tsk_bitset_val_t v, lsb; // least significant bit
13641378
static const tsk_id_t lookup[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25,
13651379
17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
1380+
const tsk_bitset_val_t *restrict self_d = BITSET_DATA_ROW(self, row);
13661381

13671382
n = 0;
1368-
for (i = 0; i < self->size; i++) {
1369-
v = self->data[i];
1370-
off = i * ((tsk_size_t) TSK_BIT_ARRAY_NUM_BITS);
1383+
for (i = 0; i < self->row_len; i++) {
1384+
v = self_d[i];
1385+
off = i * TSK_BITSET_BITS;
13711386
if (v == 0) {
13721387
continue;
13731388
}
@@ -1381,7 +1396,7 @@ tsk_bit_array_get_items(
13811396
}
13821397

13831398
void
1384-
tsk_bit_array_free(tsk_bit_array_t *self)
1399+
tsk_bitset_free(tsk_bitset_t *self)
13851400
{
13861401
tsk_safe_free(self->data);
13871402
}

c/tskit/core.h

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,29 +1104,31 @@ FILE *tsk_get_debug_stream(void);
11041104

11051105
/* Bit Array functionality */
11061106

1107-
typedef uint32_t tsk_bit_array_value_t;
1107+
// define a 32-bit chunk size for our bitsets.
1108+
// this means we'll be able to hold 32 distinct items in each 32 bit uint
1109+
#define TSK_BITSET_BITS ((tsk_size_t) 32)
1110+
typedef uint32_t tsk_bitset_val_t;
1111+
11081112
typedef struct {
1109-
tsk_size_t size; // Number of chunks per row
1110-
tsk_bit_array_value_t *data; // Array data
1111-
} tsk_bit_array_t;
1112-
1113-
#define TSK_BIT_ARRAY_CHUNK 5U
1114-
#define TSK_BIT_ARRAY_NUM_BITS (1U << TSK_BIT_ARRAY_CHUNK)
1115-
1116-
int tsk_bit_array_init(tsk_bit_array_t *self, tsk_size_t num_bits, tsk_size_t length);
1117-
void tsk_bit_array_free(tsk_bit_array_t *self);
1118-
void tsk_bit_array_get_row(
1119-
const tsk_bit_array_t *self, tsk_size_t row, tsk_bit_array_t *out);
1120-
void tsk_bit_array_intersect(
1121-
const tsk_bit_array_t *self, const tsk_bit_array_t *other, tsk_bit_array_t *out);
1122-
void tsk_bit_array_subtract(tsk_bit_array_t *self, const tsk_bit_array_t *other);
1123-
void tsk_bit_array_add(tsk_bit_array_t *self, const tsk_bit_array_t *other);
1124-
void tsk_bit_array_add_bit(tsk_bit_array_t *self, const tsk_bit_array_value_t bit);
1125-
bool tsk_bit_array_contains(
1126-
const tsk_bit_array_t *self, const tsk_bit_array_value_t bit);
1127-
tsk_size_t tsk_bit_array_count(const tsk_bit_array_t *self);
1128-
void tsk_bit_array_get_items(
1129-
const tsk_bit_array_t *self, tsk_id_t *items, tsk_size_t *n_items);
1113+
tsk_size_t row_len; // Number of size TSK_BITSET_BITS chunks per row
1114+
tsk_size_t len; // Number of rows
1115+
tsk_bitset_val_t *data;
1116+
} tsk_bitset_t;
1117+
1118+
int tsk_bitset_init(tsk_bitset_t *self, tsk_size_t num_bits, tsk_size_t length);
1119+
void tsk_bitset_free(tsk_bitset_t *self);
1120+
void tsk_bitset_intersect(const tsk_bitset_t *self, tsk_size_t self_row,
1121+
const tsk_bitset_t *other, tsk_size_t other_row, tsk_bitset_t *out);
1122+
void tsk_bitset_subtract(tsk_bitset_t *self, tsk_size_t self_row,
1123+
const tsk_bitset_t *other, tsk_size_t other_row);
1124+
void tsk_bitset_union(tsk_bitset_t *self, tsk_size_t self_row, const tsk_bitset_t *other,
1125+
tsk_size_t other_row);
1126+
void tsk_bitset_set_bit(tsk_bitset_t *self, tsk_size_t row, const tsk_bitset_val_t bit);
1127+
bool tsk_bitset_contains(
1128+
const tsk_bitset_t *self, tsk_size_t row, const tsk_bitset_val_t bit);
1129+
tsk_size_t tsk_bitset_count(const tsk_bitset_t *self, tsk_size_t row);
1130+
void tsk_bitset_get_items(
1131+
const tsk_bitset_t *self, tsk_size_t row, tsk_id_t *items, tsk_size_t *n_items);
11301132

11311133
#ifdef __cplusplus
11321134
}

0 commit comments

Comments
 (0)