From: Akim Demaille Date: Sun, 28 Oct 2018 16:32:15 +0000 (+0100) Subject: bitset: new module X-Git-Tag: v1.0~5290 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=de7956e9d91c826855b664867fb6e1ce35988020;p=gnulib.git bitset: new module * lib/bitset.c, lib/bitset.h, lib/bitset/array.c, * lib/bitset/array.h, lib/bitset/base.h, lib/bitset/expandable.c, * lib/bitset/expandable.h, lib/bitset/list.c, lib/bitset/list.h, * lib/bitset/stats.c, lib/bitset/stats.h, lib/bitset/vector.c, * lib/bitset/vector.h, modules/bitset: New. --- diff --git a/ChangeLog b/ChangeLog index cbc7260ef3..9fae7910fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2018-11-25 Akim Demaille + + bitset: new module + * lib/bitset.c, lib/bitset.h, lib/bitset/array.c, + * lib/bitset/array.h, lib/bitset/base.h, lib/bitset/expandable.c, + * lib/bitset/expandable.h, lib/bitset/list.c, lib/bitset/list.h, + * lib/bitset/stats.c, lib/bitset/stats.h, lib/bitset/vector.c, + * lib/bitset/vector.h, modules/bitset: + New. + 2018-11-23 Bruno Haible localename: Fix gettext test failures on mingw. diff --git a/lib/bitset.c b/lib/bitset.c new file mode 100644 index 0000000000..8b89820913 --- /dev/null +++ b/lib/bitset.c @@ -0,0 +1,480 @@ +/* General bitsets. + + Copyright (C) 2002-2006, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "bitset.h" + +#include +#include + +#include "obstack.h" + +#include "bitset/array.h" +#include "bitset/expandable.h" +#include "bitset/list.h" +#include "bitset/stats.h" +#include "bitset/vector.h" + +const char * const bitset_type_names[] = BITSET_TYPE_NAMES; + + +/* Return number of bytes required to create a N_BIT bitset + of TYPE. The bitset may grow to require more bytes than this. */ +size_t +bitset_bytes (enum bitset_type type, bitset_bindex n_bits) +{ + if (bitset_stats_enabled) + return bitset_stats_bytes (); + + switch (type) + { + default: + abort (); + + case BITSET_ARRAY: + return abitset_bytes (n_bits); + + case BITSET_LIST: + return lbitset_bytes (n_bits); + + case BITSET_TABLE: + return ebitset_bytes (n_bits); + + case BITSET_VARRAY: + return vbitset_bytes (n_bits); + } +} + + +/* Initialise bitset BSET of TYPE for N_BITS. */ +bitset +bitset_init (bitset bset, bitset_bindex n_bits, enum bitset_type type) +{ + if (bitset_stats_enabled) + return bitset_stats_init (bset, n_bits, type); + + switch (type) + { + default: + abort (); + + case BITSET_ARRAY: + return abitset_init (bset, n_bits); + + case BITSET_LIST: + return lbitset_init (bset, n_bits); + + case BITSET_TABLE: + return ebitset_init (bset, n_bits); + + case BITSET_VARRAY: + return vbitset_init (bset, n_bits); + } +} + + +/* Select a bitset type for a set of N_BITS and with attribute hints + specified by ATTR. For variable size bitsets, N_BITS is only a + hint and may be zero. */ +enum bitset_type +bitset_type_choose (bitset_bindex n_bits ATTRIBUTE_UNUSED, unsigned attr) +{ + /* Check attributes. */ + if (attr & BITSET_FIXED && attr & BITSET_VARIABLE) + abort (); + if (attr & BITSET_SPARSE && attr & BITSET_DENSE) + abort (); + + /* Choose the type of bitset. Note that sometimes we will be asked + for a zero length fixed size bitset. */ + + + /* If no attributes selected, choose a good compromise. */ + if (!attr) + return BITSET_VARRAY; + + if (attr & BITSET_SPARSE) + return BITSET_LIST; + + if (attr & BITSET_FIXED) + return BITSET_ARRAY; + + if (attr & BITSET_GREEDY) + return BITSET_TABLE; + + return BITSET_VARRAY; +} + + +/* Create a bitset of N_BITS of type TYPE. */ +bitset +bitset_alloc (bitset_bindex n_bits, enum bitset_type type) +{ + size_t bytes = bitset_bytes (type, n_bits); + + bitset bset = xcalloc (1, bytes); + + /* The cache is disabled until some elements are allocated. If we + have variable length arrays, then we may need to allocate a dummy + element. */ + + return bitset_init (bset, n_bits, type); +} + + +/* Create a bitset of N_BITS of type TYPE. */ +bitset +bitset_obstack_alloc (struct obstack *bobstack, + bitset_bindex n_bits, enum bitset_type type) +{ + size_t bytes = bitset_bytes (type, n_bits); + + bitset bset = obstack_alloc (bobstack, bytes); + memset (bset, 0, bytes); + + return bitset_init (bset, n_bits, type); +} + + +/* Create a bitset of N_BITS and with attribute hints specified by + ATTR. */ +bitset +bitset_create (bitset_bindex n_bits, unsigned attr) +{ + enum bitset_type type = bitset_type_choose (n_bits, attr); + + return bitset_alloc (n_bits, type); +} + + +/* Free bitset BSET. */ +void +bitset_free (bitset bset) +{ + BITSET_FREE_ (bset); + free (bset); +} + + +/* Free bitset BSET allocated on obstack. */ +void +bitset_obstack_free (bitset bset) +{ + BITSET_FREE_ (bset); +} + + +/* Return bitset type. */ +enum bitset_type +bitset_type_get (bitset bset) +{ + enum bitset_type type = BITSET_TYPE_ (bset); + if (type != BITSET_STATS) + return type; + + return bitset_stats_type_get (bset); +} + + +/* Return name of bitset type. */ +const char * +bitset_type_name_get (bitset bset) +{ + enum bitset_type type = bitset_type_get (bset); + + return bitset_type_names[type]; +} + + +/* Find next bit set in SRC starting from and including BITNO. + Return BITSET_BINDEX_MAX if SRC empty. */ +bitset_bindex +bitset_next (bitset src, bitset_bindex bitno) +{ + bitset_bindex next = bitno; + bitset_bindex val; + if (!bitset_list (src, &val, 1, &next)) + return BITSET_BINDEX_MAX; + return val; +} + + +/* Return true if both bitsets are of the same type and size. */ +extern bool +bitset_compatible_p (bitset bset1, bitset bset2) +{ + return BITSET_COMPATIBLE_ (bset1, bset2); +} + + +/* Find previous bit set in SRC starting from and including BITNO. + Return BITSET_BINDEX_MAX if SRC empty. */ +bitset_bindex +bitset_prev (bitset src, bitset_bindex bitno) +{ + bitset_bindex next = bitno; + bitset_bindex val; + if (!bitset_list_reverse (src, &val, 1, &next)) + return BITSET_BINDEX_MAX; + return val; +} + + +/* Find first set bit. */ +bitset_bindex +bitset_first (bitset src) +{ + return bitset_next (src, 0); +} + + +/* Find last set bit. */ +bitset_bindex +bitset_last (bitset src) +{ + return bitset_prev (src, 0); +} + + +/* Is BITNO in SRC the only set bit? */ +bool +bitset_only_set_p (bitset src, bitset_bindex bitno) +{ + bitset_bindex val[2]; + bitset_bindex next = 0; + + if (bitset_list (src, val, 2, &next) != 1) + return false; + return val[0] == bitno; +} + + +/* Print contents of bitset BSET to FILE. */ +static void +bitset_print (FILE *file, bitset bset, bool verbose) +{ + if (verbose) + fprintf (file, "n_bits = %lu, set = {", + (unsigned long) bitset_size (bset)); + + unsigned pos = 30; + bitset_bindex i; + bitset_iterator iter; + BITSET_FOR_EACH (iter, bset, i, 0) + { + if (pos > 70) + { + fprintf (file, "\n"); + pos = 0; + } + + fprintf (file, "%lu ", (unsigned long) i); + pos += 1 + (i >= 10) + (i >= 100); + } + + if (verbose) + fprintf (file, "}\n"); +} + + +/* Dump bitset BSET to FILE. */ +void +bitset_dump (FILE *file, bitset bset) +{ + bitset_print (file, bset, false); +} + + +/* Release memory associated with bitsets. */ +void +bitset_release_memory (void) +{ + lbitset_release_memory (); + ebitset_release_memory (); +} + + +/* Toggle bit BITNO in bitset BSET and the new value of the bit. */ +bool +bitset_toggle_ (bitset bset, bitset_bindex bitno) +{ + /* This routine is for completeness. It could be optimized if + required. */ + if (bitset_test (bset, bitno)) + { + bitset_reset (bset, bitno); + return false; + } + else + { + bitset_set (bset, bitno); + return true; + } +} + + +/* Return number of bits in bitset SRC. */ +bitset_bindex +bitset_size_ (bitset src) +{ + return BITSET_NBITS_ (src); +} + + +/* Return number of bits set in bitset SRC. */ +bitset_bindex +bitset_count_ (bitset src) +{ + bitset_bindex list[BITSET_LIST_SIZE]; + bitset_bindex count = 0; + + /* This could be greatly sped up by adding a count method for each + bitset implementation that uses a direct technique (based on + masks) for counting the number of bits set in a word. */ + + { + bitset_bindex next = 0; + bitset_bindex num; + while ((num = bitset_list (src, list, BITSET_LIST_SIZE, &next))) + count += num; + } + + return count; +} + + +/* DST = SRC. Return true if DST != SRC. + This is a fallback for the case where SRC and DST are different + bitset types. */ +bool +bitset_copy_ (bitset dst, bitset src) +{ + bitset_bindex i; + bitset_iterator iter; + + /* Convert bitset types. We assume that the DST bitset + is large enough to hold the SRC bitset. */ + bitset_zero (dst); + BITSET_FOR_EACH (iter, src, i, 0) + bitset_set (dst, i); + + return true; +} + + +/* This is a fallback for implementations that do not support + four operand operations. */ +static inline bool +bitset_op4_cmp (bitset dst, bitset src1, bitset src2, bitset src3, + enum bitset_ops op) +{ + bool changed = false; + + /* Create temporary bitset. */ + bool stats_enabled_save = bitset_stats_enabled; + bitset_stats_enabled = false; + bitset tmp = bitset_alloc (0, bitset_type_get (dst)); + bitset_stats_enabled = stats_enabled_save; + + switch (op) + { + default: + abort (); + + case BITSET_OP_OR_AND: + bitset_or (tmp, src1, src2); + changed = bitset_and_cmp (dst, src3, tmp); + break; + + case BITSET_OP_AND_OR: + bitset_and (tmp, src1, src2); + changed = bitset_or_cmp (dst, src3, tmp); + break; + + case BITSET_OP_ANDN_OR: + bitset_andn (tmp, src1, src2); + changed = bitset_or_cmp (dst, src3, tmp); + break; + } + + bitset_free (tmp); + return changed; +} + + +/* DST = (SRC1 & SRC2) | SRC3. */ +void +bitset_and_or_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_and_or_cmp_ (dst, src1, src2, src3); +} + + +/* DST = (SRC1 & SRC2) | SRC3. Return non-zero if + DST != (SRC1 & SRC2) | SRC3. */ +bool +bitset_and_or_cmp_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + return bitset_op4_cmp (dst, src1, src2, src3, BITSET_OP_AND_OR); +} + + +/* DST = (SRC1 & ~SRC2) | SRC3. */ +void +bitset_andn_or_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_andn_or_cmp_ (dst, src1, src2, src3); +} + + +/* DST = (SRC1 & ~SRC2) | SRC3. Return non-zero if + DST != (SRC1 & ~SRC2) | SRC3. */ +bool +bitset_andn_or_cmp_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + return bitset_op4_cmp (dst, src1, src2, src3, BITSET_OP_ANDN_OR); +} + + +/* DST = (SRC1 | SRC2) & SRC3. */ +void +bitset_or_and_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_or_and_cmp_ (dst, src1, src2, src3); +} + + +/* DST = (SRC1 | SRC2) & SRC3. Return non-zero if + DST != (SRC1 | SRC2) & SRC3. */ +bool +bitset_or_and_cmp_ (bitset dst, bitset src1, bitset src2, bitset src3) +{ + return bitset_op4_cmp (dst, src1, src2, src3, BITSET_OP_OR_AND); +} + + +/* Function to be called from debugger to print bitset. */ +void +debug_bitset (bitset bset) +{ + if (bset) + bitset_print (stderr, bset, true); +} diff --git a/lib/bitset.h b/lib/bitset.h new file mode 100644 index 0000000000..161ac58eeb --- /dev/null +++ b/lib/bitset.h @@ -0,0 +1,389 @@ +/* Generic bitsets. + + Copyright (C) 2002-2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_H +#define _BITSET_H + +/* This file is the public interface to the bitset abstract data type. + Only use the functions and macros defined in this file. */ + +#include +#if USE_UNLOCKED_IO +# include "unlocked-io.h" +#endif + +#include "bitset/base.h" +#include "obstack.h" + +/* Attributes used to select a bitset implementation. */ +enum bitset_attr {BITSET_FIXED = 1, /* Bitset size fixed. */ + BITSET_VARIABLE = 2, /* Bitset size variable. */ + BITSET_DENSE = 4, /* Bitset dense. */ + BITSET_SPARSE = 8, /* Bitset sparse. */ + BITSET_FRUGAL = 16, /* Prefer most compact. */ + BITSET_GREEDY = 32}; /* Prefer fastest at memory expense. */ + +typedef unsigned bitset_attrs; + +/* The contents of the union should be considered to be private. + While I would like to make this union opaque, it needs to be + visible for the inline bit set/test functions, and for delegation + to the proper implementation. */ +union bitset_union +{ + /* This must be the first member of every other structure that is a + member of this union. */ + struct bbitset_struct b; /* Base bitset data. */ + + struct abitset_struct + { + struct bbitset_struct b; + bitset_word words[1]; /* The array of bits. */ + } a; + + struct ebitset_struct + { + struct bbitset_struct b; + bitset_windex size; /* Number of elements. */ + struct ebitset_elt_struct **elts; /* Expanding array of ptrs to elts. */ + } e; + + struct lbitset_struct + { + struct bbitset_struct b; + struct lbitset_elt_struct *head; /* First element in linked list. */ + struct lbitset_elt_struct *tail; /* Last element in linked list. */ + } l; + + struct bitset_stats_struct + { + struct bbitset_struct b; + bitset bset; + } s; + + struct vbitset_struct + { + struct bbitset_struct b; + bitset_windex size; /* Allocated size of array. */ + } v; +}; + + +/* The contents of this structure should be considered private. + It is used for iterating over set bits. */ +typedef struct +{ + bitset_bindex list[BITSET_LIST_SIZE]; + bitset_bindex next; + bitset_bindex num; + bitset_bindex i; +} bitset_iterator; + + +/* Return bytes required for bitset of desired type and size. */ +size_t bitset_bytes (enum bitset_type, bitset_bindex); + +/* Initialise a bitset with desired type and size. */ +bitset bitset_init (bitset, bitset_bindex, enum bitset_type); + +/* Select an implementation type based on the desired bitset size + and attributes. */ +enum bitset_type bitset_type_choose (bitset_bindex, bitset_attrs); + +/* Create a bitset of desired type and size. The bitset is zeroed. */ +bitset bitset_alloc (bitset_bindex, enum bitset_type); + +/* Free bitset. */ +void bitset_free (bitset); + +/* Create a bitset of desired type and size using an obstack. The + bitset is zeroed. */ +bitset bitset_obstack_alloc (struct obstack *bobstack, + bitset_bindex, enum bitset_type); + +/* Free bitset allocated on obstack. */ +void bitset_obstack_free (bitset); + +/* Create a bitset of desired size and attributes. The bitset is zeroed. */ +bitset bitset_create (bitset_bindex, bitset_attrs); + +/* Return bitset type. */ +enum bitset_type bitset_type_get (bitset); + +/* Return bitset type name. */ +const char *bitset_type_name_get (bitset); + + +/* Set bit BITNO in bitset BSET. */ +static inline void +bitset_set (bitset bset, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + bitset_windex offset = windex - bset->b.cindex; + + if (offset < bset->b.csize) + bset->b.cdata[offset] |= ((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); + else + BITSET_SET_ (bset, bitno); +} + + +/* Reset bit BITNO in bitset BSET. */ +static inline void +bitset_reset (bitset bset, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + bitset_windex offset = windex - bset->b.cindex; + + if (offset < bset->b.csize) + bset->b.cdata[offset] &= ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); + else + BITSET_RESET_ (bset, bitno); +} + + +/* Test bit BITNO in bitset BSET. */ +static inline bool +bitset_test (bitset bset, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + bitset_windex offset = windex - bset->b.cindex; + + if (offset < bset->b.csize) + return (bset->b.cdata[offset] >> (bitno % BITSET_WORD_BITS)) & 1; + else + return BITSET_TEST_ (bset, bitno); +} + + +/* Toggle bit BITNO in bitset BSET and return non-zero if now set. */ +#define bitset_toggle(bset, bitno) BITSET_TOGGLE_ (bset, bitno) + +/* Return size in bits of bitset SRC. */ +#define bitset_size(SRC) BITSET_SIZE_ (SRC) + +/* Change size of bitset. */ +void bitset_resize (bitset, bitset_bindex); + +/* Return number of bits set in bitset SRC. */ +#define bitset_count(SRC) BITSET_COUNT_ (SRC) + + +/* Return SRC == 0. */ +#define bitset_empty_p(SRC) BITSET_EMPTY_P_ (SRC) + +/* DST = ~0. */ +#define bitset_ones(DST) BITSET_ONES_ (DST) + +/* DST = 0. */ +#define bitset_zero(DST) BITSET_ZERO_ (DST) + + + +/* DST = SRC. */ +#define bitset_copy(DST, SRC) BITSET_COPY_ (DST, SRC) + +/* Return DST & SRC == 0. */ +#define bitset_disjoint_p(DST, SRC) BITSET_DISJOINT_P_ (DST, SRC) + +/* Return DST == SRC. */ +#define bitset_equal_p(DST, SRC) BITSET_EQUAL_P_ (DST, SRC) + +/* DST = ~SRC. */ +#define bitset_not(DST, SRC) BITSET_NOT_ (DST, SRC) + +/* Return DST == DST | SRC. */ +#define bitset_subset_p(DST, SRC) BITSET_SUBSET_P_ (DST, SRC) + + + +/* DST = SRC1 & SRC2. */ +#define bitset_and(DST, SRC1, SRC2) BITSET_AND_ (DST, SRC1, SRC2) + +/* DST = SRC1 & SRC2. Return non-zero if DST != SRC1 & SRC2. */ +#define bitset_and_cmp(DST, SRC1, SRC2) BITSET_AND_CMP_ (DST, SRC1, SRC2) + +/* DST = SRC1 & ~SRC2. */ +#define bitset_andn(DST, SRC1, SRC2) BITSET_ANDN_ (DST, SRC1, SRC2) + +/* DST = SRC1 & ~SRC2. Return non-zero if DST != SRC1 & ~SRC2. */ +#define bitset_andn_cmp(DST, SRC1, SRC2) BITSET_ANDN_CMP_ (DST, SRC1, SRC2) + +/* DST = SRC1 | SRC2. */ +#define bitset_or(DST, SRC1, SRC2) BITSET_OR_ (DST, SRC1, SRC2) + +/* DST = SRC1 | SRC2. Return non-zero if DST != SRC1 | SRC2. */ +#define bitset_or_cmp(DST, SRC1, SRC2) BITSET_OR_CMP_ (DST, SRC1, SRC2) + +/* DST = SRC1 ^ SRC2. */ +#define bitset_xor(DST, SRC1, SRC2) BITSET_XOR_ (DST, SRC1, SRC2) + +/* DST = SRC1 ^ SRC2. Return non-zero if DST != SRC1 ^ SRC2. */ +#define bitset_xor_cmp(DST, SRC1, SRC2) BITSET_XOR_CMP_ (DST, SRC1, SRC2) + + + +/* DST = (SRC1 & SRC2) | SRC3. */ +#define bitset_and_or(DST, SRC1, SRC2, SRC3) \ + BITSET_AND_OR_ (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 & SRC2) | SRC3. Return non-zero if + DST != (SRC1 & SRC2) | SRC3. */ +#define bitset_and_or_cmp(DST, SRC1, SRC2, SRC3) \ + BITSET_AND_OR_CMP_ (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 & ~SRC2) | SRC3. */ +#define bitset_andn_or(DST, SRC1, SRC2, SRC3) \ + BITSET_ANDN_OR_ (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 & ~SRC2) | SRC3. Return non-zero if + DST != (SRC1 & ~SRC2) | SRC3. */ +#define bitset_andn_or_cmp(DST, SRC1, SRC2, SRC3) \ + BITSET_ANDN_OR_CMP_ (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 | SRC2) & SRC3. */ +#define bitset_or_and(DST, SRC1, SRC2, SRC3)\ + BITSET_OR_AND_ (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 | SRC2) & SRC3. Return non-zero if + DST != (SRC1 | SRC2) & SRC3. */ +#define bitset_or_and_cmp(DST, SRC1, SRC2, SRC3)\ + BITSET_OR_AND_CMP_ (DST, SRC1, SRC2, SRC3) + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT. Return with actual number of bits found and with *NEXT + indicating where search stopped. */ +#define bitset_list(BSET, LIST, NUM, NEXT) \ + BITSET_LIST_ (BSET, LIST, NUM, NEXT) + +/* Find reverse list of up to NUM bits set in BSET starting from and + including NEXT. Return with actual number of bits found and with + *NEXT indicating where search stopped. */ +#define bitset_list_reverse(BSET, LIST, NUM, NEXT) \ + BITSET_LIST_REVERSE_ (BSET, LIST, NUM, NEXT) + +/* Return true if both bitsets are of the same type and size. */ +bool bitset_compatible_p (bitset bset1, bitset bset2); + +/* Find next set bit from the given bit index. */ +bitset_bindex bitset_next (bitset, bitset_bindex); + +/* Find previous set bit from the given bit index. */ +bitset_bindex bitset_prev (bitset, bitset_bindex); + +/* Find first set bit. */ +bitset_bindex bitset_first (bitset); + +/* Find last set bit. */ +bitset_bindex bitset_last (bitset); + +/* Return nonzero if this is the only set bit. */ +bool bitset_only_set_p (bitset, bitset_bindex); + +/* Dump bitset. */ +void bitset_dump (FILE *, bitset); + +/* Loop over all elements of BSET, starting with MIN, setting INDEX + to the index of each set bit. For example, the following will print + the bits set in a bitset: + + bitset_bindex i; + bitset_iterator iter; + + BITSET_FOR_EACH (iter, src, i, 0) + printf ("%lu ", (unsigned long) i); +*/ +#define BITSET_FOR_EACH(ITER, BSET, INDEX, MIN) \ + for (ITER.next = (MIN), ITER.num = BITSET_LIST_SIZE; \ + (ITER.num == BITSET_LIST_SIZE) \ + && (ITER.num = bitset_list (BSET, ITER.list, \ + BITSET_LIST_SIZE, &ITER.next));) \ + for (ITER.i = 0; \ + ITER.i < ITER.num && ((INDEX) = ITER.list[ITER.i], 1); \ + ITER.i++) + + +/* Loop over all elements of BSET, in reverse order starting with + MIN, setting INDEX to the index of each set bit. For example, the + following will print the bits set in a bitset in reverse order: + + bitset_bindex i; + bitset_iterator iter; + + BITSET_FOR_EACH_REVERSE (iter, src, i, 0) + printf ("%lu ", (unsigned long) i); +*/ +#define BITSET_FOR_EACH_REVERSE(ITER, BSET, INDEX, MIN) \ + for (ITER.next = (MIN), ITER.num = BITSET_LIST_SIZE; \ + (ITER.num == BITSET_LIST_SIZE) \ + && (ITER.num = bitset_list_reverse (BSET, ITER.list, \ + BITSET_LIST_SIZE, &ITER.next));) \ + for (ITER.i = 0; \ + ITER.i < ITER.num && ((INDEX) = ITER.list[ITER.i], 1); \ + ITER.i++) + + +/* Define set operations in terms of logical operations. */ + +#define bitset_diff(DST, SRC1, SRC2) bitset_andn (DST, SRC1, SRC2) +#define bitset_diff_cmp(DST, SRC1, SRC2) bitset_andn_cmp (DST, SRC1, SRC2) + +#define bitset_intersection(DST, SRC1, SRC2) bitset_and (DST, SRC1, SRC2) +#define bitset_intersection_cmp(DST, SRC1, SRC2) bitset_and_cmp (DST, SRC1, SRC2) + +#define bitset_union(DST, SRC1, SRC2) bitset_or (DST, SRC1, SRC2) +#define bitset_union_cmp(DST, SRC1, SRC2) bitset_or_cmp (DST, SRC1, SRC2) + +/* Symmetrical difference. */ +#define bitset_symdiff(DST, SRC1, SRC2) bitset_xor (DST, SRC1, SRC2) +#define bitset_symdiff_cmp(DST, SRC1, SRC2) bitset_xor_cmp (DST, SRC1, SRC2) + +/* Union of difference. */ +#define bitset_diff_union(DST, SRC1, SRC2, SRC3) \ + bitset_andn_or (DST, SRC1, SRC2, SRC3) +#define bitset_diff_union_cmp(DST, SRC1, SRC2, SRC3) \ + bitset_andn_or_cmp (DST, SRC1, SRC2, SRC3) + + +/* Release any memory tied up with bitsets. */ +void bitset_release_memory (void); + +/* Enable bitset stats gathering. */ +void bitset_stats_enable (void); + +/* Disable bitset stats gathering. */ +void bitset_stats_disable (void); + +/* Read bitset stats file of accummulated stats. */ +void bitset_stats_read (const char *file_name); + +/* Write bitset stats file of accummulated stats. */ +void bitset_stats_write (const char *file_name); + +/* Dump bitset stats. */ +void bitset_stats_dump (FILE *); + +/* Function to debug bitset from debugger. */ +void debug_bitset (bitset); + +/* Function to debug bitset stats from debugger. */ +void debug_bitset_stats (void); + +#endif /* _BITSET_H */ diff --git a/lib/bitset/array.c b/lib/bitset/array.c new file mode 100644 index 0000000000..affca177eb --- /dev/null +++ b/lib/bitset/array.c @@ -0,0 +1,772 @@ +/* Array bitsets. + + Copyright (C) 2002-2003, 2006, 2009-2015, 2018 Free Software + Foundation, Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "bitset/array.h" +#include +#include +#include + +/* This file implements fixed size bitsets stored as an array + of words. Any unused bits in the last word must be zero. */ + +#define ABITSET_N_WORDS(N) (((N) + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS) +#define ABITSET_WORDS(X) ((X)->a.words) + + +static bitset_bindex +abitset_resize (bitset src, bitset_bindex size) +{ + /* These bitsets have a fixed size. */ + if (BITSET_SIZE_ (src) != size) + abort (); + + return size; +} + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +abitset_small_list (bitset src, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_word word = ABITSET_WORDS (src)[0]; + + /* Short circuit common case. */ + if (!word) + return 0; + + bitset_windex size = BITSET_SIZE_ (src); + bitset_bindex bitno = *next; + if (bitno >= size) + return 0; + + word >>= bitno; + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + bitset_bindex count; + if (num >= BITSET_WORD_BITS) + { + for (count = 0; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + else + { + for (count = 0; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + bitno++; + break; + } + } + word >>= 1; + } + } + + *next = bitno; + return count; +} + + +/* Set bit BITNO in bitset DST. */ +static void +abitset_set (bitset dst ATTRIBUTE_UNUSED, bitset_bindex bitno ATTRIBUTE_UNUSED) +{ + /* This should never occur for abitsets since we should always hit + the cache. It is likely someone is trying to access outside the + bounds of the bitset. */ + abort (); +} + + +/* Reset bit BITNO in bitset DST. */ +static void +abitset_reset (bitset dst ATTRIBUTE_UNUSED, + bitset_bindex bitno ATTRIBUTE_UNUSED) +{ + /* This should never occur for abitsets since we should always hit + the cache. It is likely someone is trying to access outside the + bounds of the bitset. Since the bit is zero anyway, let it pass. */ +} + + +/* Test bit BITNO in bitset SRC. */ +static bool +abitset_test (bitset src ATTRIBUTE_UNUSED, + bitset_bindex bitno ATTRIBUTE_UNUSED) +{ + /* This should never occur for abitsets since we should always + hit the cache. */ + return false; +} + + +/* Find list of up to NUM bits set in BSET in reverse order, starting + from and including NEXT and store in array LIST. Return with + actual number of bits found and with *NEXT indicating where search + stopped. */ +static bitset_bindex +abitset_list_reverse (bitset src, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_bindex rbitno = *next; + bitset_word *srcp = ABITSET_WORDS (src); + bitset_bindex n_bits = BITSET_SIZE_ (src); + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + if (rbitno >= n_bits) + return 0; + + bitset_bindex count = 0; + + bitset_bindex bitno = n_bits - (rbitno + 1); + + bitset_windex windex = bitno / BITSET_WORD_BITS; + unsigned bitcnt = bitno % BITSET_WORD_BITS; + bitset_bindex bitoff = windex * BITSET_WORD_BITS; + + do + { + bitset_word word = srcp[windex] << (BITSET_WORD_BITS - 1 - bitcnt); + for (; word; bitcnt--) + { + if (word & BITSET_MSB) + { + list[count++] = bitoff + bitcnt; + if (count >= num) + { + *next = n_bits - (bitoff + bitcnt); + return count; + } + } + word <<= 1; + } + bitoff -= BITSET_WORD_BITS; + bitcnt = BITSET_WORD_BITS - 1; + } + while (windex--); + + *next = n_bits - (bitoff + 1); + return count; +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +abitset_list (bitset src, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_windex windex; + bitset_bindex bitoff; + bitset_windex size = src->b.csize; + bitset_word *srcp = ABITSET_WORDS (src); + + bitset_bindex bitno = *next; + + bitset_bindex count = 0; + if (!bitno) + { + /* Many bitsets are zero, so make this common case fast. */ + for (windex = 0; windex < size && !srcp[windex]; windex++) + continue; + if (windex >= size) + return 0; + + /* If num is 1, we could speed things up with a binary search + of the current word. */ + + bitoff = windex * BITSET_WORD_BITS; + } + else + { + if (bitno >= BITSET_SIZE_ (src)) + return 0; + + windex = bitno / BITSET_WORD_BITS; + bitno = bitno % BITSET_WORD_BITS; + + if (bitno) + { + /* Handle the case where we start within a word. + Most often, this is executed with large bitsets + with many set bits where we filled the array + on the previous call to this function. */ + + bitoff = windex * BITSET_WORD_BITS; + bitset_word word = srcp[windex] >> bitno; + for (bitno = bitoff + bitno; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + windex++; + } + bitoff = windex * BITSET_WORD_BITS; + } + + for (; windex < size; windex++, bitoff += BITSET_WORD_BITS) + { + bitset_word word = srcp[windex]; + if (!word) + continue; + + if ((count + BITSET_WORD_BITS) < num) + { + for (bitno = bitoff; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + else + { + for (bitno = bitoff; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + } + } + + *next = bitoff; + return count; +} + + +/* Ensure that any unused bits within the last word are clear. */ +static inline void +abitset_unused_clear (bitset dst) +{ + unsigned last_bit = BITSET_SIZE_ (dst) % BITSET_WORD_BITS; + if (last_bit) + ABITSET_WORDS (dst)[dst->b.csize - 1] &= + ((bitset_word) 1 << last_bit) - 1; +} + + +static void +abitset_ones (bitset dst) +{ + bitset_word *dstp = ABITSET_WORDS (dst); + size_t bytes = sizeof (bitset_word) * dst->b.csize; + + memset (dstp, -1, bytes); + abitset_unused_clear (dst); +} + + +static void +abitset_zero (bitset dst) +{ + bitset_word *dstp = ABITSET_WORDS (dst); + size_t bytes = sizeof (bitset_word) * dst->b.csize; + + memset (dstp, 0, bytes); +} + + +static bool +abitset_empty_p (bitset dst) +{ + bitset_word *dstp = ABITSET_WORDS (dst); + + for (bitset_windex i = 0; i < dst->b.csize; i++) + if (dstp[i]) + return false; + return true; +} + + +static void +abitset_copy1 (bitset dst, bitset src) +{ + bitset_word *srcp = ABITSET_WORDS (src); + bitset_word *dstp = ABITSET_WORDS (dst); + if (srcp == dstp) + return; + bitset_windex size = dst->b.csize; + memcpy (dstp, srcp, sizeof (bitset_word) * size); +} + + +static void +abitset_not (bitset dst, bitset src) +{ + bitset_word *srcp = ABITSET_WORDS (src); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = ~(*srcp++); + abitset_unused_clear (dst); +} + + +static bool +abitset_equal_p (bitset dst, bitset src) +{ + bitset_word *srcp = ABITSET_WORDS (src); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + if (*srcp++ != *dstp++) + return false; + return true; +} + + +static bool +abitset_subset_p (bitset dst, bitset src) +{ + bitset_word *srcp = ABITSET_WORDS (src); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++, srcp++) + if (*dstp != (*srcp | *dstp)) + return false; + return true; +} + + +static bool +abitset_disjoint_p (bitset dst, bitset src) +{ + bitset_word *srcp = ABITSET_WORDS (src); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + if (*srcp++ & *dstp++) + return false; + return true; +} + + +static void +abitset_and (bitset dst, bitset src1, bitset src2) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = *src1p++ & *src2p++; +} + + +static bool +abitset_and_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = *src1p++ & *src2p++; + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_andn (bitset dst, bitset src1, bitset src2) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = *src1p++ & ~(*src2p++); +} + + +static bool +abitset_andn_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = *src1p++ & ~(*src2p++); + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_or (bitset dst, bitset src1, bitset src2) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = *src1p++ | *src2p++; +} + + +static bool +abitset_or_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = *src1p++ | *src2p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_xor (bitset dst, bitset src1, bitset src2) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = *src1p++ ^ *src2p++; +} + + +static bool +abitset_xor_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = *src1p++ ^ *src2p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_and_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = (*src1p++ & *src2p++) | *src3p++; +} + + +static bool +abitset_and_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ & *src2p++) | *src3p++; + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_andn_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = (*src1p++ & ~(*src2p++)) | *src3p++; +} + + +static bool +abitset_andn_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ & ~(*src2p++)) | *src3p++; + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_or_and (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++) + *dstp++ = (*src1p++ | *src2p++) & *src3p++; +} + + +static bool +abitset_or_and_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + bool changed = false; + bitset_word *src1p = ABITSET_WORDS (src1); + bitset_word *src2p = ABITSET_WORDS (src2); + bitset_word *src3p = ABITSET_WORDS (src3); + bitset_word *dstp = ABITSET_WORDS (dst); + bitset_windex size = dst->b.csize; + + for (bitset_windex i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ | *src2p++) & *src3p++; + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +abitset_copy (bitset dst, bitset src) +{ + if (BITSET_COMPATIBLE_ (dst, src)) + abitset_copy1 (dst, src); + else + bitset_copy_ (dst, src); +} + + +/* Vector of operations for single word bitsets. */ +struct bitset_vtable abitset_small_vtable = { + abitset_set, + abitset_reset, + bitset_toggle_, + abitset_test, + abitset_resize, + bitset_size_, + bitset_count_, + abitset_empty_p, + abitset_ones, + abitset_zero, + abitset_copy, + abitset_disjoint_p, + abitset_equal_p, + abitset_not, + abitset_subset_p, + abitset_and, + abitset_and_cmp, + abitset_andn, + abitset_andn_cmp, + abitset_or, + abitset_or_cmp, + abitset_xor, + abitset_xor_cmp, + abitset_and_or, + abitset_and_or_cmp, + abitset_andn_or, + abitset_andn_or_cmp, + abitset_or_and, + abitset_or_and_cmp, + abitset_small_list, + abitset_list_reverse, + NULL, + BITSET_ARRAY +}; + + +/* Vector of operations for multiple word bitsets. */ +struct bitset_vtable abitset_vtable = { + abitset_set, + abitset_reset, + bitset_toggle_, + abitset_test, + abitset_resize, + bitset_size_, + bitset_count_, + abitset_empty_p, + abitset_ones, + abitset_zero, + abitset_copy, + abitset_disjoint_p, + abitset_equal_p, + abitset_not, + abitset_subset_p, + abitset_and, + abitset_and_cmp, + abitset_andn, + abitset_andn_cmp, + abitset_or, + abitset_or_cmp, + abitset_xor, + abitset_xor_cmp, + abitset_and_or, + abitset_and_or_cmp, + abitset_andn_or, + abitset_andn_or_cmp, + abitset_or_and, + abitset_or_and_cmp, + abitset_list, + abitset_list_reverse, + NULL, + BITSET_ARRAY +}; + + +size_t +abitset_bytes (bitset_bindex n_bits) +{ + size_t header_size = offsetof (union bitset_union, a.words); + struct bitset_align_struct { char a; union bitset_union b; }; + size_t bitset_alignment = offsetof (struct bitset_align_struct, b); + + bitset_windex size = ABITSET_N_WORDS (n_bits); + size_t bytes = header_size + size * sizeof (bitset_word); + + /* Align the size properly for a vector of abitset objects. */ + if (header_size % bitset_alignment != 0 + || sizeof (bitset_word) % bitset_alignment != 0) + { + bytes += bitset_alignment - 1; + bytes -= bytes % bitset_alignment; + } + + return bytes; +} + + +bitset +abitset_init (bitset bset, bitset_bindex n_bits) +{ + bitset_windex size = ABITSET_N_WORDS (n_bits); + BITSET_NBITS_ (bset) = n_bits; + + /* Use optimized routines if bitset fits within a single word. + There is probably little merit if using caching since + the small bitset will always fit in the cache. */ + bset->b.vtable = size == 1 ? &abitset_small_vtable : &abitset_vtable; + bset->b.cindex = 0; + bset->b.csize = size; + bset->b.cdata = ABITSET_WORDS (bset); + return bset; +} diff --git a/lib/bitset/array.h b/lib/bitset/array.h new file mode 100644 index 0000000000..e7f737291f --- /dev/null +++ b/lib/bitset/array.h @@ -0,0 +1,30 @@ +/* Functions to support abitsets. + + Copyright (C) 2002, 2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_ARRAY_H +#define _BITSET_ARRAY_H + +#include "bitset.h" + +size_t abitset_bytes (bitset_bindex); + +bitset abitset_init (bitset, bitset_bindex); + +#endif diff --git a/lib/bitset/base.h b/lib/bitset/base.h new file mode 100644 index 0000000000..b974078be5 --- /dev/null +++ b/lib/bitset/base.h @@ -0,0 +1,312 @@ +/* Base bitset stuff. + + Copyright (C) 2002-2004, 2006, 2009-2015, 2018 Free Software + Foundation, Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_BASE_H +#define _BITSET_BASE_H + +#include +#include +#include + +#include "xalloc.h" + +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) +# define __attribute__(x) +# endif +#endif + +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) + +/* Currently we support five flavours of bitsets: + BITSET_ARRAY: Array of bits (fixed size, fast for dense bitsets). + Memory for bit array and bitset structure allocated + contiguously. + BITSET_LIST: Linked list of arrays of bits (variable size, least storage + for large very sparse sets). + BITSET_TABLE: Expandable table of pointers to arrays of bits + (variable size, less storage for large sparse sets). + Faster than BITSET_LIST for random access. + BITSET_VARRAY: Variable array of bits (variable size, fast for + dense bitsets). + BITSET_STATS: Wrapper bitset for internal use only. Used for gathering + statistics and/or better run-time checking. +*/ +enum bitset_type {BITSET_ARRAY, BITSET_LIST, BITSET_TABLE, BITSET_VARRAY, + BITSET_TYPE_NUM, BITSET_STATS}; +#define BITSET_TYPE_NAMES {"abitset", "lbitset", "ebitset", "vbitset"} + +extern const char * const bitset_type_names[]; + +enum bitset_alloc_type {BITSET_MALLOC, BITSET_OBALLOC}; + +/* Data type used to store a word of bits. */ +typedef unsigned long bitset_word; +#define BITSET_WORD_BITS ((unsigned) (CHAR_BIT * sizeof (bitset_word))) + +/* Bit index. In theory we might need a type wider than size_t, but + in practice we lose at most a factor of CHAR_BIT by going with + size_t, and that is good enough. If this type is changed to be + wider than size_t, the code needs to be modified to check for + overflow when converting bit counts to byte or word counts. + The bit and word index types must be unsigned. */ +typedef size_t bitset_bindex; + +/* Word index. */ +typedef size_t bitset_windex; + +/* Maximum values for commonly-used unsigned types. BITSET_SIZE_MAX + always equals SIZE_MAX, but some older systems lack SIZE_MAX. */ +#define BITSET_BINDEX_MAX ((bitset_bindex) -1) + +/* Limit max word index to the maximum value of a signed integer + to simplify cache disabling. */ +#define BITSET_WINDEX_MAX (((bitset_windex) -1) >> 1) +#define BITSET_SIZE_MAX ((size_t) -1) + +#define BITSET_MSB ((bitset_word) 1 << (BITSET_WORD_BITS - 1)) + +#define BITSET_LIST_SIZE 1024 + +enum bitset_ops {BITSET_OP_ZERO, BITSET_OP_ONES, + BITSET_OP_COPY, BITSET_OP_NOT, + BITSET_OP_EMPTY_P, BITSET_OP_EQUAL_P, + BITSET_OP_SUBSET_P, BITSET_OP_DISJOINT_P, + BITSET_OP_AND, BITSET_OP_OR, BITSET_OP_XOR, BITSET_OP_ANDN, + BITSET_OP_OR_AND, BITSET_OP_AND_OR, BITSET_OP_ANDN_OR}; + +struct bbitset_struct +{ + const struct bitset_vtable *vtable; + bitset_windex cindex; /* Cache word index. */ + bitset_windex csize; /* Cache size in words. */ + bitset_word *cdata; /* Cache data pointer. */ + bitset_bindex n_bits; /* Number of bits. */ + /* Perhaps we could sacrifice another word to indicate + that the bitset is known to be zero, that a bit has been set + in the cache, and that a bit has been cleared in the cache. + This would speed up some of the searches but slightly slow down + bit set/reset operations of cached bits. */ +}; + + +typedef union bitset_union *bitset; + + +/* Private accessor macros to bitset structure. */ +#define BITSET_VTABLE_(SRC) (SRC)->b.vtable +#define BITSET_CINDEX_(SRC) (SRC)->b.cindex +#define BITSET_CDATA_(SRC) (SRC)->b.cdata +#define BITSET_CSIZE_(SRC) (SRC)->b.csize +#define BITSET_NBITS_(SRC) (SRC)->b.n_bits + + +/* The contents of this structure should be considered private. */ +struct bitset_vtable +{ + void (*set) (bitset, bitset_bindex); + void (*reset) (bitset, bitset_bindex); + bool (*toggle) (bitset, bitset_bindex); + bool (*test) (bitset, bitset_bindex); + bitset_bindex (*resize) (bitset, bitset_bindex); + bitset_bindex (*size) (bitset); + bitset_bindex (*count) (bitset); + + bool (*empty_p) (bitset); + void (*ones) (bitset); + void (*zero) (bitset); + + void (*copy) (bitset, bitset); + bool (*disjoint_p) (bitset, bitset); + bool (*equal_p) (bitset, bitset); + void (*not_) (bitset, bitset); + bool (*subset_p) (bitset, bitset); + + void (*and_) (bitset, bitset, bitset); + bool (*and_cmp) (bitset, bitset, bitset); + void (*andn) (bitset, bitset, bitset); + bool (*andn_cmp) (bitset, bitset, bitset); + void (*or_) (bitset, bitset, bitset); + bool (*or_cmp) (bitset, bitset, bitset); + void (*xor_) (bitset, bitset, bitset); + bool (*xor_cmp) (bitset, bitset, bitset); + + void (*and_or) (bitset, bitset, bitset, bitset); + bool (*and_or_cmp) (bitset, bitset, bitset, bitset); + void (*andn_or) (bitset, bitset, bitset, bitset); + bool (*andn_or_cmp) (bitset, bitset, bitset, bitset); + void (*or_and) (bitset, bitset, bitset, bitset); + bool (*or_and_cmp) (bitset, bitset, bitset, bitset); + + bitset_bindex (*list) (bitset, bitset_bindex *, bitset_bindex, + bitset_bindex *); + bitset_bindex (*list_reverse) (bitset, bitset_bindex *, bitset_bindex, + bitset_bindex *); + void (*free) (bitset); + enum bitset_type type; +}; + +#define BITSET_COMPATIBLE_(BSET1, BSET2) \ +((BSET1)->b.vtable == (BSET2)->b.vtable) + +#define BITSET_CHECK2_(DST, SRC) \ +if (!BITSET_COMPATIBLE_ (DST, SRC)) abort (); + +#define BITSET_CHECK3_(DST, SRC1, SRC2) \ +if (!BITSET_COMPATIBLE_ (DST, SRC1) \ + || !BITSET_COMPATIBLE_ (DST, SRC2)) abort (); + +#define BITSET_CHECK4_(DST, SRC1, SRC2, SRC3) \ +if (!BITSET_COMPATIBLE_ (DST, SRC1) || !BITSET_COMPATIBLE_ (DST, SRC2) \ + || !BITSET_COMPATIBLE_ (DST, SRC3)) abort (); + + +/* Redefine number of bits in bitset DST. */ +#define BITSET_RESIZE_(DST, SIZE) (DST)->b.vtable->resize (DST, SIZE) + +/* Return size in bits of bitset SRC. */ +#define BITSET_SIZE_(SRC) (SRC)->b.vtable->size (SRC) + +/* Return number of bits set in bitset SRC. */ +#define BITSET_COUNT_(SRC) (SRC)->b.vtable->count (SRC) + +/* Return type of bitset SRC. */ +#define BITSET_TYPE_(DST) (DST)->b.vtable->type + +/* Set bit BITNO in bitset DST. */ +#define BITSET_SET_(DST, BITNO) (DST)->b.vtable->set (DST, BITNO) + +/* Reset bit BITNO in bitset DST. */ +#define BITSET_RESET_(DST, BITNO) (DST)->b.vtable->reset (DST, BITNO) + +/* Toggle bit BITNO in bitset DST. */ +#define BITSET_TOGGLE_(DST, BITNO) (DST)->b.vtable->toggle (DST, BITNO) + +/* Return non-zero if bit BITNO in bitset SRC is set. */ +#define BITSET_TEST_(SRC, BITNO) (SRC)->b.vtable->test (SRC, BITNO) + +/* Free bitset SRC. */ +#define BITSET_FREE_(SRC)\ + ((SRC)->b.vtable->free ? (SRC)->b.vtable->free (SRC) :(void)0) + + +/* Return SRC == 0. */ +#define BITSET_EMPTY_P_(SRC) (SRC)->b.vtable->empty_p (SRC) + +/* DST = ~0. */ +#define BITSET_ONES_(DST) (DST)->b.vtable->ones (DST) + +/* DST = 0. */ +#define BITSET_ZERO_(DST) (DST)->b.vtable->zero (DST) + + + +/* DST = SRC. */ +#define BITSET_COPY_(DST, SRC) (SRC)->b.vtable->copy (DST, SRC) + +/* Return DST & SRC == 0. */ +#define BITSET_DISJOINT_P_(DST, SRC) (SRC)->b.vtable->disjoint_p (DST, SRC) + +/* Return DST == SRC. */ +#define BITSET_EQUAL_P_(DST, SRC) (SRC)->b.vtable->equal_p (DST, SRC) + +/* DST = ~SRC. */ +#define BITSET_NOT_(DST, SRC) (SRC)->b.vtable->not_ (DST, SRC) + +/* Return DST == DST | SRC. */ +#define BITSET_SUBSET_P_(DST, SRC) (SRC)->b.vtable->subset_p (DST, SRC) + + +/* DST = SRC1 & SRC2. */ +#define BITSET_AND_(DST, SRC1, SRC2) (SRC1)->b.vtable->and_ (DST, SRC1, SRC2) +#define BITSET_AND_CMP_(DST, SRC1, SRC2) (SRC1)->b.vtable->and_cmp (DST, SRC1, SRC2) + +/* DST = SRC1 & ~SRC2. */ +#define BITSET_ANDN_(DST, SRC1, SRC2) (SRC1)->b.vtable->andn (DST, SRC1, SRC2) +#define BITSET_ANDN_CMP_(DST, SRC1, SRC2) (SRC1)->b.vtable->andn_cmp (DST, SRC1, SRC2) + +/* DST = SRC1 | SRC2. */ +#define BITSET_OR_(DST, SRC1, SRC2) (SRC1)->b.vtable->or_ (DST, SRC1, SRC2) +#define BITSET_OR_CMP_(DST, SRC1, SRC2) (SRC1)->b.vtable->or_cmp (DST, SRC1, SRC2) + +/* DST = SRC1 ^ SRC2. */ +#define BITSET_XOR_(DST, SRC1, SRC2) (SRC1)->b.vtable->xor_ (DST, SRC1, SRC2) +#define BITSET_XOR_CMP_(DST, SRC1, SRC2) (SRC1)->b.vtable->xor_cmp (DST, SRC1, SRC2) + + + +/* DST = (SRC1 & SRC2) | SRC3. Return non-zero if + DST != (SRC1 & SRC2) | SRC3. */ +#define BITSET_AND_OR_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->and_or (DST, SRC1, SRC2, SRC3) +#define BITSET_AND_OR_CMP_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->and_or_cmp (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 & ~SRC2) | SRC3. Return non-zero if + DST != (SRC1 & ~SRC2) | SRC3. */ +#define BITSET_ANDN_OR_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->andn_or (DST, SRC1, SRC2, SRC3) +#define BITSET_ANDN_OR_CMP_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->andn_or_cmp (DST, SRC1, SRC2, SRC3) + +/* DST = (SRC1 | SRC2) & SRC3. Return non-zero if + DST != (SRC1 | SRC2) & SRC3. */ +#define BITSET_OR_AND_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->or_and (DST, SRC1, SRC2, SRC3) +#define BITSET_OR_AND_CMP_(DST, SRC1, SRC2, SRC3) \ + (SRC1)->b.vtable->or_and_cmp (DST, SRC1, SRC2, SRC3) + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT. Return with actual number of bits found and with *NEXT + indicating where search stopped. */ +#define BITSET_LIST_(BSET, LIST, NUM, NEXT) \ + (BSET)->b.vtable->list (BSET, LIST, NUM, NEXT) + +/* Find reverse list of up to NUM bits set in BSET starting from and + including NEXT. Return with actual number of bits found and with + *NEXT indicating where search stopped. */ +#define BITSET_LIST_REVERSE_(BSET, LIST, NUM, NEXT) \ + (BSET)->b.vtable->list_reverse (BSET, LIST, NUM, NEXT) + + +/* Private functions for bitset implementations. */ + +bool bitset_toggle_ (bitset, bitset_bindex); + +bitset_bindex bitset_count_ (bitset); + +bitset_bindex bitset_size_ (bitset); + +bool bitset_copy_ (bitset, bitset); + +void bitset_and_or_ (bitset, bitset, bitset, bitset); + +bool bitset_and_or_cmp_ (bitset, bitset, bitset, bitset); + +void bitset_andn_or_ (bitset, bitset, bitset, bitset); + +bool bitset_andn_or_cmp_ (bitset, bitset, bitset, bitset); + +void bitset_or_and_ (bitset, bitset, bitset, bitset); + +bool bitset_or_and_cmp_ (bitset, bitset, bitset, bitset); + +#endif /* _BBITSET_H */ diff --git a/lib/bitset/expandable.c b/lib/bitset/expandable.c new file mode 100644 index 0000000000..e831fdc213 --- /dev/null +++ b/lib/bitset/expandable.c @@ -0,0 +1,1229 @@ +/* Functions to support expandable bitsets. + + Copyright (C) 2002-2006, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "bitset/expandable.h" + +#include +#include + +#include "obstack.h" + +/* This file implements expandable bitsets. These bitsets can be of + arbitrary length and are more efficient than arrays of bits for + large sparse sets. + + Empty elements are represented by a NULL pointer in the table of + element pointers. An alternative is to point to a special zero + element. Similarly, we could represent an all 1's element with + another special ones element pointer. + + Bitsets are commonly empty so we need to ensure that this special + case is fast. A zero bitset is indicated when cdata is 0. This is + conservative since cdata may be non zero and the bitset may still + be zero. + + The bitset cache can be disabled either by setting cindex to + BITSET_WINDEX_MAX or by setting csize to 0. Here + we use the former approach since cindex needs to be updated whenever + cdata is changed. +*/ + + +/* Number of words to use for each element. */ +#define EBITSET_ELT_WORDS 2 + +/* Number of bits stored in each element. */ +#define EBITSET_ELT_BITS \ + ((unsigned) (EBITSET_ELT_WORDS * BITSET_WORD_BITS)) + +/* Ebitset element. We use an array of bits. */ +typedef struct ebitset_elt_struct +{ + union + { + bitset_word words[EBITSET_ELT_WORDS]; /* Bits that are set. */ + struct ebitset_elt_struct *next; + } + u; +} +ebitset_elt; + + +typedef ebitset_elt *ebitset_elts; + + +/* Number of elements to initially allocate. */ + +#ifndef EBITSET_INITIAL_SIZE +# define EBITSET_INITIAL_SIZE 2 +#endif + + +enum ebitset_find_mode + { EBITSET_FIND, EBITSET_CREATE, EBITSET_SUBST }; + +static ebitset_elt ebitset_zero_elts[1]; /* Elements of all zero bits. */ + +/* Obstack to allocate bitset elements from. */ +static struct obstack ebitset_obstack; +static bool ebitset_obstack_init = false; +static ebitset_elt *ebitset_free_list; /* Free list of bitset elements. */ + +#define EBITSET_N_ELTS(N) (((N) + EBITSET_ELT_BITS - 1) / EBITSET_ELT_BITS) +#define EBITSET_ELTS(BSET) ((BSET)->e.elts) +#define EBITSET_SIZE(BSET) EBITSET_N_ELTS (BITSET_NBITS_ (BSET)) +#define EBITSET_ASIZE(BSET) ((BSET)->e.size) + +#define EBITSET_NEXT(ELT) ((ELT)->u.next) +#define EBITSET_WORDS(ELT) ((ELT)->u.words) + +/* Disable bitset cache and mark BSET as being zero. */ +#define EBITSET_ZERO_SET(BSET) ((BSET)->b.cindex = BITSET_WINDEX_MAX, \ + (BSET)->b.cdata = 0) + +#define EBITSET_CACHE_DISABLE(BSET) ((BSET)->b.cindex = BITSET_WINDEX_MAX) + +/* Disable bitset cache and mark BSET as being possibly non-zero. */ +#define EBITSET_NONZERO_SET(BSET) \ + (EBITSET_CACHE_DISABLE (BSET), (BSET)->b.cdata = (bitset_word *)~0) + +/* A conservative estimate of whether the bitset is zero. + This is non-zero only if we know for sure that the bitset is zero. */ +#define EBITSET_ZERO_P(BSET) ((BSET)->b.cdata == 0) + +/* Enable cache to point to element with table index EINDEX. + The element must exist. */ +#define EBITSET_CACHE_SET(BSET, EINDEX) \ + ((BSET)->b.cindex = (EINDEX) * EBITSET_ELT_WORDS, \ + (BSET)->b.cdata = EBITSET_WORDS (EBITSET_ELTS (BSET) [EINDEX])) + +#undef min +#undef max +#define min(a, b) ((a) > (b) ? (b) : (a)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +static bitset_bindex +ebitset_resize (bitset src, bitset_bindex n_bits) +{ + if (n_bits == BITSET_NBITS_ (src)) + return n_bits; + + bitset_windex oldsize = EBITSET_SIZE (src); + bitset_windex newsize = EBITSET_N_ELTS (n_bits); + + if (oldsize < newsize) + { + /* The bitset needs to grow. If we already have enough memory + allocated, then just zero what we need. */ + if (newsize > EBITSET_ASIZE (src)) + { + /* We need to allocate more memory. When oldsize is + non-zero this means that we are changing the size, so + grow the bitset 25% larger than requested to reduce + number of reallocations. */ + + bitset_windex size = oldsize == 0 ? newsize : newsize + newsize / 4; + EBITSET_ELTS (src) + = realloc (EBITSET_ELTS (src), size * sizeof (ebitset_elt *)); + EBITSET_ASIZE (src) = size; + } + + memset (EBITSET_ELTS (src) + oldsize, 0, + (newsize - oldsize) * sizeof (ebitset_elt *)); + } + else + { + /* The bitset needs to shrink. There's no point deallocating + the memory unless it is shrinking by a reasonable amount. */ + if ((oldsize - newsize) >= oldsize / 2) + { + EBITSET_ELTS (src) + = realloc (EBITSET_ELTS (src), newsize * sizeof (ebitset_elt *)); + EBITSET_ASIZE (src) = newsize; + } + + /* Need to prune any excess bits. FIXME. */ + } + + BITSET_NBITS_ (src) = n_bits; + return n_bits; +} + + +/* Allocate a ebitset element. The bits are not cleared. */ +static inline ebitset_elt * +ebitset_elt_alloc (void) +{ + ebitset_elt *elt; + + if (ebitset_free_list != 0) + { + elt = ebitset_free_list; + ebitset_free_list = EBITSET_NEXT (elt); + } + else + { + if (!ebitset_obstack_init) + { + ebitset_obstack_init = true; + + /* Let particular systems override the size of a chunk. */ + +#ifndef OBSTACK_CHUNK_SIZE +#define OBSTACK_CHUNK_SIZE 0 +#endif + + /* Let them override the alloc and free routines too. */ + +#ifndef OBSTACK_CHUNK_ALLOC +#define OBSTACK_CHUNK_ALLOC xmalloc +#endif + +#ifndef OBSTACK_CHUNK_FREE +#define OBSTACK_CHUNK_FREE free +#endif + +#if ! defined __GNUC__ || __GNUC__ < 2 +#define __alignof__(type) 0 +#endif + + obstack_specify_allocation (&ebitset_obstack, OBSTACK_CHUNK_SIZE, + __alignof__ (ebitset_elt), + OBSTACK_CHUNK_ALLOC, + OBSTACK_CHUNK_FREE); + } + + /* Perhaps we should add a number of new elements to the free + list. */ + elt = (ebitset_elt *) obstack_alloc (&ebitset_obstack, + sizeof (ebitset_elt)); + } + + return elt; +} + + +/* Allocate a ebitset element. The bits are cleared. */ +static inline ebitset_elt * +ebitset_elt_calloc (void) +{ + ebitset_elt *elt = ebitset_elt_alloc (); + memset (EBITSET_WORDS (elt), 0, sizeof (EBITSET_WORDS (elt))); + return elt; +} + + +static inline void +ebitset_elt_free (ebitset_elt *elt) +{ + EBITSET_NEXT (elt) = ebitset_free_list; + ebitset_free_list = elt; +} + + +/* Remove element with index EINDEX from bitset BSET. */ +static inline void +ebitset_elt_remove (bitset bset, bitset_windex eindex) +{ + ebitset_elts *elts = EBITSET_ELTS (bset); + ebitset_elt *elt = elts[eindex]; + + elts[eindex] = 0; + ebitset_elt_free (elt); +} + + +/* Add ELT into elts at index EINDEX of bitset BSET. */ +static inline void +ebitset_elt_add (bitset bset, ebitset_elt *elt, bitset_windex eindex) +{ + ebitset_elts *elts = EBITSET_ELTS (bset); + /* Assume that the elts entry not allocated. */ + elts[eindex] = elt; +} + + +/* Are all bits in an element zero? */ +static inline bool +ebitset_elt_zero_p (ebitset_elt *elt) +{ + for (int i = 0; i < EBITSET_ELT_WORDS; i++) + if (EBITSET_WORDS (elt)[i]) + return false; + return true; +} + + +static ebitset_elt * +ebitset_elt_find (bitset bset, bitset_bindex bindex, + enum ebitset_find_mode mode) +{ + bitset_windex eindex = bindex / EBITSET_ELT_BITS; + + ebitset_elts *elts = EBITSET_ELTS (bset); + bitset_windex size = EBITSET_SIZE (bset); + + if (eindex < size) + { + ebitset_elt *elt = elts[eindex]; + if (elt) + { + if (EBITSET_WORDS (elt) != bset->b.cdata) + EBITSET_CACHE_SET (bset, eindex); + return elt; + } + } + + /* The element could not be found. */ + + switch (mode) + { + default: + abort (); + + case EBITSET_FIND: + return 0; + + case EBITSET_CREATE: + if (eindex >= size) + ebitset_resize (bset, bindex); + + /* Create a new element. */ + { + ebitset_elt *elt = ebitset_elt_calloc (); + ebitset_elt_add (bset, elt, eindex); + EBITSET_CACHE_SET (bset, eindex); + return elt; + } + + case EBITSET_SUBST: + return &ebitset_zero_elts[0]; + } +} + + +/* Weed out the zero elements from the elts. */ +static inline bitset_windex +ebitset_weed (bitset bset) +{ + if (EBITSET_ZERO_P (bset)) + return 0; + + ebitset_elts *elts = EBITSET_ELTS (bset); + bitset_windex count = 0; + bitset_windex j; + for (j = 0; j < EBITSET_SIZE (bset); j++) + { + ebitset_elt *elt = elts[j]; + + if (elt) + { + if (ebitset_elt_zero_p (elt)) + { + ebitset_elt_remove (bset, j); + count++; + } + } + else + count++; + } + + count = j - count; + if (!count) + { + /* All the bits are zero. We could shrink the elts. + For now just mark BSET as known to be zero. */ + EBITSET_ZERO_SET (bset); + } + else + EBITSET_NONZERO_SET (bset); + + return count; +} + + +/* Set all bits in the bitset to zero. */ +static inline void +ebitset_zero (bitset bset) +{ + if (EBITSET_ZERO_P (bset)) + return; + + ebitset_elts *elts = EBITSET_ELTS (bset); + for (bitset_windex j = 0; j < EBITSET_SIZE (bset); j++) + { + ebitset_elt *elt = elts[j]; + if (elt) + ebitset_elt_remove (bset, j); + } + + /* All the bits are zero. We could shrink the elts. + For now just mark BSET as known to be zero. */ + EBITSET_ZERO_SET (bset); +} + + +static inline bool +ebitset_equal_p (bitset dst, bitset src) +{ + if (src == dst) + return true; + + ebitset_weed (dst); + ebitset_weed (src); + + if (EBITSET_SIZE (src) != EBITSET_SIZE (dst)) + return false; + + ebitset_elts *selts = EBITSET_ELTS (src); + ebitset_elts *delts = EBITSET_ELTS (dst); + + for (bitset_windex j = 0; j < EBITSET_SIZE (src); j++) + { + ebitset_elt *selt = selts[j]; + ebitset_elt *delt = delts[j]; + + if (!selt && !delt) + continue; + if ((selt && !delt) || (!selt && delt)) + return false; + + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++) + if (EBITSET_WORDS (selt)[i] != EBITSET_WORDS (delt)[i]) + return false; + } + return true; +} + + +/* Copy bits from bitset SRC to bitset DST. */ +static inline void +ebitset_copy_ (bitset dst, bitset src) +{ + if (src == dst) + return; + + ebitset_zero (dst); + + if (BITSET_NBITS_ (dst) != BITSET_NBITS_ (src)) + ebitset_resize (dst, BITSET_NBITS_ (src)); + + ebitset_elts *selts = EBITSET_ELTS (src); + ebitset_elts *delts = EBITSET_ELTS (dst); + for (bitset_windex j = 0; j < EBITSET_SIZE (src); j++) + { + ebitset_elt *selt = selts[j]; + if (selt) + { + ebitset_elt *tmp = ebitset_elt_alloc (); + delts[j] = tmp; + memcpy (EBITSET_WORDS (tmp), EBITSET_WORDS (selt), + sizeof (EBITSET_WORDS (selt))); + } + } + EBITSET_NONZERO_SET (dst); +} + + +/* Copy bits from bitset SRC to bitset DST. Return true if + bitsets different. */ +static inline bool +ebitset_copy_cmp (bitset dst, bitset src) +{ + if (src == dst) + return false; + + if (EBITSET_ZERO_P (dst)) + { + ebitset_copy_ (dst, src); + return !EBITSET_ZERO_P (src); + } + + if (ebitset_equal_p (dst, src)) + return false; + + ebitset_copy_ (dst, src); + return true; +} + + +/* Set bit BITNO in bitset DST. */ +static void +ebitset_set (bitset dst, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + ebitset_elt_find (dst, bitno, EBITSET_CREATE); + + dst->b.cdata[windex - dst->b.cindex] |= + (bitset_word) 1 << (bitno % BITSET_WORD_BITS); +} + + +/* Reset bit BITNO in bitset DST. */ +static void +ebitset_reset (bitset dst, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + if (!ebitset_elt_find (dst, bitno, EBITSET_FIND)) + return; + + dst->b.cdata[windex - dst->b.cindex] &= + ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); + + /* If all the data is zero, perhaps we should remove it now... + However, there is a good chance that the element will be needed + again soon. */ +} + + +/* Test bit BITNO in bitset SRC. */ +static bool +ebitset_test (bitset src, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + return (ebitset_elt_find (src, bitno, EBITSET_FIND) + && ((src->b.cdata[windex - src->b.cindex] + >> (bitno % BITSET_WORD_BITS)) + & 1)); +} + + +static void +ebitset_free (bitset bset) +{ + ebitset_zero (bset); + free (EBITSET_ELTS (bset)); +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +ebitset_list_reverse (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + if (EBITSET_ZERO_P (bset)) + return 0; + + bitset_windex size = EBITSET_SIZE (bset); + bitset_bindex n_bits = size * EBITSET_ELT_BITS; + bitset_bindex rbitno = *next; + + if (rbitno >= n_bits) + return 0; + + ebitset_elts *elts = EBITSET_ELTS (bset); + + bitset_bindex bitno = n_bits - (rbitno + 1); + + bitset_windex windex = bitno / BITSET_WORD_BITS; + bitset_windex eindex = bitno / EBITSET_ELT_BITS; + bitset_windex woffset = windex - eindex * EBITSET_ELT_WORDS; + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + bitset_bindex count = 0; + unsigned bcount = bitno % BITSET_WORD_BITS; + bitset_bindex boffset = windex * BITSET_WORD_BITS; + + do + { + ebitset_elt *elt = elts[eindex]; + if (elt) + { + bitset_word *srcp = EBITSET_WORDS (elt); + + do + { + for (bitset_word word = srcp[woffset] << (BITSET_WORD_BITS - 1 - bcount); + word; bcount--) + { + if (word & BITSET_MSB) + { + list[count++] = boffset + bcount; + if (count >= num) + { + *next = n_bits - (boffset + bcount); + return count; + } + } + word <<= 1; + } + boffset -= BITSET_WORD_BITS; + bcount = BITSET_WORD_BITS - 1; + } + while (woffset--); + } + + woffset = EBITSET_ELT_WORDS - 1; + boffset = eindex * EBITSET_ELT_BITS - BITSET_WORD_BITS; + } + while (eindex--); + + *next = n_bits - (boffset + 1); + return count; +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +ebitset_list (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + if (EBITSET_ZERO_P (bset)) + return 0; + + bitset_bindex bitno = *next; + bitset_bindex count = 0; + + ebitset_elts *elts = EBITSET_ELTS (bset); + bitset_windex size = EBITSET_SIZE (bset); + bitset_windex eindex = bitno / EBITSET_ELT_BITS; + + if (bitno % EBITSET_ELT_BITS) + { + /* We need to start within an element. This is not very common. */ + + ebitset_elt *elt = elts[eindex]; + if (elt) + { + bitset_windex woffset; + bitset_word *srcp = EBITSET_WORDS (elt); + + bitset_windex windex = bitno / BITSET_WORD_BITS; + woffset = eindex * EBITSET_ELT_WORDS; + + for (; (windex - woffset) < EBITSET_ELT_WORDS; windex++) + { + bitset_word word = srcp[windex - woffset] >> (bitno % BITSET_WORD_BITS); + + for (; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + bitno = (windex + 1) * BITSET_WORD_BITS; + } + } + + /* Skip to next element. */ + eindex++; + } + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + for (; eindex < size; eindex++) + { + bitset_word *srcp; + + ebitset_elt *elt = elts[eindex]; + if (!elt) + continue; + + srcp = EBITSET_WORDS (elt); + bitset_windex windex = eindex * EBITSET_ELT_WORDS; + + if ((count + EBITSET_ELT_BITS) < num) + { + /* The coast is clear, plant boot! */ + +#if EBITSET_ELT_WORDS == 2 + bitset_word word = srcp[0]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + if (!(word & 0xff)) + { + word >>= 8; + bitno += 8; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + windex++; + bitno = windex * BITSET_WORD_BITS; + + word = srcp[1]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + windex++; + bitno = windex * BITSET_WORD_BITS; +#else + for (int i = 0; i < EBITSET_ELT_WORDS; i++, windex++) + { + bitno = windex * BITSET_WORD_BITS; + + word = srcp[i]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + if (!(word & 0xff)) + { + word >>= 8; + bitno += 8; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + } +#endif + } + else + { + /* Tread more carefully since we need to check + if array overflows. */ + + for (int i = 0; i < EBITSET_ELT_WORDS; i++, windex++) + { + bitno = windex * BITSET_WORD_BITS; + + for (bitset_word word = srcp[i]; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + } + } + } + + *next = bitno; + return count; +} + + +/* Ensure that any unused bits within the last element are clear. */ +static inline void +ebitset_unused_clear (bitset dst) +{ + bitset_bindex n_bits = BITSET_NBITS_ (dst); + unsigned last_bit = n_bits % EBITSET_ELT_BITS; + + if (last_bit) + { + ebitset_elts *elts = EBITSET_ELTS (dst); + + bitset_windex eindex = n_bits / EBITSET_ELT_BITS; + + ebitset_elt *elt = elts[eindex]; + if (elt) + { + bitset_word *srcp = EBITSET_WORDS (elt); + + bitset_windex windex = n_bits / BITSET_WORD_BITS; + bitset_windex woffset = eindex * EBITSET_ELT_WORDS; + + srcp[windex - woffset] &= ((bitset_word) 1 << last_bit) - 1; + windex++; + for (; (windex - woffset) < EBITSET_ELT_WORDS; windex++) + srcp[windex - woffset] = 0; + } + } +} + + +static void +ebitset_ones (bitset dst) +{ + for (bitset_windex j = 0; j < EBITSET_SIZE (dst); j++) + { + /* Create new elements if they cannot be found. Perhaps + we should just add pointers to a ones element? */ + ebitset_elt *elt = + ebitset_elt_find (dst, j * EBITSET_ELT_BITS, EBITSET_CREATE); + memset (EBITSET_WORDS (elt), -1, sizeof (EBITSET_WORDS (elt))); + } + EBITSET_NONZERO_SET (dst); + ebitset_unused_clear (dst); +} + + +static bool +ebitset_empty_p (bitset dst) +{ + if (EBITSET_ZERO_P (dst)) + return 1; + + ebitset_elts *elts = EBITSET_ELTS (dst); + for (bitset_windex j = 0; j < EBITSET_SIZE (dst); j++) + { + ebitset_elt *elt = elts[j]; + + if (elt) + { + if (!ebitset_elt_zero_p (elt)) + return 0; + /* Do some weeding as we go. */ + ebitset_elt_remove (dst, j); + } + } + + /* All the bits are zero. We could shrink the elts. + For now just mark DST as known to be zero. */ + EBITSET_ZERO_SET (dst); + return 1; +} + + +static void +ebitset_not (bitset dst, bitset src) +{ + ebitset_resize (dst, BITSET_NBITS_ (src)); + + for (bitset_windex j = 0; j < EBITSET_SIZE (src); j++) + { + /* Create new elements for dst if they cannot be found + or substitute zero elements if src elements not found. */ + ebitset_elt *selt = + ebitset_elt_find (dst, j * EBITSET_ELT_BITS, EBITSET_SUBST); + ebitset_elt *delt = + ebitset_elt_find (dst, j * EBITSET_ELT_BITS, EBITSET_CREATE); + + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++) + EBITSET_WORDS (delt)[i] = ~EBITSET_WORDS (selt)[i]; + } + EBITSET_NONZERO_SET (dst); + ebitset_unused_clear (dst); +} + + +/* Is DST == DST | SRC? */ +static bool +ebitset_subset_p (bitset dst, bitset src) +{ + ebitset_elts *selts = EBITSET_ELTS (src); + ebitset_elts *delts = EBITSET_ELTS (dst); + + bitset_windex ssize = EBITSET_SIZE (src); + bitset_windex dsize = EBITSET_SIZE (dst); + + for (bitset_windex j = 0; j < ssize; j++) + { + ebitset_elt *selt = j < ssize ? selts[j] : 0; + ebitset_elt *delt = j < dsize ? delts[j] : 0; + + if (!selt && !delt) + continue; + + if (!selt) + selt = &ebitset_zero_elts[0]; + if (!delt) + delt = &ebitset_zero_elts[0]; + + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++) + if (EBITSET_WORDS (delt)[i] + != (EBITSET_WORDS (selt)[i] | EBITSET_WORDS (delt)[i])) + return false; + } + return true; +} + + +/* Is DST & SRC == 0? */ +static bool +ebitset_disjoint_p (bitset dst, bitset src) +{ + ebitset_elts *selts = EBITSET_ELTS (src); + ebitset_elts *delts = EBITSET_ELTS (dst); + + bitset_windex ssize = EBITSET_SIZE (src); + bitset_windex dsize = EBITSET_SIZE (dst); + + for (bitset_windex j = 0; j < ssize; j++) + { + ebitset_elt *selt = j < ssize ? selts[j] : 0; + ebitset_elt *delt = j < dsize ? delts[j] : 0; + + if (!selt || !delt) + continue; + + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++) + if ((EBITSET_WORDS (selt)[i] & EBITSET_WORDS (delt)[i])) + return false; + } + return true; +} + + + +static bool +ebitset_op3_cmp (bitset dst, bitset src1, bitset src2, enum bitset_ops op) +{ + bool changed = false; + + ebitset_resize (dst, max (BITSET_NBITS_ (src1), BITSET_NBITS_ (src2))); + + bitset_windex ssize1 = EBITSET_SIZE (src1); + bitset_windex ssize2 = EBITSET_SIZE (src2); + bitset_windex dsize = EBITSET_SIZE (dst); + bitset_windex size = ssize1; + if (size < ssize2) + size = ssize2; + + ebitset_elts *selts1 = EBITSET_ELTS (src1); + ebitset_elts *selts2 = EBITSET_ELTS (src2); + ebitset_elts *delts = EBITSET_ELTS (dst); + + bitset_windex j = 0; + for (j = 0; j < size; j++) + { + ebitset_elt *selt1 = j < ssize1 ? selts1[j] : 0; + ebitset_elt *selt2 = j < ssize2 ? selts2[j] : 0; + ebitset_elt *delt = j < dsize ? delts[j] : 0; + + if (!selt1 && !selt2) + { + if (delt) + { + changed = true; + ebitset_elt_remove (dst, j); + } + continue; + } + + if (!selt1) + selt1 = &ebitset_zero_elts[0]; + if (!selt2) + selt2 = &ebitset_zero_elts[0]; + if (!delt) + delt = ebitset_elt_calloc (); + else + delts[j] = 0; + + bitset_word *srcp1 = EBITSET_WORDS (selt1); + bitset_word *srcp2 = EBITSET_WORDS (selt2); + bitset_word *dstp = EBITSET_WORDS (delt); + switch (op) + { + default: + abort (); + + case BITSET_OP_OR: + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ | *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_AND: + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ & *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_XOR: + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ ^ *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_ANDN: + for (unsigned i = 0; i < EBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ & ~(*srcp2++); + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + } + + if (!ebitset_elt_zero_p (delt)) + { + ebitset_elt_add (dst, delt, j); + } + else + { + ebitset_elt_free (delt); + } + } + + /* If we have elements of DST left over, free them all. */ + for (; j < dsize; j++) + { + changed = true; + + ebitset_elt *delt = delts[j]; + if (delt) + ebitset_elt_remove (dst, j); + } + + EBITSET_NONZERO_SET (dst); + return changed; +} + + +static bool +ebitset_and_cmp (bitset dst, bitset src1, bitset src2) +{ + if (EBITSET_ZERO_P (src2)) + { + ebitset_weed (dst); + bool changed = EBITSET_ZERO_P (dst); + ebitset_zero (dst); + return changed; + } + else if (EBITSET_ZERO_P (src1)) + { + ebitset_weed (dst); + bool changed = EBITSET_ZERO_P (dst); + ebitset_zero (dst); + return changed; + } + return ebitset_op3_cmp (dst, src1, src2, BITSET_OP_AND); +} + + +static void +ebitset_and (bitset dst, bitset src1, bitset src2) +{ + ebitset_and_cmp (dst, src1, src2); +} + + +static bool +ebitset_andn_cmp (bitset dst, bitset src1, bitset src2) +{ + if (EBITSET_ZERO_P (src2)) + { + return ebitset_copy_cmp (dst, src1); + } + else if (EBITSET_ZERO_P (src1)) + { + ebitset_weed (dst); + bool changed = EBITSET_ZERO_P (dst); + ebitset_zero (dst); + return changed; + } + return ebitset_op3_cmp (dst, src1, src2, BITSET_OP_ANDN); +} + + +static void +ebitset_andn (bitset dst, bitset src1, bitset src2) +{ + ebitset_andn_cmp (dst, src1, src2); +} + + +static bool +ebitset_or_cmp (bitset dst, bitset src1, bitset src2) +{ + if (EBITSET_ZERO_P (src2)) + { + return ebitset_copy_cmp (dst, src1); + } + else if (EBITSET_ZERO_P (src1)) + { + return ebitset_copy_cmp (dst, src2); + } + return ebitset_op3_cmp (dst, src1, src2, BITSET_OP_OR); +} + + +static void +ebitset_or (bitset dst, bitset src1, bitset src2) +{ + ebitset_or_cmp (dst, src1, src2); +} + + +static bool +ebitset_xor_cmp (bitset dst, bitset src1, bitset src2) +{ + if (EBITSET_ZERO_P (src2)) + { + return ebitset_copy_cmp (dst, src1); + } + else if (EBITSET_ZERO_P (src1)) + { + return ebitset_copy_cmp (dst, src2); + } + return ebitset_op3_cmp (dst, src1, src2, BITSET_OP_XOR); +} + + +static void +ebitset_xor (bitset dst, bitset src1, bitset src2) +{ + ebitset_xor_cmp (dst, src1, src2); +} + + +static void +ebitset_copy (bitset dst, bitset src) +{ + if (BITSET_COMPATIBLE_ (dst, src)) + ebitset_copy_ (dst, src); + else + bitset_copy_ (dst, src); +} + + +/* Vector of operations for linked-list bitsets. */ +struct bitset_vtable ebitset_vtable = { + ebitset_set, + ebitset_reset, + bitset_toggle_, + ebitset_test, + ebitset_resize, + bitset_size_, + bitset_count_, + ebitset_empty_p, + ebitset_ones, + ebitset_zero, + ebitset_copy, + ebitset_disjoint_p, + ebitset_equal_p, + ebitset_not, + ebitset_subset_p, + ebitset_and, + ebitset_and_cmp, + ebitset_andn, + ebitset_andn_cmp, + ebitset_or, + ebitset_or_cmp, + ebitset_xor, + ebitset_xor_cmp, + bitset_and_or_, + bitset_and_or_cmp_, + bitset_andn_or_, + bitset_andn_or_cmp_, + bitset_or_and_, + bitset_or_and_cmp_, + ebitset_list, + ebitset_list_reverse, + ebitset_free, + BITSET_TABLE +}; + + +/* Return size of initial structure. */ +size_t +ebitset_bytes (bitset_bindex n_bits ATTRIBUTE_UNUSED) +{ + return sizeof (struct ebitset_struct); +} + + +/* Initialize a bitset. */ + +bitset +ebitset_init (bitset bset, bitset_bindex n_bits) +{ + bset->b.vtable = &ebitset_vtable; + + bset->b.csize = EBITSET_ELT_WORDS; + + EBITSET_ZERO_SET (bset); + + EBITSET_ASIZE (bset) = 0; + EBITSET_ELTS (bset) = 0; + ebitset_resize (bset, n_bits); + + return bset; +} + + +void +ebitset_release_memory (void) +{ + ebitset_free_list = 0; + if (ebitset_obstack_init) + { + ebitset_obstack_init = false; + obstack_free (&ebitset_obstack, NULL); + } +} diff --git a/lib/bitset/expandable.h b/lib/bitset/expandable.h new file mode 100644 index 0000000000..781903e408 --- /dev/null +++ b/lib/bitset/expandable.h @@ -0,0 +1,32 @@ +/* Functions to support ebitsets. + + Copyright (C) 2002, 2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_EXPANDABLE_H +#define _BITSET_EXPANDABLE_H + +#include "bitset.h" + +size_t ebitset_bytes (bitset_bindex); + +bitset ebitset_init (bitset, bitset_bindex); + +void ebitset_release_memory (void); + +#endif diff --git a/lib/bitset/list.c b/lib/bitset/list.c new file mode 100644 index 0000000000..d784270f5f --- /dev/null +++ b/lib/bitset/list.c @@ -0,0 +1,1327 @@ +/* Functions to support link list bitsets. + + Copyright (C) 2002-2004, 2006, 2009-2015, 2018 Free Software + Foundation, Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "bitset/list.h" + +#include +#include +#include +#include + +#include "obstack.h" + +/* This file implements linked-list bitsets. These bitsets can be of + arbitrary length and are more efficient than arrays of bits for + large sparse sets. + + Usually if all the bits in an element are zero we remove the element + from the list. However, a side effect of the bit caching is that we + do not always notice when an element becomes zero. Hence the + lbitset_weed function which removes zero elements. */ + + +/* Number of words to use for each element. The larger the value the + greater the size of the cache and the shorter the time to find a given bit + but the more memory wasted for sparse bitsets and the longer the time + to search for set bits. + + The routines that dominate timing profiles are lbitset_elt_find + and lbitset_elt_link, especially when accessing the bits randomly. */ + +#define LBITSET_ELT_WORDS 2 + +typedef bitset_word lbitset_word; + +#define LBITSET_WORD_BITS BITSET_WORD_BITS + +/* Number of bits stored in each element. */ +#define LBITSET_ELT_BITS \ + ((unsigned) (LBITSET_ELT_WORDS * LBITSET_WORD_BITS)) + +/* Lbitset element. We use an array of bits for each element. + These are linked together in a doubly-linked list. */ +typedef struct lbitset_elt_struct +{ + struct lbitset_elt_struct *next; /* Next element. */ + struct lbitset_elt_struct *prev; /* Previous element. */ + bitset_windex index; /* bitno / BITSET_WORD_BITS. */ + bitset_word words[LBITSET_ELT_WORDS]; /* Bits that are set. */ +} +lbitset_elt; + + +enum lbitset_find_mode + { LBITSET_FIND, LBITSET_CREATE, LBITSET_SUBST }; + +static lbitset_elt lbitset_zero_elts[3]; /* Elements of all zero bits. */ + +/* Obstack to allocate bitset elements from. */ +static struct obstack lbitset_obstack; +static bool lbitset_obstack_init = false; +static lbitset_elt *lbitset_free_list; /* Free list of bitset elements. */ + +extern void debug_lbitset (bitset); + +#define LBITSET_CURRENT1(X) \ + ((lbitset_elt *) (void *) ((char *) (X) - offsetof (lbitset_elt, words))) + +#define LBITSET_CURRENT(X) LBITSET_CURRENT1((X)->b.cdata) + +#define LBITSET_HEAD(X) ((X)->l.head) +#define LBITSET_TAIL(X) ((X)->l.tail) + +/* Allocate a lbitset element. The bits are not cleared. */ +static inline lbitset_elt * +lbitset_elt_alloc (void) +{ + lbitset_elt *elt; + + if (lbitset_free_list != 0) + { + elt = lbitset_free_list; + lbitset_free_list = elt->next; + } + else + { + if (!lbitset_obstack_init) + { + lbitset_obstack_init = true; + + /* Let particular systems override the size of a chunk. */ + +#ifndef OBSTACK_CHUNK_SIZE +# define OBSTACK_CHUNK_SIZE 0 +#endif + + /* Let them override the alloc and free routines too. */ + +#ifndef OBSTACK_CHUNK_ALLOC +# define OBSTACK_CHUNK_ALLOC xmalloc +#endif + +#ifndef OBSTACK_CHUNK_FREE +# define OBSTACK_CHUNK_FREE free +#endif + +#if ! defined __GNUC__ || __GNUC__ < 2 +# define __alignof__(type) 0 +#endif + + obstack_specify_allocation (&lbitset_obstack, OBSTACK_CHUNK_SIZE, + __alignof__ (lbitset_elt), + OBSTACK_CHUNK_ALLOC, + OBSTACK_CHUNK_FREE); + } + + /* Perhaps we should add a number of new elements to the free + list. */ + elt = (lbitset_elt *) obstack_alloc (&lbitset_obstack, + sizeof (lbitset_elt)); + } + + return elt; +} + + +/* Allocate a lbitset element. The bits are cleared. */ +static inline lbitset_elt * +lbitset_elt_calloc (void) +{ + lbitset_elt *elt = lbitset_elt_alloc (); + memset (elt->words, 0, sizeof (elt->words)); + return elt; +} + + +static inline void +lbitset_elt_free (lbitset_elt *elt) +{ + elt->next = lbitset_free_list; + lbitset_free_list = elt; +} + + +/* Unlink element ELT from bitset BSET. */ +static inline void +lbitset_elt_unlink (bitset bset, lbitset_elt *elt) +{ + lbitset_elt *next = elt->next; + lbitset_elt *prev = elt->prev; + + if (prev) + prev->next = next; + + if (next) + next->prev = prev; + + if (LBITSET_HEAD (bset) == elt) + LBITSET_HEAD (bset) = next; + if (LBITSET_TAIL (bset) == elt) + LBITSET_TAIL (bset) = prev; + + /* Update cache pointer. Since the first thing we try is to insert + before current, make current the next entry in preference to the + previous. */ + if (LBITSET_CURRENT (bset) == elt) + { + if (next) + { + bset->b.cdata = next->words; + bset->b.cindex = next->index; + } + else if (prev) + { + bset->b.cdata = prev->words; + bset->b.cindex = prev->index; + } + else + { + bset->b.csize = 0; + bset->b.cdata = 0; + } + } + + lbitset_elt_free (elt); +} + + +/* Cut the chain of bitset BSET before element ELT and free the + elements. */ +static inline void +lbitset_prune (bitset bset, lbitset_elt *elt) +{ + if (!elt) + return; + + if (elt->prev) + { + LBITSET_TAIL (bset) = elt->prev; + bset->b.cdata = elt->prev->words; + bset->b.cindex = elt->prev->index; + elt->prev->next = 0; + } + else + { + LBITSET_HEAD (bset) = 0; + LBITSET_TAIL (bset) = 0; + bset->b.cdata = 0; + bset->b.csize = 0; + } + + lbitset_elt *next; + for (; elt; elt = next) + { + next = elt->next; + lbitset_elt_free (elt); + } +} + + +/* Are all bits in an element zero? */ +static inline bool +lbitset_elt_zero_p (lbitset_elt *elt) +{ + for (int i = 0; i < LBITSET_ELT_WORDS; i++) + if (elt->words[i]) + return false; + return true; +} + + +/* Link the bitset element into the current bitset linked list. */ +static inline void +lbitset_elt_link (bitset bset, lbitset_elt *elt) +{ + bitset_windex windex = elt->index; + + lbitset_elt *current = bset->b.csize ? LBITSET_CURRENT (bset) : LBITSET_HEAD (bset); + + /* If this is the first and only element, add it in. */ + if (LBITSET_HEAD (bset) == 0) + { + elt->next = elt->prev = 0; + LBITSET_HEAD (bset) = elt; + LBITSET_TAIL (bset) = elt; + } + + /* If this index is less than that of the current element, it goes + somewhere before the current element. */ + else if (windex < bset->b.cindex) + { + lbitset_elt *ptr; + for (ptr = current; + ptr->prev && ptr->prev->index > windex; ptr = ptr->prev) + continue; + + if (ptr->prev) + ptr->prev->next = elt; + else + LBITSET_HEAD (bset) = elt; + + elt->prev = ptr->prev; + elt->next = ptr; + ptr->prev = elt; + } + + /* Otherwise, it must go somewhere after the current element. */ + else + { + lbitset_elt *ptr; + for (ptr = current; + ptr->next && ptr->next->index < windex; ptr = ptr->next) + continue; + + if (ptr->next) + ptr->next->prev = elt; + else + LBITSET_TAIL (bset) = elt; + + elt->next = ptr->next; + elt->prev = ptr; + ptr->next = elt; + } + + /* Set up so this is the first element searched. */ + bset->b.cindex = windex; + bset->b.csize = LBITSET_ELT_WORDS; + bset->b.cdata = elt->words; +} + + +static lbitset_elt * +lbitset_elt_find (bitset bset, bitset_windex windex, + enum lbitset_find_mode mode) +{ + lbitset_elt *current; + + if (bset->b.csize) + { + current = LBITSET_CURRENT (bset); + /* Check if element is the cached element. */ + if ((windex - bset->b.cindex) < bset->b.csize) + return current; + } + else + { + current = LBITSET_HEAD (bset); + } + + if (current) + { + lbitset_elt *elt; + if (windex < bset->b.cindex) + { + for (elt = current; + elt->prev && elt->index > windex; elt = elt->prev) + continue; + } + else + { + for (elt = current; + elt->next && (elt->index + LBITSET_ELT_WORDS - 1) < windex; + elt = elt->next) + continue; + } + + /* ELT is the nearest to the one we want. If it's not the one + we want, the one we want does not exist. */ + if (windex - elt->index < LBITSET_ELT_WORDS) + { + bset->b.cindex = elt->index; + bset->b.csize = LBITSET_ELT_WORDS; + bset->b.cdata = elt->words; + return elt; + } + } + + switch (mode) + { + default: + abort (); + + case LBITSET_FIND: + return 0; + + case LBITSET_CREATE: + windex -= windex % LBITSET_ELT_WORDS; + lbitset_elt *elt = lbitset_elt_calloc (); + elt->index = windex; + lbitset_elt_link (bset, elt); + return elt; + + case LBITSET_SUBST: + return &lbitset_zero_elts[0]; + } +} + + +/* Weed out the zero elements from the list. */ +static inline void +lbitset_weed (bitset bset) +{ + lbitset_elt *next; + for (lbitset_elt *elt = LBITSET_HEAD (bset); elt; elt = next) + { + next = elt->next; + if (lbitset_elt_zero_p (elt)) + lbitset_elt_unlink (bset, elt); + } +} + + +/* Set all bits in the bitset to zero. */ +static void +lbitset_zero (bitset bset) +{ + lbitset_elt *head = LBITSET_HEAD (bset); + if (!head) + return; + + /* Clear a bitset by freeing the linked list at the head element. */ + lbitset_prune (bset, head); +} + + +/* Is DST == SRC? */ +static inline bool +lbitset_equal_p (bitset dst, bitset src) +{ + if (src == dst) + return true; + + lbitset_weed (src); + lbitset_weed (dst); + lbitset_elt *selt; + lbitset_elt *delt; + for (selt = LBITSET_HEAD (src), delt = LBITSET_HEAD (dst); + selt && delt; selt = selt->next, delt = delt->next) + { + if (selt->index != delt->index) + return false; + + for (int j = 0; j < LBITSET_ELT_WORDS; j++) + if (delt->words[j] != selt->words[j]) + return false; + } + return !selt && !delt; +} + + +/* Copy bits from bitset SRC to bitset DST. */ +static inline void +lbitset_copy (bitset dst, bitset src) +{ + if (src == dst) + return; + + lbitset_zero (dst); + + lbitset_elt *head = LBITSET_HEAD (src); + if (!head) + return; + + lbitset_elt *prev = 0; + lbitset_elt *tmp; + for (lbitset_elt *elt = head; elt; elt = elt->next) + { + tmp = lbitset_elt_alloc (); + tmp->index = elt->index; + tmp->prev = prev; + tmp->next = 0; + if (prev) + prev->next = tmp; + else + LBITSET_HEAD (dst) = tmp; + prev = tmp; + + memcpy (tmp->words, elt->words, sizeof (elt->words)); + } + LBITSET_TAIL (dst) = tmp; + + dst->b.csize = LBITSET_ELT_WORDS; + dst->b.cdata = LBITSET_HEAD (dst)->words; + dst->b.cindex = LBITSET_HEAD (dst)->index; +} + + +/* Copy bits from bitset SRC to bitset DST. Return true if + bitsets different. */ +static inline bool +lbitset_copy_cmp (bitset dst, bitset src) +{ + if (src == dst) + return false; + + if (!LBITSET_HEAD (dst)) + { + lbitset_copy (dst, src); + return LBITSET_HEAD (src) != 0; + } + + if (lbitset_equal_p (dst, src)) + return false; + + lbitset_copy (dst, src); + return true; +} + + +static bitset_bindex +lbitset_resize (bitset src, bitset_bindex size) +{ + BITSET_NBITS_ (src) = size; + + /* Need to prune any excess bits. FIXME. */ + return size; +} + +/* Set bit BITNO in bitset DST. */ +static void +lbitset_set (bitset dst, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + lbitset_elt_find (dst, windex, LBITSET_CREATE); + + dst->b.cdata[windex - dst->b.cindex] |= + (bitset_word) 1 << (bitno % BITSET_WORD_BITS); +} + + +/* Reset bit BITNO in bitset DST. */ +static void +lbitset_reset (bitset dst, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + if (!lbitset_elt_find (dst, windex, LBITSET_FIND)) + return; + + dst->b.cdata[windex - dst->b.cindex] &= + ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); + + /* If all the data is zero, perhaps we should unlink it now... */ +} + + +/* Test bit BITNO in bitset SRC. */ +static bool +lbitset_test (bitset src, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + return (lbitset_elt_find (src, windex, LBITSET_FIND) + && ((src->b.cdata[windex - src->b.cindex] + >> (bitno % BITSET_WORD_BITS)) + & 1)); +} + + +static void +lbitset_free (bitset bset) +{ + lbitset_zero (bset); +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +lbitset_list_reverse (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + lbitset_elt *elt = LBITSET_TAIL (bset); + if (!elt) + return 0; + + bitset_bindex n_bits = (elt->index + LBITSET_ELT_WORDS) * BITSET_WORD_BITS; + bitset_bindex rbitno = *next; + + if (rbitno >= n_bits) + return 0; + + bitset_bindex bitno = n_bits - (rbitno + 1); + + bitset_windex windex = bitno / BITSET_WORD_BITS; + + /* Skip back to starting element. */ + for (; elt && elt->index > windex; elt = elt->prev) + continue; + + if (!elt) + return 0; + + unsigned bcount; + if (windex >= elt->index + LBITSET_ELT_WORDS) + { + /* We are trying to start in no-mans land so start + at end of current elt. */ + bcount = BITSET_WORD_BITS - 1; + windex = elt->index + LBITSET_ELT_WORDS - 1; + } + else + { + bcount = bitno % BITSET_WORD_BITS; + } + + bitset_bindex count = 0; + bitset_bindex boffset = windex * BITSET_WORD_BITS; + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + while (elt) + { + bitset_word *srcp = elt->words; + + for (; (windex - elt->index) < LBITSET_ELT_WORDS; + windex--, boffset -= BITSET_WORD_BITS, + bcount = BITSET_WORD_BITS - 1) + { + bitset_word word = + srcp[windex - elt->index] << (BITSET_WORD_BITS - 1 - bcount); + + for (; word; bcount--) + { + if (word & BITSET_MSB) + { + list[count++] = boffset + bcount; + if (count >= num) + { + *next = n_bits - (boffset + bcount); + return count; + } + } + word <<= 1; + } + } + + elt = elt->prev; + if (elt) + { + windex = elt->index + LBITSET_ELT_WORDS - 1; + boffset = windex * BITSET_WORD_BITS; + } + } + + *next = n_bits - (boffset + 1); + return count; +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +lbitset_list (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + lbitset_elt *head = LBITSET_HEAD (bset); + if (!head) + return 0; + + bitset_windex windex; + lbitset_elt *elt; + + bitset_bindex bitno = *next; + bitset_bindex count = 0; + + if (!bitno) + { + /* This is the most common case. */ + + /* Start with the first element. */ + elt = head; + windex = elt->index; + bitno = windex * BITSET_WORD_BITS; + } + else + { + windex = bitno / BITSET_WORD_BITS; + + /* Skip to starting element. */ + for (elt = head; + elt && (elt->index + LBITSET_ELT_WORDS - 1) < windex; + elt = elt->next) + continue; + + if (!elt) + return 0; + + if (windex < elt->index) + { + windex = elt->index; + bitno = windex * BITSET_WORD_BITS; + } + else + { + bitset_word *srcp = elt->words; + + /* We are starting within an element. */ + + for (; (windex - elt->index) < LBITSET_ELT_WORDS; windex++) + { + bitset_word word = srcp[windex - elt->index] >> (bitno % BITSET_WORD_BITS); + + for (; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + bitno = (windex + 1) * BITSET_WORD_BITS; + } + + elt = elt->next; + if (elt) + { + windex = elt->index; + bitno = windex * BITSET_WORD_BITS; + } + } + } + + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + while (elt) + { + bitset_word *srcp = elt->words; + + if ((count + LBITSET_ELT_BITS) < num) + { + /* The coast is clear, plant boot! */ + +#if LBITSET_ELT_WORDS == 2 + bitset_word word = srcp[0]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + if (!(word & 0xff)) + { + word >>= 8; + bitno += 8; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + windex++; + bitno = windex * BITSET_WORD_BITS; + + word = srcp[1]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + windex++; + bitno = windex * BITSET_WORD_BITS; +#else + for (int i = 0; i < LBITSET_ELT_WORDS; i++) + { + bitset_word word = srcp[i]; + if (word) + { + if (!(word & 0xffff)) + { + word >>= 16; + bitno += 16; + } + if (!(word & 0xff)) + { + word >>= 8; + bitno += 8; + } + for (; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + windex++; + bitno = windex * BITSET_WORD_BITS; + } +#endif + } + else + { + /* Tread more carefully since we need to check + if array overflows. */ + + for (int i = 0; i < LBITSET_ELT_WORDS; i++) + { + for (bitset_word word = srcp[i]; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + windex++; + bitno = windex * BITSET_WORD_BITS; + } + } + + elt = elt->next; + if (elt) + { + windex = elt->index; + bitno = windex * BITSET_WORD_BITS; + } + } + + *next = bitno; + return count; +} + + +static bool +lbitset_empty_p (bitset dst) +{ + lbitset_elt *elt; + lbitset_elt *next; + + for (elt = LBITSET_HEAD (dst); elt; elt = next) + { + next = elt->next; + if (!lbitset_elt_zero_p (elt)) + return false; + /* Weed as we go. */ + lbitset_elt_unlink (dst, elt); + } + + return true; +} + + +/* Ensure that any unused bits within the last element are clear. */ +static inline void +lbitset_unused_clear (bitset dst) +{ + bitset_bindex n_bits = BITSET_SIZE_ (dst); + unsigned last_bit = n_bits % LBITSET_ELT_BITS; + + if (last_bit) + { + lbitset_elt *elt = LBITSET_TAIL (dst); + bitset_word *srcp = elt->words; + bitset_windex windex = n_bits / BITSET_WORD_BITS; + + srcp[windex - elt->index] &= ((bitset_word) 1 << last_bit) - 1; + windex++; + + for (; (windex - elt->index) < LBITSET_ELT_WORDS; windex++) + srcp[windex - elt->index] = 0; + } +} + + +static void +lbitset_ones (bitset dst) +{ + /* This is a decidedly unfriendly operation for a linked list + bitset! It makes a sparse bitset become dense. An alternative + is to have a flag that indicates that the bitset stores the + complement of what it indicates. */ + + bitset_windex windex + = (BITSET_SIZE_ (dst) + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS; + + for (bitset_windex i = 0; i < windex; i += LBITSET_ELT_WORDS) + { + /* Create new elements if they cannot be found. */ + lbitset_elt *elt = lbitset_elt_find (dst, i, LBITSET_CREATE); + memset (elt->words, -1, sizeof (elt->words)); + } + + lbitset_unused_clear (dst); +} + + +static void +lbitset_not (bitset dst, bitset src) +{ + bitset_windex windex + = (BITSET_SIZE_ (dst) + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS; + + for (bitset_windex i = 0; i < windex; i += LBITSET_ELT_WORDS) + { + /* Create new elements for dst if they cannot be found + or substitute zero elements if src elements not found. */ + lbitset_elt *selt = lbitset_elt_find (src, i, LBITSET_SUBST); + lbitset_elt *delt = lbitset_elt_find (dst, i, LBITSET_CREATE); + + for (unsigned j = 0; j < LBITSET_ELT_WORDS; j++) + delt->words[j] = ~selt->words[j]; + } + lbitset_unused_clear (dst); + lbitset_weed (dst); +} + + +/* Is DST == DST | SRC? */ +static bool +lbitset_subset_p (bitset dst, bitset src) +{ + for (lbitset_elt *selt = LBITSET_HEAD (src), *delt = LBITSET_HEAD (dst); + selt || delt; selt = selt->next, delt = delt->next) + { + if (!selt) + selt = &lbitset_zero_elts[0]; + else if (!delt) + delt = &lbitset_zero_elts[0]; + else if (selt->index != delt->index) + { + if (selt->index < delt->index) + { + lbitset_zero_elts[2].next = delt; + delt = &lbitset_zero_elts[2]; + } + else + { + lbitset_zero_elts[1].next = selt; + selt = &lbitset_zero_elts[1]; + } + } + + for (unsigned j = 0; j < LBITSET_ELT_WORDS; j++) + if (delt->words[j] != (selt->words[j] | delt->words[j])) + return false; + } + return true; +} + + +/* Is DST & SRC == 0? */ +static bool +lbitset_disjoint_p (bitset dst, bitset src) +{ + for (lbitset_elt *selt = LBITSET_HEAD (src), *delt = LBITSET_HEAD (dst); + selt && delt; selt = selt->next, delt = delt->next) + { + if (selt->index != delt->index) + { + if (selt->index < delt->index) + { + lbitset_zero_elts[2].next = delt; + delt = &lbitset_zero_elts[2]; + } + else + { + lbitset_zero_elts[1].next = selt; + selt = &lbitset_zero_elts[1]; + } + /* Since the elements are different, there is no + intersection of these elements. */ + continue; + } + + for (unsigned j = 0; j < LBITSET_ELT_WORDS; j++) + if (selt->words[j] & delt->words[j]) + return false; + } + return true; +} + + +static bool +lbitset_op3_cmp (bitset dst, bitset src1, bitset src2, enum bitset_ops op) +{ + lbitset_elt *selt1 = LBITSET_HEAD (src1); + lbitset_elt *selt2 = LBITSET_HEAD (src2); + lbitset_elt *delt = LBITSET_HEAD (dst); + bool changed = false; + + LBITSET_HEAD (dst) = 0; + dst->b.csize = 0; + + bitset_windex windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX; + bitset_windex windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX; + + while (selt1 || selt2) + { + bitset_windex windex; + lbitset_elt *stmp1; + lbitset_elt *stmp2; + + /* Figure out whether we need to substitute zero elements for + missing links. */ + if (windex1 == windex2) + { + windex = windex1; + stmp1 = selt1; + stmp2 = selt2; + selt1 = selt1->next; + windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX; + selt2 = selt2->next; + windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX; + } + else if (windex1 < windex2) + { + windex = windex1; + stmp1 = selt1; + stmp2 = &lbitset_zero_elts[0]; + selt1 = selt1->next; + windex1 = (selt1) ? selt1->index : BITSET_WINDEX_MAX; + } + else + { + windex = windex2; + stmp1 = &lbitset_zero_elts[0]; + stmp2 = selt2; + selt2 = selt2->next; + windex2 = (selt2) ? selt2->index : BITSET_WINDEX_MAX; + } + + /* Find the appropriate element from DST. Begin by discarding + elements that we've skipped. */ + lbitset_elt *dtmp; + while (delt && delt->index < windex) + { + changed = true; + dtmp = delt; + delt = delt->next; + lbitset_elt_free (dtmp); + } + if (delt && delt->index == windex) + { + dtmp = delt; + delt = delt->next; + } + else + dtmp = lbitset_elt_calloc (); + + /* Do the operation, and if any bits are set, link it into the + linked list. */ + bitset_word *srcp1 = stmp1->words; + bitset_word *srcp2 = stmp2->words; + bitset_word *dstp = dtmp->words; + switch (op) + { + default: + abort (); + + case BITSET_OP_OR: + for (unsigned i = 0; i < LBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ | *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_AND: + for (unsigned i = 0; i < LBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ & *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_XOR: + for (unsigned i = 0; i < LBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ ^ *srcp2++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + + case BITSET_OP_ANDN: + for (unsigned i = 0; i < LBITSET_ELT_WORDS; i++, dstp++) + { + bitset_word tmp = *srcp1++ & ~(*srcp2++); + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + break; + } + + if (!lbitset_elt_zero_p (dtmp)) + { + dtmp->index = windex; + /* Perhaps this could be optimised... */ + lbitset_elt_link (dst, dtmp); + } + else + { + lbitset_elt_free (dtmp); + } + } + + /* If we have elements of DST left over, free them all. */ + if (delt) + { + changed = true; + lbitset_prune (dst, delt); + } + + return changed; +} + + +static bool +lbitset_and_cmp (bitset dst, bitset src1, bitset src2) +{ + lbitset_elt *selt1 = LBITSET_HEAD (src1); + lbitset_elt *selt2 = LBITSET_HEAD (src2); + + if (!selt2) + { + lbitset_weed (dst); + bool changed = !LBITSET_HEAD (dst); + lbitset_zero (dst); + return changed; + } + else if (!selt1) + { + lbitset_weed (dst); + bool changed = !LBITSET_HEAD (dst); + lbitset_zero (dst); + return changed; + } + else + return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_AND); +} + + +static void +lbitset_and (bitset dst, bitset src1, bitset src2) +{ + lbitset_and_cmp (dst, src1, src2); +} + + +static bool +lbitset_andn_cmp (bitset dst, bitset src1, bitset src2) +{ + lbitset_elt *selt1 = LBITSET_HEAD (src1); + lbitset_elt *selt2 = LBITSET_HEAD (src2); + + if (!selt2) + { + return lbitset_copy_cmp (dst, src1); + } + else if (!selt1) + { + lbitset_weed (dst); + bool changed = !LBITSET_HEAD (dst); + lbitset_zero (dst); + return changed; + } + else + return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_ANDN); +} + + +static void +lbitset_andn (bitset dst, bitset src1, bitset src2) +{ + lbitset_andn_cmp (dst, src1, src2); +} + + +static bool +lbitset_or_cmp (bitset dst, bitset src1, bitset src2) +{ + lbitset_elt *selt1 = LBITSET_HEAD (src1); + lbitset_elt *selt2 = LBITSET_HEAD (src2); + + if (!selt2) + return lbitset_copy_cmp (dst, src1); + else if (!selt1) + return lbitset_copy_cmp (dst, src2); + else + return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_OR); +} + + +static void +lbitset_or (bitset dst, bitset src1, bitset src2) +{ + lbitset_or_cmp (dst, src1, src2); +} + + +static bool +lbitset_xor_cmp (bitset dst, bitset src1, bitset src2) +{ + lbitset_elt *selt1 = LBITSET_HEAD (src1); + lbitset_elt *selt2 = LBITSET_HEAD (src2); + + if (!selt2) + return lbitset_copy_cmp (dst, src1); + else if (!selt1) + return lbitset_copy_cmp (dst, src2); + else + return lbitset_op3_cmp (dst, src1, src2, BITSET_OP_XOR); +} + + +static void +lbitset_xor (bitset dst, bitset src1, bitset src2) +{ + lbitset_xor_cmp (dst, src1, src2); +} + + + +/* Vector of operations for linked-list bitsets. */ +struct bitset_vtable lbitset_vtable = { + lbitset_set, + lbitset_reset, + bitset_toggle_, + lbitset_test, + lbitset_resize, + bitset_size_, + bitset_count_, + lbitset_empty_p, + lbitset_ones, + lbitset_zero, + lbitset_copy, + lbitset_disjoint_p, + lbitset_equal_p, + lbitset_not, + lbitset_subset_p, + lbitset_and, + lbitset_and_cmp, + lbitset_andn, + lbitset_andn_cmp, + lbitset_or, + lbitset_or_cmp, + lbitset_xor, + lbitset_xor_cmp, + bitset_and_or_, + bitset_and_or_cmp_, + bitset_andn_or_, + bitset_andn_or_cmp_, + bitset_or_and_, + bitset_or_and_cmp_, + lbitset_list, + lbitset_list_reverse, + lbitset_free, + BITSET_LIST +}; + + +/* Return size of initial structure. */ +size_t +lbitset_bytes (bitset_bindex n_bits ATTRIBUTE_UNUSED) +{ + return sizeof (struct lbitset_struct); +} + + +/* Initialize a bitset. */ +bitset +lbitset_init (bitset bset, bitset_bindex n_bits ATTRIBUTE_UNUSED) +{ + BITSET_NBITS_ (bset) = n_bits; + bset->b.vtable = &lbitset_vtable; + return bset; +} + + +void +lbitset_release_memory (void) +{ + lbitset_free_list = 0; + if (lbitset_obstack_init) + { + lbitset_obstack_init = false; + obstack_free (&lbitset_obstack, NULL); + } +} + + +/* Function to be called from debugger to debug lbitset. */ +void +debug_lbitset (bitset bset) +{ + if (!bset) + return; + + for (lbitset_elt *elt = LBITSET_HEAD (bset); elt; elt = elt->next) + { + fprintf (stderr, "Elt %lu\n", (unsigned long) elt->index); + for (unsigned i = 0; i < LBITSET_ELT_WORDS; i++) + { + bitset_word word = elt->words[i]; + + fprintf (stderr, " Word %u:", i); + for (unsigned j = 0; j < LBITSET_WORD_BITS; j++) + if ((word & ((bitset_word) 1 << j))) + fprintf (stderr, " %u", j); + fprintf (stderr, "\n"); + } + } +} diff --git a/lib/bitset/list.h b/lib/bitset/list.h new file mode 100644 index 0000000000..839b3ec84f --- /dev/null +++ b/lib/bitset/list.h @@ -0,0 +1,32 @@ +/* Functions to support lbitsets. + + Copyright (C) 2002, 2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_LIST_H +#define _BITSET_LIST_H + +#include "bitset.h" + +size_t lbitset_bytes (bitset_bindex); + +bitset lbitset_init (bitset, bitset_bindex); + +void lbitset_release_memory (void); + +#endif diff --git a/lib/bitset/stats.c b/lib/bitset/stats.c new file mode 100644 index 0000000000..4947ffbb50 --- /dev/null +++ b/lib/bitset/stats.c @@ -0,0 +1,731 @@ +/* Bitset statistics. + + Copyright (C) 2002-2006, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This file is a wrapper bitset implementation for the other bitset + implementations. It provides bitset compatibility checking and + statistics gathering without having to instrument the bitset + implementations. When statistics gathering is enabled, the bitset + operations get vectored through here and we then call the appropriate + routines. */ + +#include + +#include "bitset/stats.h" + +#include +#include +#include + +#include "gettext.h" +#define _(Msgid) gettext (Msgid) + +#include "bitset/array.h" +#include "bitset/base.h" +#include "bitset/expandable.h" +#include "bitset/list.h" +#include "bitset/vector.h" + +/* Configuration macros. */ +#define BITSET_STATS_FILE "bitset.dat" +#define BITSET_LOG_COUNT_BINS 10 +#define BITSET_LOG_SIZE_BINS 16 +#define BITSET_DENSITY_BINS 20 + + +/* Accessor macros. */ +#define BITSET_STATS_ALLOCS_INC(TYPE) \ + bitset_stats_info->types[(TYPE)].allocs++ +#define BITSET_STATS_FREES_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].frees++ +#define BITSET_STATS_SETS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].sets++ +#define BITSET_STATS_CACHE_SETS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].cache_sets++ +#define BITSET_STATS_RESETS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].resets++ +#define BITSET_STATS_CACHE_RESETS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].cache_resets++ +#define BITSET_STATS_TESTS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].tests++ +#define BITSET_STATS_CACHE_TESTS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].cache_tests++ +#define BITSET_STATS_LISTS_INC(BSET) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].lists++ +#define BITSET_STATS_LIST_COUNTS_INC(BSET, I) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].list_counts[(I)]++ +#define BITSET_STATS_LIST_SIZES_INC(BSET, I) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].list_sizes[(I)]++ +#define BITSET_STATS_LIST_DENSITY_INC(BSET, I) \ + bitset_stats_info->types[BITSET_TYPE_ (BSET)].list_density[(I)]++ + + +struct bitset_type_info_struct +{ + unsigned allocs; + unsigned frees; + unsigned lists; + unsigned sets; + unsigned cache_sets; + unsigned resets; + unsigned cache_resets; + unsigned tests; + unsigned cache_tests; + unsigned list_counts[BITSET_LOG_COUNT_BINS]; + unsigned list_sizes[BITSET_LOG_SIZE_BINS]; + unsigned list_density[BITSET_DENSITY_BINS]; +}; + +struct bitset_stats_info_struct +{ + unsigned runs; + struct bitset_type_info_struct types[BITSET_TYPE_NUM]; +}; + + +struct bitset_stats_info_struct bitset_stats_info_data; +struct bitset_stats_info_struct *bitset_stats_info; +bool bitset_stats_enabled = false; + + +/* Print a percentage histogram with message MSG to FILE. */ +static void +bitset_percent_histogram_print (FILE *file, const char *name, const char *msg, + unsigned n_bins, unsigned *bins) +{ + unsigned total = 0; + for (unsigned i = 0; i < n_bins; i++) + total += bins[i]; + + if (!total) + return; + + fprintf (file, "%s %s", name, msg); + for (unsigned i = 0; i < n_bins; i++) + fprintf (file, "%.0f-%.0f%%\t%8u (%5.1f%%)\n", + i * 100.0 / n_bins, + (i + 1) * 100.0 / n_bins, bins[i], + (100.0 * bins[i]) / total); +} + + +/* Print a log histogram with message MSG to FILE. */ +static void +bitset_log_histogram_print (FILE *file, const char *name, const char *msg, + unsigned n_bins, unsigned *bins) +{ + unsigned total = 0; + for (unsigned i = 0; i < n_bins; i++) + total += bins[i]; + + if (!total) + return; + + /* Determine number of useful bins. */ + { + unsigned i; + for (i = n_bins; i > 3 && ! bins[i - 1]; i--) + continue; + n_bins = i; + } + + /* 2 * ceil (log10 (2) * (N - 1)) + 1. */ + unsigned max_width = 2 * (unsigned) (0.30103 * (n_bins - 1) + 0.9999) + 1; + + fprintf (file, "%s %s", name, msg); + { + unsigned i; + for (i = 0; i < 2; i++) + fprintf (file, "%*d\t%8u (%5.1f%%)\n", + max_width, i, bins[i], 100.0 * bins[i] / total); + + for (; i < n_bins; i++) + fprintf (file, "%*lu-%lu\t%8u (%5.1f%%)\n", + max_width - ((unsigned) (0.30103 * (i) + 0.9999) + 1), + 1UL << (i - 1), + (1UL << i) - 1, + bins[i], + (100.0 * bins[i]) / total); + } +} + + +/* Print bitset statistics to FILE. */ +static void +bitset_stats_print_1 (FILE *file, const char *name, + struct bitset_type_info_struct *stats) +{ + if (!stats) + return; + + fprintf (file, "%s:\n", name); + fprintf (file, _("%u bitset_allocs, %u freed (%.2f%%).\n"), + stats->allocs, stats->frees, + stats->allocs ? 100.0 * stats->frees / stats->allocs : 0); + fprintf (file, _("%u bitset_sets, %u cached (%.2f%%)\n"), + stats->sets, stats->cache_sets, + stats->sets ? 100.0 * stats->cache_sets / stats->sets : 0); + fprintf (file, _("%u bitset_resets, %u cached (%.2f%%)\n"), + stats->resets, stats->cache_resets, + stats->resets ? 100.0 * stats->cache_resets / stats->resets : 0); + fprintf (file, _("%u bitset_tests, %u cached (%.2f%%)\n"), + stats->tests, stats->cache_tests, + stats->tests ? 100.0 * stats->cache_tests / stats->tests : 0); + + fprintf (file, _("%u bitset_lists\n"), stats->lists); + + bitset_log_histogram_print (file, name, _("count log histogram\n"), + BITSET_LOG_COUNT_BINS, stats->list_counts); + + bitset_log_histogram_print (file, name, _("size log histogram\n"), + BITSET_LOG_SIZE_BINS, stats->list_sizes); + + bitset_percent_histogram_print (file, name, _("density histogram\n"), + BITSET_DENSITY_BINS, stats->list_density); +} + + +/* Print all bitset statistics to FILE. */ +static void +bitset_stats_print (FILE *file, bool verbose ATTRIBUTE_UNUSED) +{ + if (!bitset_stats_info) + return; + + fprintf (file, _("Bitset statistics:\n\n")); + + if (bitset_stats_info->runs > 1) + fprintf (file, _("Accumulated runs = %u\n"), bitset_stats_info->runs); + + for (int i = 0; i < BITSET_TYPE_NUM; i++) + bitset_stats_print_1 (file, bitset_type_names[i], + &bitset_stats_info->types[i]); +} + + +/* Initialise bitset statistics logging. */ +void +bitset_stats_enable (void) +{ + if (!bitset_stats_info) + bitset_stats_info = &bitset_stats_info_data; + bitset_stats_enabled = true; +} + + +void +bitset_stats_disable (void) +{ + bitset_stats_enabled = false; +} + + +/* Read bitset statistics file. */ +void +bitset_stats_read (const char *file_name) +{ + if (!bitset_stats_info) + return; + + if (!file_name) + file_name = BITSET_STATS_FILE; + + FILE *file = fopen (file_name, "r"); + if (file) + { + if (fread (&bitset_stats_info_data, sizeof (bitset_stats_info_data), + 1, file) != 1) + { + if (ferror (file)) + perror (_("cannot read stats file")); + else + fprintf (stderr, _("bad stats file size\n")); + } + if (fclose (file) != 0) + perror (_("cannot read stats file")); + } + bitset_stats_info_data.runs++; +} + + +/* Write bitset statistics file. */ +void +bitset_stats_write (const char *file_name) +{ + if (!bitset_stats_info) + return; + + if (!file_name) + file_name = BITSET_STATS_FILE; + + FILE *file = fopen (file_name, "w"); + if (file) + { + if (fwrite (&bitset_stats_info_data, sizeof (bitset_stats_info_data), + 1, file) != 1) + perror (_("cannot write stats file")); + if (fclose (file) != 0) + perror (_("cannot write stats file")); + } + else + perror (_("cannot open stats file for writing")); +} + + +/* Dump bitset statistics to FILE. */ +void +bitset_stats_dump (FILE *file) +{ + bitset_stats_print (file, false); +} + + +/* Function to be called from debugger to print bitset stats. */ +void +debug_bitset_stats (void) +{ + bitset_stats_print (stderr, true); +} + + +static void +bitset_stats_set (bitset dst, bitset_bindex bitno) +{ + bitset bset = dst->s.bset; + bitset_windex wordno = bitno / BITSET_WORD_BITS; + bitset_windex offset = wordno - bset->b.cindex; + + BITSET_STATS_SETS_INC (bset); + + if (offset < bset->b.csize) + { + bset->b.cdata[offset] |= (bitset_word) 1 << (bitno % BITSET_WORD_BITS); + BITSET_STATS_CACHE_SETS_INC (bset); + } + else + BITSET_SET_ (bset, bitno); +} + + +static void +bitset_stats_reset (bitset dst, bitset_bindex bitno) +{ + bitset bset = dst->s.bset; + bitset_windex wordno = bitno / BITSET_WORD_BITS; + bitset_windex offset = wordno - bset->b.cindex; + + BITSET_STATS_RESETS_INC (bset); + + if (offset < bset->b.csize) + { + bset->b.cdata[offset] &= + ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); + BITSET_STATS_CACHE_RESETS_INC (bset); + } + else + BITSET_RESET_ (bset, bitno); +} + + +static bool +bitset_stats_toggle (bitset src, bitset_bindex bitno) +{ + return BITSET_TOGGLE_ (src->s.bset, bitno); +} + + +static bool +bitset_stats_test (bitset src, bitset_bindex bitno) +{ + bitset bset = src->s.bset; + bitset_windex wordno = bitno / BITSET_WORD_BITS; + bitset_windex offset = wordno - bset->b.cindex; + + BITSET_STATS_TESTS_INC (bset); + + if (offset < bset->b.csize) + { + BITSET_STATS_CACHE_TESTS_INC (bset); + return (bset->b.cdata[offset] >> (bitno % BITSET_WORD_BITS)) & 1; + } + else + return BITSET_TEST_ (bset, bitno); +} + + +static bitset_bindex +bitset_stats_resize (bitset src, bitset_bindex size) +{ + return BITSET_RESIZE_ (src->s.bset, size); +} + + +static bitset_bindex +bitset_stats_size (bitset src) +{ + return BITSET_SIZE_ (src->s.bset); +} + + +static bitset_bindex +bitset_stats_count (bitset src) +{ + return BITSET_COUNT_ (src->s.bset); +} + + +static bool +bitset_stats_empty_p (bitset dst) +{ + return BITSET_EMPTY_P_ (dst->s.bset); +} + + +static void +bitset_stats_ones (bitset dst) +{ + BITSET_ONES_ (dst->s.bset); +} + + +static void +bitset_stats_zero (bitset dst) +{ + BITSET_ZERO_ (dst->s.bset); +} + + +static void +bitset_stats_copy (bitset dst, bitset src) +{ + BITSET_CHECK2_ (dst, src); + BITSET_COPY_ (dst->s.bset, src->s.bset); +} + + +static bool +bitset_stats_disjoint_p (bitset dst, bitset src) +{ + BITSET_CHECK2_ (dst, src); + return BITSET_DISJOINT_P_ (dst->s.bset, src->s.bset); +} + + +static bool +bitset_stats_equal_p (bitset dst, bitset src) +{ + BITSET_CHECK2_ (dst, src); + return BITSET_EQUAL_P_ (dst->s.bset, src->s.bset); +} + + +static void +bitset_stats_not (bitset dst, bitset src) +{ + BITSET_CHECK2_ (dst, src); + BITSET_NOT_ (dst->s.bset, src->s.bset); +} + + +static bool +bitset_stats_subset_p (bitset dst, bitset src) +{ + BITSET_CHECK2_ (dst, src); + return BITSET_SUBSET_P_ (dst->s.bset, src->s.bset); +} + + +static void +bitset_stats_and (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + BITSET_AND_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static bool +bitset_stats_and_cmp (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + return BITSET_AND_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static void +bitset_stats_andn (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + BITSET_ANDN_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static bool +bitset_stats_andn_cmp (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + return BITSET_ANDN_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static void +bitset_stats_or (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + BITSET_OR_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static bool +bitset_stats_or_cmp (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + return BITSET_OR_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static void +bitset_stats_xor (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + BITSET_XOR_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static bool +bitset_stats_xor_cmp (bitset dst, bitset src1, bitset src2) +{ + BITSET_CHECK3_ (dst, src1, src2); + return BITSET_XOR_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset); +} + + +static void +bitset_stats_and_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + BITSET_AND_OR_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static bool +bitset_stats_and_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + return BITSET_AND_OR_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static void +bitset_stats_andn_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + BITSET_ANDN_OR_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static bool +bitset_stats_andn_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + return BITSET_ANDN_OR_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static void +bitset_stats_or_and (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + BITSET_OR_AND_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static bool +bitset_stats_or_and_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + BITSET_CHECK4_ (dst, src1, src2, src3); + return BITSET_OR_AND_CMP_ (dst->s.bset, src1->s.bset, src2->s.bset, src3->s.bset); +} + + +static bitset_bindex +bitset_stats_list (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_bindex count = BITSET_LIST_ (bset->s.bset, list, num, next); + + BITSET_STATS_LISTS_INC (bset->s.bset); + + /* Log histogram of number of set bits. */ + { + bitset_bindex i; + bitset_bindex tmp; + for (i = 0, tmp = count; tmp; tmp >>= 1, i++) + continue; + if (i >= BITSET_LOG_COUNT_BINS) + i = BITSET_LOG_COUNT_BINS - 1; + BITSET_STATS_LIST_COUNTS_INC (bset->s.bset, i); + } + + /* Log histogram of number of bits in set. */ + bitset_bindex size = BITSET_SIZE_ (bset->s.bset); + { + bitset_bindex i; + bitset_bindex tmp; + for (i = 0, tmp = size; tmp; tmp >>= 1, i++) + continue; + if (i >= BITSET_LOG_SIZE_BINS) + i = BITSET_LOG_SIZE_BINS - 1; + BITSET_STATS_LIST_SIZES_INC (bset->s.bset, i); + } + + /* Histogram of fraction of bits set. */ + { + bitset_bindex i = size ? (count * BITSET_DENSITY_BINS) / size : 0; + if (i >= BITSET_DENSITY_BINS) + i = BITSET_DENSITY_BINS - 1; + BITSET_STATS_LIST_DENSITY_INC (bset->s.bset, i); + } + return count; +} + + +static bitset_bindex +bitset_stats_list_reverse (bitset bset, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + return BITSET_LIST_REVERSE_ (bset->s.bset, list, num, next); +} + + +static void +bitset_stats_free (bitset bset) +{ + BITSET_STATS_FREES_INC (bset->s.bset); + BITSET_FREE_ (bset->s.bset); +} + + +struct bitset_vtable bitset_stats_vtable = { + bitset_stats_set, + bitset_stats_reset, + bitset_stats_toggle, + bitset_stats_test, + bitset_stats_resize, + bitset_stats_size, + bitset_stats_count, + bitset_stats_empty_p, + bitset_stats_ones, + bitset_stats_zero, + bitset_stats_copy, + bitset_stats_disjoint_p, + bitset_stats_equal_p, + bitset_stats_not, + bitset_stats_subset_p, + bitset_stats_and, + bitset_stats_and_cmp, + bitset_stats_andn, + bitset_stats_andn_cmp, + bitset_stats_or, + bitset_stats_or_cmp, + bitset_stats_xor, + bitset_stats_xor_cmp, + bitset_stats_and_or, + bitset_stats_and_or_cmp, + bitset_stats_andn_or, + bitset_stats_andn_or_cmp, + bitset_stats_or_and, + bitset_stats_or_and_cmp, + bitset_stats_list, + bitset_stats_list_reverse, + bitset_stats_free, + BITSET_STATS +}; + + +/* Return enclosed bitset type. */ +enum bitset_type +bitset_stats_type_get (bitset bset) +{ + return BITSET_TYPE_ (bset->s.bset); +} + + +size_t +bitset_stats_bytes (void) +{ + return sizeof (struct bitset_stats_struct); +} + + +bitset +bitset_stats_init (bitset bset, bitset_bindex n_bits, enum bitset_type type) +{ + bset->b.vtable = &bitset_stats_vtable; + + /* Disable cache. */ + bset->b.cindex = 0; + bset->b.csize = 0; + bset->b.cdata = 0; + + BITSET_NBITS_ (bset) = n_bits; + + /* Set up the actual bitset implementation that + we are a wrapper over. */ + switch (type) + { + default: + abort (); + + case BITSET_ARRAY: + { + size_t bytes = abitset_bytes (n_bits); + bset->s.bset = xcalloc (1, bytes); + abitset_init (bset->s.bset, n_bits); + } + break; + + case BITSET_LIST: + { + size_t bytes = lbitset_bytes (n_bits); + bset->s.bset = xcalloc (1, bytes); + lbitset_init (bset->s.bset, n_bits); + } + break; + + case BITSET_TABLE: + { + size_t bytes = ebitset_bytes (n_bits); + bset->s.bset = xcalloc (1, bytes); + ebitset_init (bset->s.bset, n_bits); + } + break; + + case BITSET_VARRAY: + { + size_t bytes = vbitset_bytes (n_bits); + bset->s.bset = xcalloc (1, bytes); + vbitset_init (bset->s.bset, n_bits); + } + break; + } + + BITSET_STATS_ALLOCS_INC (type); + + return bset; +} diff --git a/lib/bitset/stats.h b/lib/bitset/stats.h new file mode 100644 index 0000000000..cd0da1c4ee --- /dev/null +++ b/lib/bitset/stats.h @@ -0,0 +1,34 @@ +/* Functions to support bitset statistics. + + Copyright (C) 2002-2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_STATS_H +#define _BITSET_STATS_H + +#include "bitset/base.h" + +extern bool bitset_stats_enabled; + +enum bitset_type bitset_stats_type_get (bitset); + +size_t bitset_stats_bytes (void); + +bitset bitset_stats_init (bitset, bitset_bindex, enum bitset_type); + +#endif diff --git a/lib/bitset/vector.c b/lib/bitset/vector.c new file mode 100644 index 0000000000..75e7345054 --- /dev/null +++ b/lib/bitset/vector.c @@ -0,0 +1,988 @@ +/* Variable array bitsets. + + Copyright (C) 2002-2006, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "bitset/vector.h" + +#include +#include + +/* This file implements variable size bitsets stored as a variable + length array of words. Any unused bits in the last word must be + zero. + + Note that binary or ternary operations assume that each bitset operand + has the same size. +*/ + +static void vbitset_unused_clear (bitset); + +static void vbitset_set (bitset, bitset_bindex); +static void vbitset_reset (bitset, bitset_bindex); +static bool vbitset_test (bitset, bitset_bindex); +static bitset_bindex vbitset_list (bitset, bitset_bindex *, + bitset_bindex, bitset_bindex *); +static bitset_bindex vbitset_list_reverse (bitset, bitset_bindex *, + bitset_bindex, bitset_bindex *); + +#define VBITSET_N_WORDS(N) (((N) + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS) +#define VBITSET_WORDS(X) ((X)->b.cdata) +#define VBITSET_SIZE(X) ((X)->b.csize) +#define VBITSET_ASIZE(X) ((X)->v.size) + +#undef min +#undef max +#define min(a, b) ((a) > (b) ? (b) : (a)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +static bitset_bindex +vbitset_resize (bitset src, bitset_bindex n_bits) +{ + if (n_bits == BITSET_NBITS_ (src)) + return n_bits; + + bitset_windex oldsize = VBITSET_SIZE (src); + bitset_windex newsize = VBITSET_N_WORDS (n_bits); + + if (oldsize < newsize) + { + /* The bitset needs to grow. If we already have enough memory + allocated, then just zero what we need. */ + if (newsize > VBITSET_ASIZE (src)) + { + /* We need to allocate more memory. When oldsize is + non-zero this means that we are changing the size, so + grow the bitset 25% larger than requested to reduce + number of reallocations. */ + + bitset_windex size = oldsize == 0 ? newsize : newsize + newsize / 4; + VBITSET_WORDS (src) + = realloc (VBITSET_WORDS (src), size * sizeof (bitset_word)); + VBITSET_ASIZE (src) = size; + } + + memset (VBITSET_WORDS (src) + oldsize, 0, + (newsize - oldsize) * sizeof (bitset_word)); + VBITSET_SIZE (src) = newsize; + } + else + { + /* The bitset needs to shrink. There's no point deallocating + the memory unless it is shrinking by a reasonable amount. */ + if ((oldsize - newsize) >= oldsize / 2) + { + VBITSET_WORDS (src) + = realloc (VBITSET_WORDS (src), newsize * sizeof (bitset_word)); + VBITSET_ASIZE (src) = newsize; + } + + /* Need to prune any excess bits. FIXME. */ + + VBITSET_SIZE (src) = newsize; + } + + BITSET_NBITS_ (src) = n_bits; + return n_bits; +} + + +/* Set bit BITNO in bitset DST. */ +static void +vbitset_set (bitset dst, bitset_bindex bitno) +{ + bitset_windex windex = bitno / BITSET_WORD_BITS; + + /* Perhaps we should abort. The user should explicitly call + bitset_resize since this will not catch the case when we set a + bit larger than the current size but smaller than the allocated + size. */ + vbitset_resize (dst, bitno); + + dst->b.cdata[windex - dst->b.cindex] |= + (bitset_word) 1 << (bitno % BITSET_WORD_BITS); +} + + +/* Reset bit BITNO in bitset DST. */ +static void +vbitset_reset (bitset dst ATTRIBUTE_UNUSED, bitset_bindex bitno ATTRIBUTE_UNUSED) +{ + /* We must be accessing outside the cache so the bit is + zero anyway. */ +} + + +/* Test bit BITNO in bitset SRC. */ +static bool +vbitset_test (bitset src ATTRIBUTE_UNUSED, + bitset_bindex bitno ATTRIBUTE_UNUSED) +{ + /* We must be accessing outside the cache so the bit is + zero anyway. */ + return false; +} + + +/* Find list of up to NUM bits set in BSET in reverse order, starting + from and including NEXT and store in array LIST. Return with + actual number of bits found and with *NEXT indicating where search + stopped. */ +static bitset_bindex +vbitset_list_reverse (bitset src, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_word *srcp = VBITSET_WORDS (src); + bitset_bindex n_bits = BITSET_SIZE_ (src); + + bitset_bindex rbitno = *next; + + /* If num is 1, we could speed things up with a binary search + of the word of interest. */ + + if (rbitno >= n_bits) + return 0; + + bitset_bindex count = 0; + + bitset_bindex bitno = n_bits - (rbitno + 1); + + bitset_windex windex = bitno / BITSET_WORD_BITS; + unsigned bitcnt = bitno % BITSET_WORD_BITS; + bitset_bindex bitoff = windex * BITSET_WORD_BITS; + + do + { + bitset_word word = srcp[windex] << (BITSET_WORD_BITS - 1 - bitcnt); + for (; word; bitcnt--) + { + if (word & BITSET_MSB) + { + list[count++] = bitoff + bitcnt; + if (count >= num) + { + *next = n_bits - (bitoff + bitcnt); + return count; + } + } + word <<= 1; + } + bitoff -= BITSET_WORD_BITS; + bitcnt = BITSET_WORD_BITS - 1; + } + while (windex--); + + *next = n_bits - (bitoff + 1); + return count; +} + + +/* Find list of up to NUM bits set in BSET starting from and including + *NEXT and store in array LIST. Return with actual number of bits + found and with *NEXT indicating where search stopped. */ +static bitset_bindex +vbitset_list (bitset src, bitset_bindex *list, + bitset_bindex num, bitset_bindex *next) +{ + bitset_windex size = VBITSET_SIZE (src); + bitset_word *srcp = VBITSET_WORDS (src); + bitset_bindex bitno = *next; + bitset_bindex count = 0; + + bitset_windex windex; + bitset_bindex bitoff; + bitset_word word; + + if (!bitno) + { + /* Many bitsets are zero, so make this common case fast. */ + for (windex = 0; windex < size && !srcp[windex]; windex++) + continue; + if (windex >= size) + return 0; + + /* If num is 1, we could speed things up with a binary search + of the current word. */ + + bitoff = windex * BITSET_WORD_BITS; + } + else + { + if (bitno >= BITSET_SIZE_ (src)) + return 0; + + windex = bitno / BITSET_WORD_BITS; + bitno = bitno % BITSET_WORD_BITS; + + if (bitno) + { + /* Handle the case where we start within a word. + Most often, this is executed with large bitsets + with many set bits where we filled the array + on the previous call to this function. */ + + bitoff = windex * BITSET_WORD_BITS; + word = srcp[windex] >> bitno; + for (bitno = bitoff + bitno; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + windex++; + } + bitoff = windex * BITSET_WORD_BITS; + } + + for (; windex < size; windex++, bitoff += BITSET_WORD_BITS) + { + if (!(word = srcp[windex])) + continue; + + if ((count + BITSET_WORD_BITS) < num) + { + for (bitno = bitoff; word; bitno++) + { + if (word & 1) + list[count++] = bitno; + word >>= 1; + } + } + else + { + for (bitno = bitoff; word; bitno++) + { + if (word & 1) + { + list[count++] = bitno; + if (count >= num) + { + *next = bitno + 1; + return count; + } + } + word >>= 1; + } + } + } + + *next = bitoff; + return count; +} + + +/* Ensure that any unused bits within the last word are clear. */ +static inline void +vbitset_unused_clear (bitset dst) +{ + unsigned last_bit = BITSET_SIZE_ (dst) % BITSET_WORD_BITS; + if (last_bit) + VBITSET_WORDS (dst)[VBITSET_SIZE (dst) - 1] &= + ((bitset_word) 1 << last_bit) - 1; +} + + +static void +vbitset_ones (bitset dst) +{ + bitset_word *dstp = VBITSET_WORDS (dst); + unsigned bytes = sizeof (bitset_word) * VBITSET_SIZE (dst); + + memset (dstp, -1, bytes); + vbitset_unused_clear (dst); +} + + +static void +vbitset_zero (bitset dst) +{ + bitset_word *dstp = VBITSET_WORDS (dst); + unsigned bytes = sizeof (bitset_word) * VBITSET_SIZE (dst); + + memset (dstp, 0, bytes); +} + + +static bool +vbitset_empty_p (bitset dst) +{ + bitset_word *dstp = VBITSET_WORDS (dst); + + for (unsigned i = 0; i < VBITSET_SIZE (dst); i++) + if (dstp[i]) + return false; + return true; +} + + +static void +vbitset_copy1 (bitset dst, bitset src) +{ + if (src == dst) + return; + + vbitset_resize (dst, BITSET_SIZE_ (src)); + + bitset_word *srcp = VBITSET_WORDS (src); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex ssize = VBITSET_SIZE (src); + bitset_windex dsize = VBITSET_SIZE (dst); + + memcpy (dstp, srcp, sizeof (bitset_word) * ssize); + + memset (dstp + sizeof (bitset_word) * ssize, 0, + sizeof (bitset_word) * (dsize - ssize)); +} + + +static void +vbitset_not (bitset dst, bitset src) +{ + vbitset_resize (dst, BITSET_SIZE_ (src)); + + bitset_word *srcp = VBITSET_WORDS (src); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex ssize = VBITSET_SIZE (src); + bitset_windex dsize = VBITSET_SIZE (dst); + + for (unsigned i = 0; i < ssize; i++) + *dstp++ = ~(*srcp++); + + vbitset_unused_clear (dst); + memset (dstp + sizeof (bitset_word) * ssize, 0, + sizeof (bitset_word) * (dsize - ssize)); +} + + +static bool +vbitset_equal_p (bitset dst, bitset src) +{ + bitset_word *srcp = VBITSET_WORDS (src); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex ssize = VBITSET_SIZE (src); + bitset_windex dsize = VBITSET_SIZE (dst); + + unsigned i; + for (i = 0; i < min (ssize, dsize); i++) + if (*srcp++ != *dstp++) + return false; + + if (ssize > dsize) + { + for (; i < ssize; i++) + if (*srcp++) + return false; + } + else + { + for (; i < dsize; i++) + if (*dstp++) + return false; + } + + return true; +} + + +static bool +vbitset_subset_p (bitset dst, bitset src) +{ + bitset_word *srcp = VBITSET_WORDS (src); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex ssize = VBITSET_SIZE (src); + bitset_windex dsize = VBITSET_SIZE (dst); + + unsigned i; + for (i = 0; i < min (ssize, dsize); i++, dstp++, srcp++) + if (*dstp != (*srcp | *dstp)) + return 0; + + if (ssize > dsize) + { + for (; i < ssize; i++) + if (*srcp++) + return false; + } + + return true; +} + + +static bool +vbitset_disjoint_p (bitset dst, bitset src) +{ + bitset_word *srcp = VBITSET_WORDS (src); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex ssize = VBITSET_SIZE (src); + bitset_windex dsize = VBITSET_SIZE (dst); + + for (unsigned i = 0; i < min (ssize, dsize); i++) + if (*srcp++ & *dstp++) + return false; + + return true; +} + + +static void +vbitset_and (bitset dst, bitset src1, bitset src2) +{ + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + for (unsigned i = 0; i < min (ssize1, ssize2); i++) + *dstp++ = *src1p++ & *src2p++; + + memset (dstp, 0, sizeof (bitset_word) * (dsize - min (ssize1, ssize2))); +} + + +static bool +vbitset_and_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++, dstp++) + { + bitset_word tmp = *src1p++ & *src2p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + if (ssize2 > ssize1) + { + src1p = src2p; + ssize1 = ssize2; + } + + for (; i < ssize1; i++, dstp++) + { + if (*dstp != 0) + { + changed = true; + *dstp = 0; + } + } + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); + + return changed; +} + + +static void +vbitset_andn (bitset dst, bitset src1, bitset src2) +{ + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++) + *dstp++ = *src1p++ & ~(*src2p++); + + if (ssize2 > ssize1) + { + for (; i < ssize2; i++) + *dstp++ = 0; + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize2)); + } + else + { + for (; i < ssize1; i++) + *dstp++ = *src1p++; + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); + } +} + + +static bool +vbitset_andn_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++, dstp++) + { + bitset_word tmp = *src1p++ & ~(*src2p++); + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + if (ssize2 > ssize1) + { + for (; i < ssize2; i++, dstp++) + { + if (*dstp != 0) + { + changed = true; + *dstp = 0; + } + } + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize2)); + } + else + { + for (; i < ssize1; i++, dstp++) + { + bitset_word tmp = *src1p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); + } + + return changed; +} + + +static void +vbitset_or (bitset dst, bitset src1, bitset src2) +{ + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++) + *dstp++ = *src1p++ | *src2p++; + + if (ssize2 > ssize1) + { + src1p = src2p; + ssize1 = ssize2; + } + + for (; i < ssize1; i++) + *dstp++ = *src1p++; + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); +} + + +static bool +vbitset_or_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++, dstp++) + { + bitset_word tmp = *src1p++ | *src2p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + if (ssize2 > ssize1) + { + src1p = src2p; + ssize1 = ssize2; + } + + for (; i < ssize1; i++, dstp++) + { + bitset_word tmp = *src1p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); + + return changed; +} + + +static void +vbitset_xor (bitset dst, bitset src1, bitset src2) +{ + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++) + *dstp++ = *src1p++ ^ *src2p++; + + if (ssize2 > ssize1) + { + src1p = src2p; + ssize1 = ssize2; + } + + for (; i < ssize1; i++) + *dstp++ = *src1p++; + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); +} + + +static bool +vbitset_xor_cmp (bitset dst, bitset src1, bitset src2) +{ + bool changed = false; + vbitset_resize (dst, max (BITSET_SIZE_ (src1), BITSET_SIZE_ (src2))); + + bitset_windex dsize = VBITSET_SIZE (dst); + bitset_windex ssize1 = VBITSET_SIZE (src1); + bitset_windex ssize2 = VBITSET_SIZE (src2); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + + unsigned i; + for (i = 0; i < min (ssize1, ssize2); i++, dstp++) + { + bitset_word tmp = *src1p++ ^ *src2p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + if (ssize2 > ssize1) + { + src1p = src2p; + ssize1 = ssize2; + } + + for (; i < ssize1; i++, dstp++) + { + bitset_word tmp = *src1p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + + memset (dstp, 0, sizeof (bitset_word) * (dsize - ssize1)); + + return changed; +} + + +/* FIXME, these operations need fixing for different size + bitsets. */ + +static void +vbitset_and_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + { + bitset_and_or_ (dst, src1, src2, src3); + return; + } + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + for (unsigned i = 0; i < size; i++) + *dstp++ = (*src1p++ & *src2p++) | *src3p++; +} + + +static bool +vbitset_and_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + return bitset_and_or_cmp_ (dst, src1, src2, src3); + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + bool changed = false; + for (unsigned i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ & *src2p++) | *src3p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +vbitset_andn_or (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + { + bitset_andn_or_ (dst, src1, src2, src3); + return; + } + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + for (unsigned i = 0; i < size; i++) + *dstp++ = (*src1p++ & ~(*src2p++)) | *src3p++; +} + + +static bool +vbitset_andn_or_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + return bitset_andn_or_cmp_ (dst, src1, src2, src3); + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + bool changed = false; + for (unsigned i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ & ~(*src2p++)) | *src3p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +vbitset_or_and (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + { + bitset_or_and_ (dst, src1, src2, src3); + return; + } + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + for (unsigned i = 0; i < size; i++) + *dstp++ = (*src1p++ | *src2p++) & *src3p++; +} + + +static bool +vbitset_or_and_cmp (bitset dst, bitset src1, bitset src2, bitset src3) +{ + if (BITSET_NBITS_ (src1) != BITSET_NBITS_ (src2) + || BITSET_NBITS_ (src1) != BITSET_NBITS_ (src3)) + return bitset_or_and_cmp_ (dst, src1, src2, src3); + + vbitset_resize (dst, BITSET_NBITS_ (src1)); + + bitset_word *src1p = VBITSET_WORDS (src1); + bitset_word *src2p = VBITSET_WORDS (src2); + bitset_word *src3p = VBITSET_WORDS (src3); + bitset_word *dstp = VBITSET_WORDS (dst); + bitset_windex size = VBITSET_SIZE (dst); + + unsigned i; + bool changed = false; + for (i = 0; i < size; i++, dstp++) + { + bitset_word tmp = (*src1p++ | *src2p++) & *src3p++; + + if (*dstp != tmp) + { + changed = true; + *dstp = tmp; + } + } + return changed; +} + + +static void +vbitset_copy (bitset dst, bitset src) +{ + if (BITSET_COMPATIBLE_ (dst, src)) + vbitset_copy1 (dst, src); + else + bitset_copy_ (dst, src); +} + + +/* Vector of operations for multiple word bitsets. */ +struct bitset_vtable vbitset_vtable = { + vbitset_set, + vbitset_reset, + bitset_toggle_, + vbitset_test, + vbitset_resize, + bitset_size_, + bitset_count_, + vbitset_empty_p, + vbitset_ones, + vbitset_zero, + vbitset_copy, + vbitset_disjoint_p, + vbitset_equal_p, + vbitset_not, + vbitset_subset_p, + vbitset_and, + vbitset_and_cmp, + vbitset_andn, + vbitset_andn_cmp, + vbitset_or, + vbitset_or_cmp, + vbitset_xor, + vbitset_xor_cmp, + vbitset_and_or, + vbitset_and_or_cmp, + vbitset_andn_or, + vbitset_andn_or_cmp, + vbitset_or_and, + vbitset_or_and_cmp, + vbitset_list, + vbitset_list_reverse, + NULL, + BITSET_VARRAY +}; + + +size_t +vbitset_bytes (bitset_bindex n_bits ATTRIBUTE_UNUSED) +{ + return sizeof (struct vbitset_struct); +} + + +bitset +vbitset_init (bitset bset, bitset_bindex n_bits) +{ + bset->b.vtable = &vbitset_vtable; + bset->b.cindex = 0; + VBITSET_SIZE (bset) = 0; + vbitset_resize (bset, n_bits); + return bset; +} diff --git a/lib/bitset/vector.h b/lib/bitset/vector.h new file mode 100644 index 0000000000..44b16b0232 --- /dev/null +++ b/lib/bitset/vector.h @@ -0,0 +1,30 @@ +/* Functions to support vbitsets. + + Copyright (C) 2002, 2004, 2009-2015, 2018 Free Software Foundation, + Inc. + + Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz). + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _BITSET_VECTOR_H +#define _BITSET_VECTOR_H + +#include "bitset.h" + +size_t vbitset_bytes (bitset_bindex); + +bitset vbitset_init (bitset, bitset_bindex); + +#endif diff --git a/modules/bitset b/modules/bitset new file mode 100644 index 0000000000..d77d35ed55 --- /dev/null +++ b/modules/bitset @@ -0,0 +1,35 @@ +Description: +A common interface to several implementations of bitsets. + +Files: +lib/bitset.c +lib/bitset.h +lib/bitset/array.c +lib/bitset/array.h +lib/bitset/base.h +lib/bitset/expandable.c +lib/bitset/expandable.h +lib/bitset/list.c +lib/bitset/list.h +lib/bitset/stats.c +lib/bitset/stats.h +lib/bitset/vector.c +lib/bitset/vector.h + +Depends-on: +gettext-h +obstack +xalloc + +Makefile.am: +lib_SOURCES += bitset.c bitset/array.c bitset/stats.c \ + bitset/expandable.c bitset/list.c bitset/vector.c + +Include: +"bitset.h" + +License: +GPLv3+ + +Maintainer: +Akim Demaille