* containing only a single word (likely the majority of them) this halves the
* number of loop condition checks.
*
+ * Callers must ensure that the set returned by functions in this file which
+ * adjust the members of an existing set is assigned to all pointers pointing
+ * to that existing set. No guarantees are made that we'll ever modify the
+ * existing set in-place and return it.
+ *
+ * To help find bugs caused by callers failing to record the return value of
+ * the function which manipulates an existing set, we support building with
+ * REALLOCATE_BITMAPSETS. This results in the set being reallocated each time
+ * the set is altered and the existing being pfreed. This is useful as if any
+ * references still exist to the old set, we're more likely to notice as
+ * any users of the old set will be accessing pfree'd memory. This option is
+ * only intended to be used for debugging.
*
* Copyright (c) 2003-2024, PostgreSQL Global Development Group
*
#error "invalid BITS_PER_BITMAPWORD"
#endif
+#ifdef USE_ASSERT_CHECKING
+/*
+ * bms_is_valid_set - for cassert builds to check for valid sets
+ */
+static bool
+bms_is_valid_set(const Bitmapset *a)
+{
+ /* NULL is the correct representation of an empty set */
+ if (a == NULL)
+ return true;
+
+ /* check the node tag is set correctly. pfree'd pointer, maybe? */
+ if (!IsA(a, Bitmapset))
+ return false;
+
+ /* trailing zero words are not allowed */
+ if (a->words[a->nwords - 1] == 0)
+ return false;
+
+ return true;
+}
+#endif
+
+#ifdef REALLOCATE_BITMAPSETS
+/*
+ * bms_copy_and_free
+ * Only required in REALLOCATE_BITMAPSETS builds. Provide a simple way
+ * to return a freshly allocated set and pfree the original.
+ *
+ * Note: callers which accept multiple sets must be careful when calling this
+ * function to clone one parameter as other parameters may point to the same
+ * set. A good option is to call this just before returning the resulting
+ * set.
+ */
+static Bitmapset *
+bms_copy_and_free(Bitmapset *a)
+{
+ Bitmapset *c = bms_copy(a);
+
+ bms_free(a);
+ return c;
+}
+#endif
/*
* bms_copy - make a palloc'd copy of a bitmapset
Bitmapset *result;
size_t size;
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
return NULL;
- Assert(IsA(a, Bitmapset));
+
size = BITMAPSET_SIZE(a->nwords);
result = (Bitmapset *) palloc(size);
memcpy(result, a, size);
{
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
{
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
/*
- * These operations all make a freshly palloc'd result,
- * leaving their inputs untouched
- */
-
-
-/*
- * bms_union - set union
+ * bms_union - create and return a new set containing all members from both
+ * input sets. Both inputs are left unmodified.
*/
Bitmapset *
bms_union(const Bitmapset *a, const Bitmapset *b)
int otherlen;
int i;
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
}
/*
- * bms_intersect - set intersection
+ * bms_intersect - create and return a new set containing members which both
+ * input sets have in common. Both inputs are left unmodified.
*/
Bitmapset *
bms_intersect(const Bitmapset *a, const Bitmapset *b)
int resultlen;
int i;
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL || b == NULL)
}
/*
- * bms_difference - set difference (ie, A without members of B)
+ * bms_difference - create and return a new set containing all the members of
+ * 'a' without the members of 'b'.
*/
Bitmapset *
bms_difference(const Bitmapset *a, const Bitmapset *b)
Bitmapset *result;
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
if (b == NULL)
return bms_copy(a);
- Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset));
-
/*
* In Postgres' usage, an empty result is a very common case, so it's
* worth optimizing for that by testing bms_nonempty_difference(). This
{
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
if (b == NULL)
return false;
- Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset));
-
/* 'a' can't be a subset of 'b' if it contains more words */
if (a->nwords > b->nwords)
return false;
int shortlen;
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
if (b == NULL)
return BMS_SUBSET2;
- Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset));
-
/* Check common words */
result = BMS_EQUAL; /* status so far */
shortlen = Min(a->nwords, b->nwords);
int wordnum,
bitnum;
+ Assert(bms_is_valid_set(a));
+
/* XXX better to just return false for x<0 ? */
if (x < 0)
elog(ERROR, "negative bitmapset member not allowed");
if (a == NULL)
return false;
- Assert(IsA(a, Bitmapset));
-
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
if (wordnum >= a->nwords)
int result = 0;
bitmapword mask;
+ Assert(bms_is_valid_set(a));
+
/* return -1 if not a member of the bitmap */
if (!bms_is_member(x, a))
return -1;
- Assert(IsA(a, Bitmapset));
-
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
int shortlen;
int i;
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL || b == NULL)
int wordnum,
bitnum;
- Assert(a == NULL || IsA(a, Bitmapset));
+ Assert(bms_is_valid_set(a));
if (a == NULL || b == NIL)
return false;
{
int i;
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
int nwords;
int wordnum;
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
elog(ERROR, "bitmapset is empty");
- Assert(IsA(a, Bitmapset));
-
nwords = a->nwords;
wordnum = 0;
do
int nwords;
int wordnum;
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
return false;
- Assert(IsA(a, Bitmapset));
+
nwords = a->nwords;
wordnum = 0;
do
int nwords;
int wordnum;
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
return 0;
- Assert(IsA(a, Bitmapset));
+
nwords = a->nwords;
wordnum = 0;
do
int nwords;
int wordnum;
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
return BMS_EMPTY_SET;
- Assert(IsA(a, Bitmapset));
+
nwords = a->nwords;
wordnum = 0;
do
}
-/*
- * 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!
+ * 'a' is recycled when possible.
*/
Bitmapset *
bms_add_member(Bitmapset *a, int x)
int wordnum,
bitnum;
+ Assert(bms_is_valid_set(a));
+
if (x < 0)
elog(ERROR, "negative bitmapset member not allowed");
if (a == NULL)
return bms_make_singleton(x);
- Assert(IsA(a, Bitmapset));
+
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
{
int oldnwords = a->nwords;
int i;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(wordnum + 1));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
-#else
a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1));
-#endif
a->nwords = wordnum + 1;
/* zero out the enlarged portion */
i = oldnwords;
a->words[i] = 0;
} while (++i < a->nwords);
}
+
+ a->words[wordnum] |= ((bitmapword) 1 << bitnum);
+
#ifdef REALLOCATE_BITMAPSETS
- else
- {
- Bitmapset *tmp = a;
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
- }
+ /*
+ * There's no guarantee that the repalloc returned a new pointer, so copy
+ * and free unconditionally here.
+ */
+ a = bms_copy_and_free(a);
#endif
- a->words[wordnum] |= ((bitmapword) 1 << bitnum);
return a;
}
*
* No error if x is not currently a member of set
*
- * Input set is modified in-place!
+ * 'a' is recycled when possible.
*/
Bitmapset *
bms_del_member(Bitmapset *a, int x)
{
int wordnum,
bitnum;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
-#endif
+
+ Assert(bms_is_valid_set(a));
if (x < 0)
elog(ERROR, "negative bitmapset member not allowed");
if (a == NULL)
return NULL;
- Assert(IsA(a, Bitmapset));
-
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
#ifdef REALLOCATE_BITMAPSETS
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
+ a = bms_copy_and_free(a);
#endif
/* member can't exist. Return 'a' unmodified */
}
/*
- * bms_add_members - like bms_union, but left input is recycled
+ * bms_add_members - like bms_union, but left input is recycled when possible
*/
Bitmapset *
bms_add_members(Bitmapset *a, const Bitmapset *b)
int otherlen;
int i;
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
return bms_copy(b);
if (b == NULL)
+ {
+#ifdef REALLOCATE_BITMAPSETS
+ a = bms_copy_and_free(a);
+#endif
+
return a;
+ }
/* Identify shorter and longer input; copy the longer one if needed */
if (a->nwords < b->nwords)
{
}
else
{
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
-
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
-#endif
result = a;
other = b;
}
} while (++i < otherlen);
if (result != a)
pfree(a);
+#ifdef REALLOCATE_BITMAPSETS
+ else
+ result = bms_copy_and_free(result);
+#endif
+
return result;
}
ushiftbits,
wordnum;
- Assert(a == NULL || IsA(a, Bitmapset));
+ Assert(bms_is_valid_set(a));
/* do nothing if nothing is called for, without further checking */
if (upper < lower)
+ {
+#ifdef REALLOCATE_BITMAPSETS
+ a = bms_copy_and_free(a);
+#endif
+
return a;
+ }
if (lower < 0)
elog(ERROR, "negative bitmapset member not allowed");
{
int oldnwords = a->nwords;
int i;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(uwordnum + 1));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
-#else
/* ensure we have enough words to store the upper bit */
a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1));
-#endif
a->nwords = uwordnum + 1;
/* zero out the enlarged portion */
i = oldnwords;
a->words[uwordnum] |= (~(bitmapword) 0) >> ushiftbits;
}
+#ifdef REALLOCATE_BITMAPSETS
+
+ /*
+ * There's no guarantee that the repalloc returned a new pointer, so copy
+ * and free unconditionally here.
+ */
+ a = bms_copy_and_free(a);
+#endif
+
return a;
}
/*
- * bms_int_members - like bms_intersect, but left input is recycled
+ * bms_int_members - like bms_intersect, but left input is recycled when
+ * possible
*/
Bitmapset *
bms_int_members(Bitmapset *a, const Bitmapset *b)
int lastnonzero;
int shortlen;
int i;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
-#endif
-
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
return NULL;
}
-#ifdef REALLOCATE_BITMAPSETS
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
-#endif
-
/* Intersect b into a; we need never copy */
shortlen = Min(a->nwords, b->nwords);
lastnonzero = -1;
/* get rid of trailing zero words */
a->nwords = lastnonzero + 1;
+
+#ifdef REALLOCATE_BITMAPSETS
+ a = bms_copy_and_free(a);
+#endif
+
return a;
}
/*
- * bms_del_members - like bms_difference, but left input is recycled
+ * bms_del_members - delete members in 'a' that are set in 'b'. 'a' is
+ * recycled when possible.
*/
Bitmapset *
bms_del_members(Bitmapset *a, const Bitmapset *b)
{
int i;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
-#endif
- Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0));
- Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
return NULL;
if (b == NULL)
- return a;
-
+ {
#ifdef REALLOCATE_BITMAPSETS
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
+ a = bms_copy_and_free(a);
#endif
+ return a;
+ }
+
/* Remove b's bits from a; we need never copy */
if (a->nwords > b->nwords)
{
a->nwords = lastnonzero + 1;
}
+#ifdef REALLOCATE_BITMAPSETS
+ a = bms_copy_and_free(a);
+#endif
+
return a;
}
/*
- * bms_join - like bms_union, but *both* inputs are recycled
+ * bms_join - like bms_union, but *either* input *may* be recycled
*/
Bitmapset *
bms_join(Bitmapset *a, Bitmapset *b)
Bitmapset *other;
int otherlen;
int i;
-#ifdef REALLOCATE_BITMAPSETS
- Bitmapset *tmp = a;
-#endif
-
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
- Assert(a == NULL || IsA(a, Bitmapset));
- Assert(b == NULL || IsA(b, Bitmapset));
+ Assert(bms_is_valid_set(a));
+ Assert(bms_is_valid_set(b));
/* Handle cases where either input is NULL */
if (a == NULL)
+ {
+#ifdef REALLOCATE_BITMAPSETS
+ b = bms_copy_and_free(b);
+#endif
+
return b;
+ }
if (b == NULL)
- return a;
-
+ {
#ifdef REALLOCATE_BITMAPSETS
- a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords));
- memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords));
- pfree(tmp);
+ a = bms_copy_and_free(a);
#endif
+ return a;
+ }
+
/* Identify shorter and longer input; use longer one as result */
if (a->nwords < b->nwords)
{
} while (++i < otherlen);
if (other != result) /* pure paranoia */
pfree(other);
+
+#ifdef REALLOCATE_BITMAPSETS
+ result = bms_copy_and_free(result);
+#endif
+
return result;
}
int wordnum;
bitmapword mask;
- Assert(a == NULL || IsA(a, Bitmapset));
+ Assert(bms_is_valid_set(a));
if (a == NULL)
return -2;
int ushiftbits;
bitmapword mask;
- Assert(a == NULL || IsA(a, Bitmapset));
+ Assert(bms_is_valid_set(a));
/*
* If set is NULL or if there are no more bits to the right then we've
uint32
bms_hash_value(const Bitmapset *a)
{
+ Assert(bms_is_valid_set(a));
+
if (a == NULL)
return 0; /* All empty sets hash to 0 */
return DatumGetUInt32(hash_any((const unsigned char *) a->words,