
Originally GWBITMASK was capable of extending itself dynamically, a functionality that in practice was never used, and that, in fact, was broken. As GWBITMASK is used for a single purpose inside MaxScale, its size is now fixed at 256 bits, which is sufficient for 256 threads. Its fixed size removes a few potential error situations that now then do not need to be dealt with. And in principle is also more performant. In a separate change, bitmask_free will be renamed to bitmask_finish, as contrary to free-functions in general, the function does not free the argument, but only its data. The implementation will be empty, but the function left in place for symmetry reasons (alloc/free, init/finish).
332 lines
8.3 KiB
C
332 lines
8.3 KiB
C
/*
|
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
|
|
*
|
|
* Change Date: 2019-07-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <gwbitmask.h>
|
|
#include <maxscale/alloc.h>
|
|
|
|
/**
|
|
* @file gwbitmask.c Implementation of bitmask operations for the gateway
|
|
*
|
|
* GWBITMASK is a fixed size bitmask with space for 256 bits.
|
|
*
|
|
* @verbatim
|
|
* Revision History
|
|
*
|
|
* Date Who Description
|
|
* 28/06/13 Mark Riddoch Initial implementation
|
|
* 20/08/15 Martin Brampton Added caveats about limitations (above)
|
|
* 17/10/15 Martin Brampton Added display of bitmask
|
|
* 04/01/16 Martin Brampton Changed bitmask_clear to not lock and return
|
|
* whether bitmask is clear; added bitmask_clear_with_lock.
|
|
*
|
|
* @endverbatim
|
|
*/
|
|
|
|
static int bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit);
|
|
static int bitmask_count_bits_set(GWBITMASK *bitmask);
|
|
|
|
static const unsigned char bitmapclear[8] =
|
|
{
|
|
255 - 1, 255 - 2, 255 - 4, 255 - 8, 255 - 16, 255 - 32, 255 - 64, 255 - 128
|
|
};
|
|
static const unsigned char bitmapset[8] =
|
|
{
|
|
1, 2, 4, 8, 16, 32, 64, 128
|
|
};
|
|
|
|
/**
|
|
* Initialise a bitmask
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
*/
|
|
void
|
|
bitmask_init(GWBITMASK *bitmask)
|
|
{
|
|
spinlock_init(&bitmask->lock);
|
|
memset(bitmask->bits, 0, MXS_BITMASK_SIZE);
|
|
}
|
|
|
|
/**
|
|
* Free a bitmask that is no longer required
|
|
*
|
|
* @param bitmask
|
|
*/
|
|
void
|
|
bitmask_free(GWBITMASK *bitmask)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Set the bit at the specified bit position in the bitmask.
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @param bit Bit to set
|
|
* @return 1 if the bit could be set, 0 otherwise.
|
|
* Setting a bit may fail only if the bit exceeds
|
|
* the maximum length of the bitmask.
|
|
*/
|
|
int
|
|
bitmask_set(GWBITMASK *bitmask, int bit)
|
|
{
|
|
ss_dassert(bit >= 0);
|
|
|
|
spinlock_acquire(&bitmask->lock);
|
|
|
|
if (bit < MXS_BITMASK_LENGTH)
|
|
{
|
|
unsigned char *ptr = bitmask->bits;
|
|
|
|
if (bit >= 8)
|
|
{
|
|
ptr += (bit / 8);
|
|
bit = bit % 8;
|
|
}
|
|
|
|
*ptr |= bitmapset[bit];
|
|
}
|
|
|
|
spinlock_release(&bitmask->lock);
|
|
|
|
return bit < MXS_BITMASK_LENGTH;
|
|
}
|
|
|
|
/**
|
|
* Clear the bit at the specified bit position in the bitmask.
|
|
* Bits beyond the bitmask length are always assumed to be clear, so no
|
|
* action is needed if the bit parameter is beyond the length.
|
|
* Note that this function does not lock the bitmask, but assumes that
|
|
* it is under the exclusive control of the caller. If you want to use the
|
|
* bitmask spinlock to protect access while clearing the bit, then call
|
|
* the alternative bitmask_clear.
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @param bit Bit to clear
|
|
* @return int 1 if the bitmask is all clear after the operation, else 0.
|
|
*/
|
|
int
|
|
bitmask_clear_without_spinlock(GWBITMASK *bitmask, int bit)
|
|
{
|
|
ss_dassert(bit >= 0);
|
|
|
|
unsigned char *ptr = bitmask->bits;
|
|
|
|
if (bit < MXS_BITMASK_LENGTH)
|
|
{
|
|
if (bit >= 8)
|
|
{
|
|
ptr += (bit / 8);
|
|
bit = bit % 8;
|
|
}
|
|
*ptr &= bitmapclear[bit];
|
|
}
|
|
ptr = bitmask->bits;
|
|
for (int i = 0; i < MXS_BITMASK_SIZE; i++)
|
|
{
|
|
if (*(ptr + i) != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Clear the bit at the specified bit position in the bitmask using a spinlock.
|
|
* See bitmask_clear_without_spinlock for more details
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @param bit Bit to clear
|
|
* @return int 1 if the bitmask is all clear after the operation, else 0
|
|
*/
|
|
int
|
|
bitmask_clear(GWBITMASK *bitmask, int bit)
|
|
{
|
|
int result;
|
|
|
|
spinlock_acquire(&bitmask->lock);
|
|
result = bitmask_clear_without_spinlock(bitmask, bit);
|
|
spinlock_release(&bitmask->lock);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return a non-zero value if the bit at the specified bit
|
|
* position in the bitmask is set. If the specified bit is outside the
|
|
* bitmask, it is assumed to be unset; the bitmask is not extended.
|
|
* This function wraps bitmask_isset_without_spinlock with a spinlock.
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @param bit Bit to test
|
|
*/
|
|
int
|
|
bitmask_isset(GWBITMASK *bitmask, int bit)
|
|
{
|
|
int result;
|
|
|
|
spinlock_acquire(&bitmask->lock);
|
|
result = bitmask_isset_without_spinlock(bitmask, bit);
|
|
spinlock_release(&bitmask->lock);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return a non-zero value if the bit at the specified bit
|
|
* position in the bitmask is set. Should be called while holding a
|
|
* lock on the bitmask or having control of it in some other way.
|
|
*
|
|
* Bits beyond the current length are deemed unset.
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @param bit Bit to test
|
|
*/
|
|
static int
|
|
bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit)
|
|
{
|
|
ss_dassert(bit >= 0);
|
|
|
|
if (bit >= MXS_BITMASK_LENGTH)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned char *ptr = bitmask->bits;
|
|
|
|
if (bit >= 8)
|
|
{
|
|
ptr += (bit / 8);
|
|
bit = bit % 8;
|
|
}
|
|
return *ptr & bitmapset[bit];
|
|
}
|
|
|
|
/**
|
|
* Return a non-zero value of the bitmask has no bits set
|
|
* in it. This logic could be defeated if the bitmask is a
|
|
* copy and there was insufficient memory when the copy was
|
|
* made.
|
|
*
|
|
* @param bitmask Pointer the bitmask
|
|
* @return Non-zero if the bitmask has no bits set
|
|
*/
|
|
int
|
|
bitmask_isallclear(GWBITMASK *bitmask)
|
|
{
|
|
unsigned char *ptr = bitmask->bits;
|
|
int result = 1;
|
|
|
|
spinlock_acquire(&bitmask->lock);
|
|
for (int i = 0; i < MXS_BITMASK_SIZE; i++)
|
|
{
|
|
if (*(ptr + i) != 0)
|
|
{
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
spinlock_release(&bitmask->lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Copy the contents of one bitmap to another.
|
|
*
|
|
* @param dest Bitmap tp update
|
|
* @param src Bitmap to copy
|
|
*/
|
|
void
|
|
bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
|
|
{
|
|
spinlock_acquire(&src->lock);
|
|
spinlock_acquire(&dest->lock);
|
|
memcpy(dest->bits, src->bits, MXS_BITMASK_SIZE);
|
|
spinlock_release(&dest->lock);
|
|
spinlock_release(&src->lock);
|
|
}
|
|
|
|
/**
|
|
* Return a comma separated list of the numbers of the bits that are set in
|
|
* a bitmask, numbering starting at zero. The returned string must be
|
|
* freed by the caller (unless it is null on account of memory allocation
|
|
* failure).
|
|
*
|
|
* @param bitmask Bitmap to make readable
|
|
* @return pointer to the newly allocated string, or null if no memory
|
|
*/
|
|
char *
|
|
bitmask_render_readable(GWBITMASK *bitmask)
|
|
{
|
|
static const char empty[] = "No bits are set";
|
|
char *result;
|
|
|
|
spinlock_acquire(&bitmask->lock);
|
|
int count_set = bitmask_count_bits_set(bitmask);
|
|
if (count_set)
|
|
{
|
|
result = MXS_MALLOC(1 + (4 * count_set));
|
|
if (result)
|
|
{
|
|
result[0] = 0;
|
|
for (int i = 0; i < MXS_BITMASK_LENGTH; i++)
|
|
{
|
|
if (bitmask_isset_without_spinlock(bitmask, i))
|
|
{
|
|
char onebit[5];
|
|
sprintf(onebit, "%d,", i);
|
|
strcat(result, onebit);
|
|
}
|
|
}
|
|
result[strlen(result) - 1] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = MXS_MALLOC(sizeof(empty));
|
|
if (result)
|
|
{
|
|
strcpy(result, empty);
|
|
}
|
|
}
|
|
spinlock_release(&bitmask->lock);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return a count of the number of bits set in a bitmask. Helpful for setting
|
|
* the size of string needed to show the set bits in readable form.
|
|
*
|
|
* @param bitmask Bitmap whose bits are to be counted
|
|
* @return int Number of set bits
|
|
*/
|
|
static int
|
|
bitmask_count_bits_set(GWBITMASK *bitmask)
|
|
{
|
|
static const unsigned char oneBits[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
|
|
unsigned char partresults;
|
|
int result = 0;
|
|
unsigned char *ptr, *eptr;
|
|
|
|
ptr = bitmask->bits;
|
|
eptr = ptr + MXS_BITMASK_SIZE;
|
|
while (ptr < eptr)
|
|
{
|
|
partresults = oneBits[*ptr & 0x0f];
|
|
partresults += oneBits[*ptr >> 4];
|
|
result += partresults;
|
|
ptr++;
|
|
}
|
|
return result;
|
|
}
|