diff options
Diffstat (limited to 'src/backend/nodes/bitmapset.c')
-rw-r--r-- | src/backend/nodes/bitmapset.c | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c new file mode 100644 index 00000000000..5798b726497 --- /dev/null +++ b/src/backend/nodes/bitmapset.c @@ -0,0 +1,759 @@ +/*------------------------------------------------------------------------- + * + * bitmapset.c + * PostgreSQL generic bitmap set package + * + * A bitmap set can represent any set of nonnegative integers, although + * it is mainly intended for sets where the maximum value is not large, + * say at most a few hundred. By convention, a NULL pointer is always + * accepted by all operations to represent the empty set. (But beware + * that this is not the only representation of the empty set. Use + * bms_is_empty() in preference to testing for NULL.) + * + * + * Copyright (c) 2003, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/bitmapset.c,v 1.1 2003/02/08 20:20:53 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/bitmapset.h" + + +#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) +#define BITNUM(x) ((x) % BITS_PER_BITMAPWORD) + +#define BITMAPSET_SIZE(nwords) \ + (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword)) + +/*---------- + * This is a well-known cute trick for isolating the rightmost one-bit + * in a word. It assumes two's complement arithmetic. Consider any + * nonzero value, and focus attention on the rightmost one. The value is + * then something like + * xxxxxx10000 + * where x's are unspecified bits. The two's complement negative is formed + * by inverting all the bits and adding one. Inversion gives + * yyyyyy01111 + * where each y is the inverse of the corresponding x. Incrementing gives + * yyyyyy10000 + * and then ANDing with the original value gives + * 00000010000 + * This works for all cases except original value = zero, where of course + * we get zero. + *---------- + */ +#define RIGHTMOST_ONE(x) ((signedbitmapword) (x) & -((signedbitmapword) (x))) + +#define HAS_MULTIPLE_ONES(x) ((bitmapword) RIGHTMOST_ONE(x) != (x)) + + +/* + * Lookup tables to avoid need for bit-by-bit groveling + * + * rightmost_one_pos[x] gives the bit number (0-7) of the rightmost one bit + * in a nonzero byte value x. The entry for x=0 is never used. + * + * number_of_ones[x] gives the number of one-bits (0-8) in a byte value x. + * + * We could make these tables larger and reduce the number of iterations + * in the functions that use them, but bytewise shifts and masks are + * especially fast on many machines, so working a byte at a time seems best. + */ + +static const uint8 rightmost_one_pos[256] = { + 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +static const uint8 number_of_ones[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + + +/* + * bms_copy - make a palloc'd copy of a bitmapset + */ +Bitmapset * +bms_copy(const Bitmapset *a) +{ + Bitmapset *result; + size_t size; + + if (a == NULL) + return NULL; + size = BITMAPSET_SIZE(a->nwords); + result = (Bitmapset *) palloc(size); + memcpy(result, a, size); + return result; +} + +/* + * bms_equal - are two bitmapsets equal? + * + * This is logical not physical equality; in particular, a NULL pointer will + * be reported as equal to a palloc'd value containing no members. + */ +bool +bms_equal(const Bitmapset *a, const Bitmapset *b) +{ + const Bitmapset *shorter; + const Bitmapset *longer; + int shortlen; + int longlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + { + if (b == NULL) + return true; + return bms_is_empty(b); + } + else if (b == NULL) + { + return bms_is_empty(a); + } + /* Identify shorter and longer input */ + if (a->nwords <= b->nwords) + { + shorter = a; + longer = b; + } + else + { + shorter = b; + longer = a; + } + /* And process */ + shortlen = shorter->nwords; + for (i = 0; i < shortlen; i++) + { + if (shorter->words[i] != longer->words[i]) + return false; + } + longlen = longer->nwords; + for (; i < longlen; i++) + { + if (longer->words[i] != 0) + return false; + } + return true; +} + +/* + * bms_make_singleton - build a bitmapset containing a single member + */ +Bitmapset * +bms_make_singleton(int x) +{ + Bitmapset *result; + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "bms_make_singleton: negative set member not allowed"); + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1)); + result->nwords = wordnum + 1; + result->words[wordnum] = ((bitmapword) 1 << bitnum); + return result; +} + +/* + * bms_free - free a bitmapset + * + * Same as pfree except for allowing NULL input + */ +void +bms_free(Bitmapset *a) +{ + if (a) + pfree(a); +} + + +/* + * These operations all make a freshly palloc'd result, + * leaving their inputs untouched + */ + + +/* + * bms_union - set union + */ +Bitmapset * +bms_union(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return bms_copy(b); + if (b == NULL) + return bms_copy(a); + /* Identify shorter and longer input; copy the longer one */ + if (a->nwords <= b->nwords) + { + result = bms_copy(b); + other = a; + } + else + { + result = bms_copy(a); + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + { + result->words[i] |= other->words[i]; + } + return result; +} + +/* + * bms_intersect - set intersection + */ +Bitmapset * +bms_intersect(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int resultlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL || b == NULL) + return NULL; + /* Identify shorter and longer input; copy the shorter one */ + if (a->nwords <= b->nwords) + { + result = bms_copy(a); + other = b; + } + else + { + result = bms_copy(b); + other = a; + } + /* And intersect the longer input with the result */ + resultlen = result->nwords; + for (i = 0; i < resultlen; i++) + { + result->words[i] &= other->words[i]; + } + return result; +} + +/* + * bms_difference - set difference (ie, A without members of B) + */ +Bitmapset * +bms_difference(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + return bms_copy(a); + /* Copy the left input */ + result = bms_copy(a); + /* And remove b's bits from result */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + result->words[i] &= ~ b->words[i]; + } + return result; +} + +/* + * bms_is_subset - is A a subset of B? + */ +bool +bms_is_subset(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int longlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return true; /* empty set is a subset of anything */ + if (b == NULL) + return bms_is_empty(a); + /* Check common words */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + if ((a->words[i] & ~ b->words[i]) != 0) + return false; + } + /* Check extra words */ + if (a->nwords > b->nwords) + { + longlen = a->nwords; + for (; i < longlen; i++) + { + if (a->words[i] != 0) + return false; + } + } + return true; +} + +/* + * bms_is_member - is X a member of A? + */ +bool +bms_is_member(int x, const Bitmapset *a) +{ + int wordnum, + bitnum; + + /* XXX better to just return false for x<0 ? */ + if (x < 0) + elog(ERROR, "bms_is_member: negative set member not allowed"); + if (a == NULL) + return false; + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum >= a->nwords) + return false; + if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + return true; + return false; +} + +/* + * bms_overlap - do sets overlap (ie, have a nonempty intersection)? + */ +bool +bms_overlap(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL || b == NULL) + return false; + /* Check words in common */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + if ((a->words[i] & b->words[i]) != 0) + return true; + } + return false; +} + +/* + * bms_singleton_member - return the sole integer member of set + * + * Raises error if |a| is not 1. + */ +int +bms_singleton_member(const Bitmapset *a) +{ + int result = -1; + int nwords; + int wordnum; + + if (a == NULL) + elog(ERROR, "bms_singleton_member: set is empty"); + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + if (result >= 0 || HAS_MULTIPLE_ONES(w)) + elog(ERROR, "bms_singleton_member: set has multiple members"); + result = wordnum * BITS_PER_BITMAPWORD; + while ((w & 255) == 0) + { + w >>= 8; + result += 8; + } + result += rightmost_one_pos[w & 255]; + } + } + if (result < 0) + elog(ERROR, "bms_singleton_member: set is empty"); + return result; +} + +/* + * bms_num_members - count members of set + */ +int +bms_num_members(const Bitmapset *a) +{ + int result = 0; + int nwords; + int wordnum; + + if (a == NULL) + return 0; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + /* we assume here that bitmapword is an unsigned type */ + while (w != 0) + { + result += number_of_ones[w & 255]; + w >>= 8; + } + } + return result; +} + +/* + * bms_membership - does a set have zero, one, or multiple members? + * + * This is faster than making an exact count with bms_num_members(). + */ +BMS_Membership +bms_membership(const Bitmapset *a) +{ + BMS_Membership result = BMS_EMPTY_SET; + int nwords; + int wordnum; + + if (a == NULL) + return BMS_EMPTY_SET; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + if (result != BMS_EMPTY_SET || HAS_MULTIPLE_ONES(w)) + return BMS_MULTIPLE; + result = BMS_SINGLETON; + } + } + return result; +} + +/* + * bms_is_empty - is a set empty? + * + * This is even faster than bms_membership(). + */ +bool +bms_is_empty(const Bitmapset *a) +{ + int nwords; + int wordnum; + + if (a == NULL) + return true; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + return false; + } + return true; +} + + +/* + * These operations all "recycle" their non-const inputs, ie, either + * return the modified input or pfree it if it can't hold the result. + * + * These should generally be used in the style + * + * foo = bms_add_member(foo, x); + */ + + +/* + * bms_add_member - add a specified member to set + * + * Input set is modified or recycled! + */ +Bitmapset * +bms_add_member(Bitmapset *a, int x) +{ + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "bms_add_member: negative set member not allowed"); + if (a == NULL) + return bms_make_singleton(x); + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum >= a->nwords) + { + /* Slow path: make a larger set and union the input set into it */ + Bitmapset *result; + int nwords; + int i; + + result = bms_make_singleton(x); + nwords = a->nwords; + for (i = 0; i < nwords; i++) + { + result->words[i] |= a->words[i]; + } + pfree(a); + return result; + } + /* Fast path: x fits in existing set */ + a->words[wordnum] |= ((bitmapword) 1 << bitnum); + return a; +} + +/* + * bms_del_member - remove a specified member from set + * + * No error if x is not currently a member of set + * + * Input set is modified in-place! + */ +Bitmapset * +bms_del_member(Bitmapset *a, int x) +{ + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "bms_del_member: negative set member not allowed"); + if (a == NULL) + return NULL; + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum < a->nwords) + { + a->words[wordnum] &= ~ ((bitmapword) 1 << bitnum); + } + return a; +} + +/* + * bms_add_members - like bms_union, but left input is recycled + */ +Bitmapset * +bms_add_members(Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return bms_copy(b); + if (b == NULL) + return a; + /* Identify shorter and longer input; copy the longer one if needed */ + if (a->nwords < b->nwords) + { + result = bms_copy(b); + other = a; + } + else + { + result = a; + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + { + result->words[i] |= other->words[i]; + } + if (result != a) + pfree(a); + return result; +} + +/* + * bms_int_members - like bms_intersect, but left input is recycled + */ +Bitmapset * +bms_int_members(Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + { + pfree(a); + return NULL; + } + /* Intersect b into a; we need never copy */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + a->words[i] &= b->words[i]; + } + for (; i < a->nwords; i++) + { + a->words[i] = 0; + } + return a; +} + +/* + * bms_del_members - like bms_difference, but left input is recycled + */ +Bitmapset * +bms_del_members(Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + return a; + /* Remove b's bits from a; we need never copy */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + a->words[i] &= ~ b->words[i]; + } + return a; +} + +/* + * bms_join - like bms_union, but *both* inputs are recycled + */ +Bitmapset * +bms_join(Bitmapset *a, Bitmapset *b) +{ + Bitmapset *result; + Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return b; + if (b == NULL) + return a; + /* Identify shorter and longer input; use longer one as result */ + if (a->nwords < b->nwords) + { + result = b; + other = a; + } + else + { + result = a; + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + { + result->words[i] |= other->words[i]; + } + if (other != result) /* pure paranoia */ + pfree(other); + return result; +} + +/*---------- + * bms_first_member - find and remove first member of a set + * + * Returns -1 if set is empty. NB: set is destructively modified! + * + * This is intended as support for iterating through the members of a set. + * The typical pattern is + * + * tmpset = bms_copy(inputset); + * while ((x = bms_first_member(tmpset)) >= 0) + * { + * process member x; + * } + * bms_free(tmpset); + *---------- + */ +int +bms_first_member(Bitmapset *a) +{ + int nwords; + int wordnum; + + if (a == NULL) + return -1; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + int result; + + w = RIGHTMOST_ONE(w); + a->words[wordnum] &= ~ w; + + result = wordnum * BITS_PER_BITMAPWORD; + while ((w & 255) == 0) + { + w >>= 8; + result += 8; + } + result += rightmost_one_pos[w & 255]; + return result; + } + } + return -1; +} |