cogl-bitmask: Use longs instead of ints

Instead of storing only 31 bits in the pointer for a CoglBitmask, it
now assumes it can store a whole unsigned long minus the one bit used
to mark whether it has been converted to a GArray or not. This works
on the assumption that we can cast an unsigned long to a pointer and
back without losing information which I think should be true for any
platforms that Cogl is interested in. This has the advantage that on
64-bit architectures we can store 63 bits before we have to resort to
using a GArray at no extra cost. The values in the GArray are now
stored as unsigned longs as well on the assumption that it is more
efficient to load and store data in chunks of longs rather than ints.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2011-10-28 14:58:02 +01:00
parent 70eaf037e9
commit 93321d9b89
2 changed files with 80 additions and 55 deletions

View File

@ -33,6 +33,17 @@
#include "cogl-bitmask.h"
/* This code assumes that we can cast an unsigned long to a pointer
and back without losing any data */
G_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *));
#define ARRAY_INDEX(bit_num) \
((bit_num) / (sizeof (unsigned long) * 8))
#define BIT_INDEX(bit_num) \
((bit_num) & (sizeof (unsigned long) * 8 - 1))
#define BIT_MASK(bit_num) \
(1UL << BIT_INDEX (bit_num))
gboolean
_cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
unsigned int bit_num)
@ -41,22 +52,23 @@ _cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
/* If the index is off the end of the array then assume the bit is
not set */
if (bit_num >= sizeof (unsigned int) * 8 * array->len)
if (bit_num >= sizeof (unsigned long) * 8 * array->len)
return FALSE;
else
return !!(g_array_index (array, unsigned int,
bit_num / (sizeof (unsigned int) * 8)) &
(1 << (bit_num % (sizeof (unsigned int) * 8))));
return !!(g_array_index (array, unsigned long, ARRAY_INDEX (bit_num)) &
BIT_MASK (bit_num));
}
static void
_cogl_bitmask_convert_to_array (CoglBitmask *bitmask)
{
GArray *array;
/* Fetch the old values, ignoring the least significant bit */
unsigned int old_values = GPOINTER_TO_UINT (*bitmask) >> 1;
/* Fetch the old values */
unsigned long old_values = _cogl_bitmask_to_bits (bitmask);
array = g_array_new (FALSE, TRUE, sizeof (unsigned int));
array = g_array_new (FALSE, /* not zero-terminated */
TRUE, /* do clear new entries */
sizeof (unsigned long));
/* Copy the old values back in */
g_array_append_val (array, old_values);
@ -69,7 +81,8 @@ _cogl_bitmask_set_in_array (CoglBitmask *bitmask,
gboolean value)
{
GArray *array;
unsigned int array_index, new_value_mask;
unsigned int array_index;
unsigned long new_value_mask;
/* If the bitmask is not already an array then we need to allocate one */
if (!_cogl_bitmask_has_array (bitmask))
@ -77,17 +90,17 @@ _cogl_bitmask_set_in_array (CoglBitmask *bitmask,
array = (GArray *) *bitmask;
array_index = bit_num / (sizeof (unsigned int) * 8);
array_index = ARRAY_INDEX (bit_num);
/* Grow the array if necessary. This will clear the new data */
if (array_index >= array->len)
g_array_set_size (array, array_index + 1);
new_value_mask = 1 << (bit_num % (sizeof (unsigned int) * 8));
new_value_mask = BIT_MASK (bit_num);
if (value)
g_array_index (array, unsigned int, array_index) |= new_value_mask;
g_array_index (array, unsigned long, array_index) |= new_value_mask;
else
g_array_index (array, unsigned int, array_index) &= ~new_value_mask;
g_array_index (array, unsigned long, array_index) &= ~new_value_mask;
}
void
@ -109,8 +122,8 @@ _cogl_bitmask_set_bits (CoglBitmask *dst,
g_array_set_size (dst_array, src_array->len);
for (i = 0; i < src_array->len; i++)
g_array_index (dst_array, unsigned int, i) |=
g_array_index (src_array, unsigned int, i);
g_array_index (dst_array, unsigned long, i) |=
g_array_index (src_array, unsigned long, i);
}
else if (_cogl_bitmask_has_array (dst))
{
@ -118,12 +131,12 @@ _cogl_bitmask_set_bits (CoglBitmask *dst,
dst_array = (GArray *) *dst;
g_array_index (dst_array, unsigned int, 0) |=
(GPOINTER_TO_UINT (*src) >> 1);
g_array_index (dst_array, unsigned long, 0) |=
_cogl_bitmask_to_bits (src);
}
else
*dst = GUINT_TO_POINTER (GPOINTER_TO_UINT (*dst) |
GPOINTER_TO_UINT (*src));
*dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) |
_cogl_bitmask_to_bits (src));
}
void
@ -144,9 +157,9 @@ _cogl_bitmask_set_range_in_array (CoglBitmask *bitmask,
array = (GArray *) *bitmask;
/* Get the array index of the top most value that will be touched */
array_index = (n_bits - 1) / (sizeof (unsigned int) * 8);
array_index = ARRAY_INDEX (n_bits - 1);
/* Get the bit index of the top most value */
bit_index = (n_bits - 1) % (sizeof (unsigned int) * 8);
bit_index = BIT_INDEX (n_bits - 1);
/* Grow the array if necessary. This will clear the new data */
if (array_index >= array->len)
g_array_set_size (array, array_index + 1);
@ -154,20 +167,19 @@ _cogl_bitmask_set_range_in_array (CoglBitmask *bitmask,
if (value)
{
/* Set the bits that are touching this index */
g_array_index (array, unsigned int, array_index) |=
~(unsigned int) 0 >> (sizeof (unsigned int) * 8 - 1 - bit_index);
g_array_index (array, unsigned long, array_index) |=
~0UL >> (sizeof (unsigned long) * 8 - 1 - bit_index);
/* Set all of the bits in any lesser indices */
memset (array->data, 0xff, sizeof (unsigned int) * array_index);
memset (array->data, 0xff, sizeof (unsigned long) * array_index);
}
else
{
/* Clear the bits that are touching this index */
g_array_index (array, unsigned int, array_index) &=
~(unsigned int) 1 << bit_index;
g_array_index (array, unsigned long, array_index) &= ~1UL << bit_index;
/* Clear all of the bits in any lesser indices */
memset (array->data, 0x00, sizeof (unsigned int) * array_index);
memset (array->data, 0x00, sizeof (unsigned long) * array_index);
}
}
@ -190,8 +202,8 @@ _cogl_bitmask_xor_bits (CoglBitmask *dst,
g_array_set_size (dst_array, src_array->len);
for (i = 0; i < src_array->len; i++)
g_array_index (dst_array, unsigned int, i) ^=
g_array_index (src_array, unsigned int, i);
g_array_index (dst_array, unsigned long, i) ^=
g_array_index (src_array, unsigned long, i);
}
else if (_cogl_bitmask_has_array (dst))
{
@ -199,12 +211,12 @@ _cogl_bitmask_xor_bits (CoglBitmask *dst,
dst_array = (GArray *) *dst;
g_array_index (dst_array, unsigned int, 0) ^=
(GPOINTER_TO_UINT (*src) >> 1);
g_array_index (dst_array, unsigned long, 0) ^=
_cogl_bitmask_to_bits (src);
}
else
*dst = GUINT_TO_POINTER ((GPOINTER_TO_UINT (*dst) ^
GPOINTER_TO_UINT (*src)) | 1);
*dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) ^
_cogl_bitmask_to_bits (src));
}
void
@ -212,7 +224,7 @@ _cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask)
{
GArray *array = (GArray *) *bitmask;
memset (array->data, 0, sizeof (unsigned int) * array->len);
memset (array->data, 0, sizeof (unsigned long) * array->len);
}
void
@ -227,13 +239,15 @@ _cogl_bitmask_foreach (const CoglBitmask *bitmask,
for (array_index = 0; array_index < array->len; array_index++)
{
unsigned int mask = g_array_index (array, unsigned int, array_index);
unsigned long mask =
g_array_index (array, unsigned long, array_index);
int bit = 0;
while (mask)
{
if (mask & 1)
func (array_index * sizeof (unsigned int) * 8 + bit, user_data);
if (mask & 1UL)
func (array_index * sizeof (unsigned long) * 8 + bit,
user_data);
bit++;
mask >>= 1;
@ -242,12 +256,12 @@ _cogl_bitmask_foreach (const CoglBitmask *bitmask,
}
else
{
unsigned int mask = GPOINTER_TO_UINT (*bitmask) >> 1;
unsigned long mask = _cogl_bitmask_to_bits (bitmask);
int bit = 0;
while (mask)
{
if (mask & 1)
if (mask & 1UL)
func (bit, user_data);
bit++;

View File

@ -36,14 +36,17 @@ G_BEGIN_DECLS
* be allocated on the stack but it must be initialised with
* _cogl_bitmask_init() before use and then destroyed with
* _cogl_bitmask_destroy(). A CoglBitmask will try to avoid allocating
* any memory unless more than 31 bits are needed.
* any memory unless more than the number of bits in a long - 1 bits
* are needed.
*
* Internally a CoglBitmask is a pointer. If the least significant bit
* of the pointer is 1 then the rest of the bits are directly used as
* part of the bitmask, otherwise it is a pointer to a GArray of
* unsigned ints. This relies on the fact the g_malloc will return a
* pointer aligned to at least two bytes (so that the least
* significant bit of the address is always 0)
* significant bit of the address is always 0). It also assumes that
* the size of a pointer is always greater than or equal to the size
* of a long (although there is a compile time assert to verify this).
*
* If the maximum possible bit number in the set is known at compile
* time, it may make more sense to use the macros in cogl-flags.h
@ -52,13 +55,23 @@ G_BEGIN_DECLS
typedef struct _CoglBitmaskImaginaryType *CoglBitmask;
/* These are internal helper macros */
#define _cogl_bitmask_to_number(bitmask) \
((unsigned long) (*bitmask))
#define _cogl_bitmask_to_bits(bitmask) \
(_cogl_bitmask_to_number (bitmask) >> 1UL)
/* The least significant bit is set to mark that no array has been
allocated yet */
#define _cogl_bitmask_from_bits(bits) \
((void *) ((((unsigned long) (bits)) << 1UL) | 1UL))
/* Internal helper macro to determine whether this bitmask has a
GArray allocated or whether the pointer is just used directly */
#define _cogl_bitmask_has_array(bitmask) \
(!(GPOINTER_TO_UINT (*bitmask) & 1))
(!(_cogl_bitmask_to_number (bitmask) & 1UL))
/* Number of bits we can use before needing to allocate an array */
#define COGL_BITMASK_MAX_DIRECT_BITS (sizeof (unsigned int) * 8 - 1)
#define COGL_BITMASK_MAX_DIRECT_BITS (sizeof (unsigned long) * 8 - 1)
/*
* _cogl_bitmask_init:
@ -68,10 +81,8 @@ typedef struct _CoglBitmaskImaginaryType *CoglBitmask;
* bitmask functions are called. Initially all of the values are
* zero
*/
/* Set the last significant bit to mark that no array has been
allocated yet */
#define _cogl_bitmask_init(bitmask) \
G_STMT_START { *(bitmask) = GUINT_TO_POINTER (1); } G_STMT_END
G_STMT_START { *(bitmask) = _cogl_bitmask_from_bits (0); } G_STMT_END
gboolean
_cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
@ -144,7 +155,7 @@ _cogl_bitmask_get (const CoglBitmask *bitmask, unsigned int bit_num)
else if (bit_num >= COGL_BITMASK_MAX_DIRECT_BITS)
return FALSE;
else
return !!(GPOINTER_TO_UINT (*bitmask) & (1 << (bit_num + 1)));
return !!(_cogl_bitmask_to_bits (bitmask) & (1UL << bit_num));
}
/*
@ -162,11 +173,11 @@ _cogl_bitmask_set (CoglBitmask *bitmask, unsigned int bit_num, gboolean value)
bit_num >= COGL_BITMASK_MAX_DIRECT_BITS)
_cogl_bitmask_set_in_array (bitmask, bit_num, value);
else if (value)
*bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) |
(1 << (bit_num + 1)));
*bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) |
(1UL << bit_num));
else
*bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) &
~(1 << (bit_num + 1)));
*bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) &
~(1UL << bit_num));
}
/*
@ -186,11 +197,11 @@ _cogl_bitmask_set_range (CoglBitmask *bitmask,
n_bits > COGL_BITMASK_MAX_DIRECT_BITS)
_cogl_bitmask_set_range_in_array (bitmask, n_bits, value);
else if (value)
*bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) |
~(~(unsigned int) 1 << n_bits));
*bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) |
~(~0UL << n_bits));
else
*bitmask = GUINT_TO_POINTER (GPOINTER_TO_UINT (*bitmask) &
((~(unsigned int) 1 << n_bits) | 1));
*bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) &
(~0UL << n_bits));
}
/*
@ -218,7 +229,7 @@ _cogl_bitmask_clear_all (CoglBitmask *bitmask)
if (_cogl_bitmask_has_array (bitmask))
_cogl_bitmask_clear_all_in_array (bitmask);
else
*bitmask = GUINT_TO_POINTER (1);
*bitmask = _cogl_bitmask_from_bits (0);
}
G_END_DECLS