@ -5,10 +5,8 @@
*
*
* A bitmap set can represent any set of nonnegative integers , although
* A bitmap set can represent any set of nonnegative integers , although
* it is mainly intended for sets where the maximum value is not large ,
* 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
* say at most a few hundred . By convention , we always represent the
* accepted by all operations to represent the empty set . ( But beware
* empty set by a NULL pointer .
* that this is not the only representation of the empty set . Use
* bms_is_empty ( ) in preference to testing for NULL . )
*
*
*
*
* Copyright ( c ) 2003 - 2023 , PostgreSQL Global Development Group
* Copyright ( c ) 2003 - 2023 , PostgreSQL Global Development Group
@ -66,6 +64,8 @@
# error "invalid BITS_PER_BITMAPWORD"
# error "invalid BITS_PER_BITMAPWORD"
# endif
# endif
static bool bms_is_empty_internal ( const Bitmapset * a ) ;
/*
/*
* bms_copy - make a palloc ' d copy of a bitmapset
* bms_copy - make a palloc ' d copy of a bitmapset
@ -104,10 +104,10 @@ bms_equal(const Bitmapset *a, const Bitmapset *b)
{
{
if ( b = = NULL )
if ( b = = NULL )
return true ;
return true ;
return bms_is_empty ( b ) ;
return false ;
}
}
else if ( b = = NULL )
else if ( b = = NULL )
return bms_is_empty ( a ) ;
return false ;
/* Identify shorter and longer input */
/* Identify shorter and longer input */
if ( a - > nwords < = b - > nwords )
if ( a - > nwords < = b - > nwords )
{
{
@ -151,9 +151,9 @@ bms_compare(const Bitmapset *a, const Bitmapset *b)
/* Handle cases where either input is NULL */
/* Handle cases where either input is NULL */
if ( a = = NULL )
if ( a = = NULL )
return bms_is_empty ( b ) ? 0 : - 1 ;
return ( b = = NULL ) ? 0 : - 1 ;
else if ( b = = NULL )
else if ( b = = NULL )
return bms_is_empty ( a ) ? 0 : + 1 ;
return + 1 ;
/* Handle cases where one input is longer than the other */
/* Handle cases where one input is longer than the other */
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
for ( i = shortlen ; i < a - > nwords ; i + + )
for ( i = shortlen ; i < a - > nwords ; i + + )
@ -282,6 +282,12 @@ bms_intersect(const Bitmapset *a, const Bitmapset *b)
resultlen = result - > nwords ;
resultlen = result - > nwords ;
for ( i = 0 ; i < resultlen ; i + + )
for ( i = 0 ; i < resultlen ; i + + )
result - > words [ i ] & = other - > words [ i ] ;
result - > words [ i ] & = other - > words [ i ] ;
/* If we computed an empty result, we must return NULL */
if ( bms_is_empty_internal ( result ) )
{
pfree ( result ) ;
return NULL ;
}
return result ;
return result ;
}
}
@ -300,12 +306,22 @@ bms_difference(const Bitmapset *a, const Bitmapset *b)
return NULL ;
return NULL ;
if ( b = = NULL )
if ( b = = NULL )
return bms_copy ( a ) ;
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 */
/* Copy the left input */
result = bms_copy ( a ) ;
result = bms_copy ( a ) ;
/* And remove b's bits from result */
/* And remove b's bits from result */
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
for ( i = 0 ; i < shortlen ; i + + )
for ( i = 0 ; i < shortlen ; i + + )
result - > words [ i ] & = ~ b - > words [ i ] ;
result - > words [ i ] & = ~ b - > words [ i ] ;
/* Need not check for empty result, since we handled that case above */
return result ;
return result ;
}
}
@ -323,7 +339,7 @@ bms_is_subset(const Bitmapset *a, const Bitmapset *b)
if ( a = = NULL )
if ( a = = NULL )
return true ; /* empty set is a subset of anything */
return true ; /* empty set is a subset of anything */
if ( b = = NULL )
if ( b = = NULL )
return bms_is_empty ( a ) ;
return false ;
/* Check common words */
/* Check common words */
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
for ( i = 0 ; i < shortlen ; i + + )
for ( i = 0 ; i < shortlen ; i + + )
@ -362,10 +378,10 @@ bms_subset_compare(const Bitmapset *a, const Bitmapset *b)
{
{
if ( b = = NULL )
if ( b = = NULL )
return BMS_EQUAL ;
return BMS_EQUAL ;
return bms_is_empty ( b ) ? BMS_EQUAL : BMS_SUBSET1 ;
return BMS_SUBSET1 ;
}
}
if ( b = = NULL )
if ( b = = NULL )
return bms_is_empty ( a ) ? BMS_EQUAL : BMS_SUBSET2 ;
return BMS_SUBSET2 ;
/* Check common words */
/* Check common words */
result = BMS_EQUAL ; /* status so far */
result = BMS_EQUAL ; /* status so far */
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
@ -554,7 +570,7 @@ bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
if ( a = = NULL )
if ( a = = NULL )
return false ;
return false ;
if ( b = = NULL )
if ( b = = NULL )
return ! bms_is_empty ( a ) ;
return true ;
/* Check words in common */
/* Check words in common */
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
for ( i = 0 ; i < shortlen ; i + + )
for ( i = 0 ; i < shortlen ; i + + )
@ -696,18 +712,18 @@ bms_membership(const Bitmapset *a)
}
}
/*
/*
* bms_is_empty - is a set empty ?
* bms_is_empty_internal - is a set empty ?
*
*
* This is even faster than bms_membership ( ) .
* This is now used only locally , to detect cases where a function has
* computed an empty set that we must now get rid of . Hence , we can
* assume the input isn ' t NULL .
*/
*/
bool
static bool
bms_is_empty ( const Bitmapset * a )
bms_is_empty_internal ( const Bitmapset * a )
{
{
int nwords ;
int nwords ;
int wordnum ;
int wordnum ;
if ( a = = NULL )
return true ;
nwords = a - > nwords ;
nwords = a - > nwords ;
for ( wordnum = 0 ; wordnum < nwords ; wordnum + + )
for ( wordnum = 0 ; wordnum < nwords ; wordnum + + )
{
{
@ -786,6 +802,12 @@ bms_del_member(Bitmapset *a, int x)
bitnum = BITNUM ( x ) ;
bitnum = BITNUM ( x ) ;
if ( wordnum < a - > nwords )
if ( wordnum < a - > nwords )
a - > words [ wordnum ] & = ~ ( ( bitmapword ) 1 < < bitnum ) ;
a - > words [ wordnum ] & = ~ ( ( bitmapword ) 1 < < bitnum ) ;
/* If we computed an empty result, we must return NULL */
if ( bms_is_empty_internal ( a ) )
{
pfree ( a ) ;
return NULL ;
}
return a ;
return a ;
}
}
@ -922,6 +944,12 @@ bms_int_members(Bitmapset *a, const Bitmapset *b)
a - > words [ i ] & = b - > words [ i ] ;
a - > words [ i ] & = b - > words [ i ] ;
for ( ; i < a - > nwords ; i + + )
for ( ; i < a - > nwords ; i + + )
a - > words [ i ] = 0 ;
a - > words [ i ] = 0 ;
/* If we computed an empty result, we must return NULL */
if ( bms_is_empty_internal ( a ) )
{
pfree ( a ) ;
return NULL ;
}
return a ;
return a ;
}
}
@ -943,6 +971,12 @@ bms_del_members(Bitmapset *a, const Bitmapset *b)
shortlen = Min ( a - > nwords , b - > nwords ) ;
shortlen = Min ( a - > nwords , b - > nwords ) ;
for ( i = 0 ; i < shortlen ; i + + )
for ( i = 0 ; i < shortlen ; i + + )
a - > words [ i ] & = ~ b - > words [ i ] ;
a - > words [ i ] & = ~ b - > words [ i ] ;
/* If we computed an empty result, we must return NULL */
if ( bms_is_empty_internal ( a ) )
{
pfree ( a ) ;
return NULL ;
}
return a ;
return a ;
}
}