From cba48504d99e8149b6598dbb011f8e1c5a46738a Mon Sep 17 00:00:00 2001 From: David Rowley Date: Mon, 6 Nov 2017 14:39:33 +1300 Subject: [PATCH 2/7] Add bms_add_range to add members within the specified range The same behavior could be obtained by looping and using bms_add_member, however, using bms_add_range operates at the bitmapword level and should be far faster when the range is large. Author: David Rowley --- src/backend/nodes/bitmapset.c | 74 +++++++++++++++++++++++++++++++++++++++++++ src/include/nodes/bitmapset.h | 1 + 2 files changed, 75 insertions(+) diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c index bf8545d437..34d242b357 100644 --- a/src/backend/nodes/bitmapset.c +++ b/src/backend/nodes/bitmapset.c @@ -785,6 +785,80 @@ bms_add_members(Bitmapset *a, const Bitmapset *b) } /* + * 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 with + * at the bitmapword level rather than at bit level. + */ +Bitmapset * +bms_add_range(Bitmapset *a, int lower, int upper) +{ + int lwordnum, + uwordnum, + wordnum; + + if (lower < 0 || upper < 0) + elog(ERROR, "negative bitmapset member not allowed"); + if (lower > upper) + elog(ERROR, "lower range must not be above upper range"); + uwordnum = WORDNUM(upper); + + if (a == NULL) + { + a = (Bitmapset *) palloc0(BITMAPSET_SIZE(uwordnum + 1)); + a->nwords = uwordnum + 1; + } + + /* ensure we have enough words to store the upper bit */ + else if (uwordnum >= a->nwords) + { + int oldnwords = a->nwords; + int i; + + a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1)); + a->nwords = uwordnum + 1; + /* zero out the enlarged portion */ + for (i = oldnwords; i < a->nwords; i++) + a->words[i] = 0; + } + + wordnum = lwordnum = WORDNUM(lower); + + /* + * Starting at lower's wordnum, loop over each word up to upper's wordnum. + * Along the way set all bits inclusively between lower and upper to 1. We + * only need to handle the lwordnum and uwordnum specially so we don't set + * any bits outside of the range. + */ + while (wordnum <= uwordnum) + { + bitmapword mask = (bitmapword) ~0; + + /* If working on the lower word, zero out bits below 'lower'. */ + if (wordnum == lwordnum) + { + int lbitnum = BITNUM(lower); + mask >>= lbitnum; + mask <<= lbitnum; + } + + /* Likewise, if working on the upper word, zero bits above 'upper' */ + if (wordnum == uwordnum) + { + int ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(upper) + 1); + mask <<= ushiftbits; + mask >>= ushiftbits; + } + + a->words[wordnum++] |= mask; + } + + return a; +} + +/* * bms_int_members - like bms_intersect, but left input is recycled */ Bitmapset * diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h index aa3fb253c2..3b62a97775 100644 --- a/src/include/nodes/bitmapset.h +++ b/src/include/nodes/bitmapset.h @@ -90,6 +90,7 @@ extern bool bms_is_empty(const Bitmapset *a); extern Bitmapset *bms_add_member(Bitmapset *a, int x); extern Bitmapset *bms_del_member(Bitmapset *a, int x); extern Bitmapset *bms_add_members(Bitmapset *a, const Bitmapset *b); +extern Bitmapset *bms_add_range(Bitmapset *a, int lower, int upper); extern Bitmapset *bms_int_members(Bitmapset *a, const Bitmapset *b); extern Bitmapset *bms_del_members(Bitmapset *a, const Bitmapset *b); extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b); -- 2.11.0