PostgreSQL/src/backend/nodes/bitmapset.c
David Rowley a8c09daa8b Remove trailing zero words from Bitmapsets
Prior to this, Bitmapsets could contain trailing words which had no
members set.  Many places in bitmapset.c had to loop over trailing words
to check for empty words.  If we ensure we always remove these trailing
zero words then we can perform various optimizations such as fast
pathing bms_is_subset to return false when 'a' has more words than 'b'.
A similar optimization is possible in bms_equal.  Both of these together
can yield quite significant performance increases in the query planner
when querying a partitioned table with around 100 or more partitions.

While we're at it, since the minimum number of words a Bitmapset can
contain is 1, we can make use of do/while loops instead of for loops
when looping over all words in a set.  This means checking the loop
condition 1 less time, which for single-word sets cuts the loop
condition checks in half.

Author: David Rowley
Reviewed-by: Yuya Watari
Discussion: https://postgr.es/m/CAApHDvr5O41MuUjw0DQKqmAnv7QqvmLqXReEd5o4nXTzWp8-+w@mail.gmail.com
2023-07-04 12:34:48 +12:00

1242 lines
27 KiB
C

/*-------------------------------------------------------------------------
*
* 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, we always represent a set with
* the minimum possible number of words, i.e, there are never any trailing
* zero words. Enforcing this requires that an empty set is represented as
* NULL. Because an empty Bitmapset is represented as NULL, a non-NULL
* Bitmapset always has at least 1 Bitmapword. We can exploit this fact to
* speed up various loops over the Bitmapset's words array by using "do while"
* loops instead of "for" loops. This means the code does not waste time
* checking the loop condition before the first iteration. For Bitmapsets
* containing only a single word (likely the majority of them) this halves the
* number of loop condition checks.
*
*
* Copyright (c) 2003-2023, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/nodes/bitmapset.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "common/hashfn.h"
#include "nodes/bitmapset.h"
#include "nodes/pg_list.h"
#include "port/pg_bitutils.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))
/* Select appropriate bit-twiddling functions for bitmap word size */
#if BITS_PER_BITMAPWORD == 32
#define bmw_leftmost_one_pos(w) pg_leftmost_one_pos32(w)
#define bmw_rightmost_one_pos(w) pg_rightmost_one_pos32(w)
#define bmw_popcount(w) pg_popcount32(w)
#elif BITS_PER_BITMAPWORD == 64
#define bmw_leftmost_one_pos(w) pg_leftmost_one_pos64(w)
#define bmw_rightmost_one_pos(w) pg_rightmost_one_pos64(w)
#define bmw_popcount(w) pg_popcount64(w)
#else
#error "invalid BITS_PER_BITMAPWORD"
#endif
/*
* 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? or both NULL?
*/
bool
bms_equal(const Bitmapset *a, const Bitmapset *b)
{
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
{
if (b == NULL)
return true;
return false;
}
else if (b == NULL)
return false;
/* can't be equal if the word counts don't match */
if (a->nwords != b->nwords)
return false;
/* check each word matches */
i = 0;
do
{
if (a->words[i] != b->words[i])
return false;
} while (++i < a->nwords);
return true;
}
/*
* bms_compare - qsort-style comparator for bitmapsets
*
* This guarantees to report values as equal iff bms_equal would say they are
* equal. Otherwise, the highest-numbered bit that is set in one value but
* not the other determines the result. (This rule means that, for example,
* {6} is greater than {5}, which seems plausible.)
*/
int
bms_compare(const Bitmapset *a, const Bitmapset *b)
{
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
return (b == NULL) ? 0 : -1;
else if (b == NULL)
return +1;
/* the set with the most words must be greater */
if (a->nwords != b->nwords)
return (a->nwords > b->nwords) ? +1 : -1;
i = a->nwords - 1;
do
{
bitmapword aw = a->words[i];
bitmapword bw = b->words[i];
if (aw != bw)
return (aw > bw) ? +1 : -1;
} while (--i >= 0);
return 0;
}
/*
* 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, "negative bitmapset member not allowed");
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1));
result->type = T_Bitmapset;
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;
i = 0;
do
{
result->words[i] |= other->words[i];
} while (++i < otherlen);
return result;
}
/*
* bms_intersect - set intersection
*/
Bitmapset *
bms_intersect(const Bitmapset *a, const Bitmapset *b)
{
Bitmapset *result;
const Bitmapset *other;
int lastnonzero;
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;
lastnonzero = -1;
i = 0;
do
{
result->words[i] &= other->words[i];
if (result->words[i] != 0)
lastnonzero = i;
} while (++i < resultlen);
/* If we computed an empty result, we must return NULL */
if (lastnonzero == -1)
{
pfree(result);
return NULL;
}
/* get rid of trailing zero words */
result->nwords = lastnonzero + 1;
return result;
}
/*
* bms_difference - set difference (ie, A without members of B)
*/
Bitmapset *
bms_difference(const Bitmapset *a, const Bitmapset *b)
{
Bitmapset *result;
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
return NULL;
if (b == NULL)
return bms_copy(a);
/*
* In Postgres' usage, an empty result is a very common case, so it's
* worth optimizing for that by testing bms_nonempty_difference(). This
* saves us a palloc/pfree cycle compared to checking after-the-fact.
*/
if (!bms_nonempty_difference(a, b))
return NULL;
/* Copy the left input */
result = bms_copy(a);
/* And remove b's bits from result */
if (result->nwords > b->nwords)
{
/*
* We'll never need to remove trailing zero words when 'a' has more
* words than 'b' as the additional words must be non-zero.
*/
i = 0;
do
{
result->words[i] &= ~b->words[i];
} while (++i < b->nwords);
}
else
{
int lastnonzero = -1;
/* we may need to remove trailing zero words from the result. */
i = 0;
do
{
result->words[i] &= ~b->words[i];
/* remember the last non-zero word */
if (result->words[i] != 0)
lastnonzero = i;
} while (++i < result->nwords);
/* trim off trailing zero words */
result->nwords = lastnonzero + 1;
}
Assert(result->nwords != 0);
/* Need not check for empty result, since we handled that case above */
return result;
}
/*
* bms_is_subset - is A a subset of B?
*/
bool
bms_is_subset(const Bitmapset *a, const Bitmapset *b)
{
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
return true; /* empty set is a subset of anything */
if (b == NULL)
return false;
/* 'a' can't be a subset of 'b' if it contains more words */
if (a->nwords > b->nwords)
return false;
/* Check all 'a' members are set in 'b' */
i = 0;
do
{
if ((a->words[i] & ~b->words[i]) != 0)
return false;
} while (++i < a->nwords);
return true;
}
/*
* bms_subset_compare - compare A and B for equality/subset relationships
*
* This is more efficient than testing bms_is_subset in both directions.
*/
BMS_Comparison
bms_subset_compare(const Bitmapset *a, const Bitmapset *b)
{
BMS_Comparison result;
int shortlen;
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
{
if (b == NULL)
return BMS_EQUAL;
return BMS_SUBSET1;
}
if (b == NULL)
return BMS_SUBSET2;
/* Check common words */
result = BMS_EQUAL; /* status so far */
shortlen = Min(a->nwords, b->nwords);
i = 0;
do
{
bitmapword aword = a->words[i];
bitmapword bword = b->words[i];
if ((aword & ~bword) != 0)
{
/* a is not a subset of b */
if (result == BMS_SUBSET1)
return BMS_DIFFERENT;
result = BMS_SUBSET2;
}
if ((bword & ~aword) != 0)
{
/* b is not a subset of a */
if (result == BMS_SUBSET2)
return BMS_DIFFERENT;
result = BMS_SUBSET1;
}
} while (++i < shortlen);
/* Check extra words */
if (a->nwords > b->nwords)
{
/* if a has more words then a is not a subset of b */
if (result == BMS_SUBSET1)
return BMS_DIFFERENT;
return BMS_SUBSET2;
}
else if (a->nwords < b->nwords)
{
/* if b has more words then b is not a subset of a */
if (result == BMS_SUBSET2)
return BMS_DIFFERENT;
return BMS_SUBSET1;
}
return result;
}
/*
* 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, "negative bitmapset 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_member_index
* determine 0-based index of member x in the bitmap
*
* Returns (-1) when x is not a member.
*/
int
bms_member_index(Bitmapset *a, int x)
{
int i;
int bitnum;
int wordnum;
int result = 0;
bitmapword mask;
/* return -1 if not a member of the bitmap */
if (!bms_is_member(x, a))
return -1;
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
/* count bits in preceding words */
for (i = 0; i < wordnum; i++)
{
bitmapword w = a->words[i];
/* No need to count the bits in a zero word */
if (w != 0)
result += bmw_popcount(w);
}
/*
* Now add bits of the last word, but only those before the item. We can
* do that by applying a mask and then using popcount again. To get
* 0-based index, we want to count only preceding bits, not the item
* itself, so we subtract 1.
*/
mask = ((bitmapword) 1 << bitnum) - 1;
result += bmw_popcount(a->words[wordnum] & mask);
return result;
}
/*
* 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);
i = 0;
do
{
if ((a->words[i] & b->words[i]) != 0)
return true;
} while (++i < shortlen);
return false;
}
/*
* bms_overlap_list - does a set overlap an integer list?
*/
bool
bms_overlap_list(const Bitmapset *a, const List *b)
{
ListCell *lc;
int wordnum,
bitnum;
if (a == NULL || b == NIL)
return false;
foreach(lc, b)
{
int x = lfirst_int(lc);
if (x < 0)
elog(ERROR, "negative bitmapset member not allowed");
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
if (wordnum < a->nwords)
if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)
return true;
}
return false;
}
/*
* bms_nonempty_difference - do sets have a nonempty difference?
*
* i.e., are any members set in 'a' that are not also set in 'b'.
*/
bool
bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
{
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* Handle cases where either input is NULL */
if (a == NULL)
return false;
if (b == NULL)
return true;
/* if 'a' has more words then it must contain additional members */
if (a->nwords > b->nwords)
return true;
/* Check all 'a' members are set in 'b' */
i = 0;
do
{
if ((a->words[i] & ~b->words[i]) != 0)
return true;
} while (++i < a->nwords);
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, "bitmapset is empty");
nwords = a->nwords;
wordnum = 0;
do
{
bitmapword w = a->words[wordnum];
if (w != 0)
{
if (result >= 0 || HAS_MULTIPLE_ONES(w))
elog(ERROR, "bitmapset has multiple members");
result = wordnum * BITS_PER_BITMAPWORD;
result += bmw_rightmost_one_pos(w);
}
} while (++wordnum < nwords);
/* we don't expect non-NULL sets to be empty */
Assert(result >= 0);
return result;
}
/*
* bms_get_singleton_member
*
* Test whether the given set is a singleton.
* If so, set *member to the value of its sole member, and return true.
* If not, return false, without changing *member.
*
* This is more convenient and faster than calling bms_membership() and then
* bms_singleton_member(), if we don't care about distinguishing empty sets
* from multiple-member sets.
*/
bool
bms_get_singleton_member(const Bitmapset *a, int *member)
{
int result = -1;
int nwords;
int wordnum;
if (a == NULL)
return false;
nwords = a->nwords;
wordnum = 0;
do
{
bitmapword w = a->words[wordnum];
if (w != 0)
{
if (result >= 0 || HAS_MULTIPLE_ONES(w))
return false;
result = wordnum * BITS_PER_BITMAPWORD;
result += bmw_rightmost_one_pos(w);
}
} while (++wordnum < nwords);
/* we don't expect non-NULL sets to be empty */
Assert(result >= 0);
*member = result;
return true;
}
/*
* 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;
wordnum = 0;
do
{
bitmapword w = a->words[wordnum];
/* No need to count the bits in a zero word */
if (w != 0)
result += bmw_popcount(w);
} while (++wordnum < nwords);
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;
wordnum = 0;
do
{
bitmapword w = a->words[wordnum];
if (w != 0)
{
if (result != BMS_EMPTY_SET || HAS_MULTIPLE_ONES(w))
return BMS_MULTIPLE;
result = BMS_SINGLETON;
}
} while (++wordnum < nwords);
return result;
}
/*
* 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, "negative bitmapset member not allowed");
if (a == NULL)
return bms_make_singleton(x);
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
/* enlarge the set if necessary */
if (wordnum >= a->nwords)
{
int oldnwords = a->nwords;
int i;
a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1));
a->nwords = wordnum + 1;
/* zero out the enlarged portion */
i = oldnwords;
do
{
a->words[i] = 0;
} while (++i < a->nwords);
}
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, "negative bitmapset member not allowed");
if (a == NULL)
return NULL;
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
/* member can't exist. Return 'a' unmodified */
if (unlikely(wordnum >= a->nwords))
return a;
a->words[wordnum] &= ~((bitmapword) 1 << bitnum);
/* when last word becomes empty, trim off all trailing empty words */
if (a->words[wordnum] == 0 && wordnum == a->nwords - 1)
{
/* find the last non-empty word and make that the new final word */
for (int i = wordnum - 1; i >= 0; i--)
{
if (a->words[i] != 0)
{
a->nwords = i + 1;
return a;
}
}
/* the set is now empty */
pfree(a);
return NULL;
}
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;
i = 0;
do
{
result->words[i] |= other->words[i];
} while (++i < otherlen);
if (result != a)
pfree(a);
return result;
}
/*
* bms_add_range
* Add members in the range of 'lower' to 'upper' to the set.
*
* Note this could also be done by calling bms_add_member in a loop, however,
* using this function will be faster when the range is large as we work at
* the bitmapword level rather than at bit level.
*/
Bitmapset *
bms_add_range(Bitmapset *a, int lower, int upper)
{
int lwordnum,
lbitnum,
uwordnum,
ushiftbits,
wordnum;
/* do nothing if nothing is called for, without further checking */
if (upper < lower)
return a;
if (lower < 0)
elog(ERROR, "negative bitmapset member not allowed");
uwordnum = WORDNUM(upper);
if (a == NULL)
{
a = (Bitmapset *) palloc0(BITMAPSET_SIZE(uwordnum + 1));
a->type = T_Bitmapset;
a->nwords = uwordnum + 1;
}
else if (uwordnum >= a->nwords)
{
int oldnwords = a->nwords;
int i;
/* ensure we have enough words to store the upper bit */
a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1));
a->nwords = uwordnum + 1;
/* zero out the enlarged portion */
i = oldnwords;
do
{
a->words[i] = 0;
} while (++i < a->nwords);
}
wordnum = lwordnum = WORDNUM(lower);
lbitnum = BITNUM(lower);
ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(upper) + 1);
/*
* Special case when lwordnum is the same as uwordnum we must perform the
* upper and lower masking on the word.
*/
if (lwordnum == uwordnum)
{
a->words[lwordnum] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1)
& (~(bitmapword) 0) >> ushiftbits;
}
else
{
/* turn on lbitnum and all bits left of it */
a->words[wordnum++] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1);
/* turn on all bits for any intermediate words */
while (wordnum < uwordnum)
a->words[wordnum++] = ~(bitmapword) 0;
/* turn on upper's bit and all bits right of it. */
a->words[uwordnum] |= (~(bitmapword) 0) >> ushiftbits;
}
return a;
}
/*
* bms_int_members - like bms_intersect, but left input is recycled
*/
Bitmapset *
bms_int_members(Bitmapset *a, const Bitmapset *b)
{
int lastnonzero;
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);
lastnonzero = -1;
i = 0;
do
{
a->words[i] &= b->words[i];
if (a->words[i] != 0)
lastnonzero = i;
} while (++i < shortlen);
/* If we computed an empty result, we must return NULL */
if (lastnonzero == -1)
{
pfree(a);
return NULL;
}
/* get rid of trailing zero words */
a->nwords = lastnonzero + 1;
return a;
}
/*
* bms_del_members - like bms_difference, but left input is recycled
*/
Bitmapset *
bms_del_members(Bitmapset *a, const Bitmapset *b)
{
int i;
Assert(a == NULL || a->words[a->nwords - 1] != 0);
Assert(b == NULL || b->words[b->nwords - 1] != 0);
/* 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 */
if (a->nwords > b->nwords)
{
/*
* We'll never need to remove trailing zero words when 'a' has more
* words than 'b'.
*/
i = 0;
do
{
a->words[i] &= ~b->words[i];
} while (++i < b->nwords);
}
else
{
int lastnonzero = -1;
/* we may need to remove trailing zero words from the result. */
i = 0;
do
{
a->words[i] &= ~b->words[i];
/* remember the last non-zero word */
if (a->words[i] != 0)
lastnonzero = i;
} while (++i < a->nwords);
/* check if 'a' has become empty */
if (lastnonzero == -1)
{
pfree(a);
return NULL;
}
/* trim off any trailing zero words */
a->nwords = lastnonzero + 1;
}
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;
i = 0;
do
{
result->words[i] |= other->words[i];
} while (++i < otherlen);
if (other != result) /* pure paranoia */
pfree(other);
return result;
}
/*
* bms_next_member - find next member of a set
*
* Returns smallest member greater than "prevbit", or -2 if there is none.
* "prevbit" must NOT be less than -1, or the behavior is unpredictable.
*
* This is intended as support for iterating through the members of a set.
* The typical pattern is
*
* x = -1;
* while ((x = bms_next_member(inputset, x)) >= 0)
* process member x;
*
* Notice that when there are no more members, we return -2, not -1 as you
* might expect. The rationale for that is to allow distinguishing the
* loop-not-started state (x == -1) from the loop-completed state (x == -2).
* It makes no difference in simple loop usage, but complex iteration logic
* might need such an ability.
*/
int
bms_next_member(const Bitmapset *a, int prevbit)
{
int nwords;
int wordnum;
bitmapword mask;
if (a == NULL)
return -2;
nwords = a->nwords;
prevbit++;
mask = (~(bitmapword) 0) << BITNUM(prevbit);
for (wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++)
{
bitmapword w = a->words[wordnum];
/* ignore bits before prevbit */
w &= mask;
if (w != 0)
{
int result;
result = wordnum * BITS_PER_BITMAPWORD;
result += bmw_rightmost_one_pos(w);
return result;
}
/* in subsequent words, consider all bits */
mask = (~(bitmapword) 0);
}
return -2;
}
/*
* bms_prev_member - find prev member of a set
*
* Returns largest member less than "prevbit", or -2 if there is none.
* "prevbit" must NOT be more than one above the highest possible bit that can
* be set at the Bitmapset at its current size.
*
* To ease finding the highest set bit for the initial loop, the special
* prevbit value of -1 can be passed to have the function find the highest
* valued member in the set.
*
* This is intended as support for iterating through the members of a set in
* reverse. The typical pattern is
*
* x = -1;
* while ((x = bms_prev_member(inputset, x)) >= 0)
* process member x;
*
* Notice that when there are no more members, we return -2, not -1 as you
* might expect. The rationale for that is to allow distinguishing the
* loop-not-started state (x == -1) from the loop-completed state (x == -2).
* It makes no difference in simple loop usage, but complex iteration logic
* might need such an ability.
*/
int
bms_prev_member(const Bitmapset *a, int prevbit)
{
int wordnum;
int ushiftbits;
bitmapword mask;
/*
* If set is NULL or if there are no more bits to the right then we've
* nothing to do.
*/
if (a == NULL || prevbit == 0)
return -2;
/* transform -1 to the highest possible bit we could have set */
if (prevbit == -1)
prevbit = a->nwords * BITS_PER_BITMAPWORD - 1;
else
prevbit--;
ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1);
mask = (~(bitmapword) 0) >> ushiftbits;
for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
{
bitmapword w = a->words[wordnum];
/* mask out bits left of prevbit */
w &= mask;
if (w != 0)
{
int result;
result = wordnum * BITS_PER_BITMAPWORD;
result += bmw_leftmost_one_pos(w);
return result;
}
/* in subsequent words, consider all bits */
mask = (~(bitmapword) 0);
}
return -2;
}
/*
* bms_hash_value - compute a hash key for a Bitmapset
*/
uint32
bms_hash_value(const Bitmapset *a)
{
if (a == NULL)
return 0; /* All empty sets hash to 0 */
return DatumGetUInt32(hash_any((const unsigned char *) a->words,
a->nwords * sizeof(bitmapword)));
}
/*
* bitmap_hash - hash function for keys that are (pointers to) Bitmapsets
*
* Note: don't forget to specify bitmap_match as the match function!
*/
uint32
bitmap_hash(const void *key, Size keysize)
{
Assert(keysize == sizeof(Bitmapset *));
return bms_hash_value(*((const Bitmapset *const *) key));
}
/*
* bitmap_match - match function to use with bitmap_hash
*/
int
bitmap_match(const void *key1, const void *key2, Size keysize)
{
Assert(keysize == sizeof(Bitmapset *));
return !bms_equal(*((const Bitmapset *const *) key1),
*((const Bitmapset *const *) key2));
}