[util] Expose set_expand and fix an out-by-one bug

Having set_expand exposed is useful for loading data into a set.

However, it turns out there was a bug in its size calculation in that
when the requested set size was a multiple of SET_BITS (and greater than
the current set size), the new set size one be SET_BITS larger than
requested. There's now some tests for this :)
This commit is contained in:
Bill Currie 2021-07-26 09:54:03 +09:00
parent 7995f59a90
commit 03921c03c5
3 changed files with 52 additions and 9 deletions

View file

@ -57,6 +57,10 @@ typedef uint32_t set_bits_t;
#define SET_ONE ((set_bits_t) 1)
#define SET_TEST_MEMBER(s, x) \
((s)->map[(x) / SET_BITS] & (SET_ONE << ((x) % SET_BITS)))
#define SET_STATIC_INIT(x, alloc) { \
.size = SET_SIZE (x), \
.map = alloc (SET_SIZE (x) / 8), \
}
/** Represent a set using a bitmap.
@ -129,9 +133,11 @@ set_t *set_new_r (set_pool_t *set_pool);
\param size The number of elements for which space is to be allocated.
\return The newly created, empty set.
\note \a size is the actual amount of elements, not the number of the
highest element (ie, for values 0..n, size is n + 1).
*/
set_t *set_new_size (int size);
set_t *set_new_size_r (set_pool_t *set_pool, int size);
set_t *set_new_size (unsigned size);
set_t *set_new_size_r (set_pool_t *set_pool, unsigned size);
/** Delete a set that is no longer needed.
@ -140,6 +146,18 @@ set_t *set_new_size_r (set_pool_t *set_pool, int size);
void set_delete (set_t *set);
void set_delete_r (set_pool_t *set_pool, set_t *set);
/** Pre-expand a set with space for the specified element
Has no effect if the set is already large enough to hold the specified
element.
\param set The set to be expanded
\param size The minimum number of elements representable by the set
\note \a size is the actual amount of elements, not the number of the
highest element (ie, for values 0..n, size is n + 1).
*/
void set_expand (set_t *set, unsigned size);
/** Add an element to a set.
It is not an error to add an element that is already a member of the set.

View file

@ -111,16 +111,15 @@ set_delete (set_t *set)
set_delete_r (&static_set_pool, set);
}
static void
set_expand (set_t *set, unsigned x)
void
set_expand (set_t *set, unsigned size)
{
set_bits_t *map = set->map;
size_t size;
if (x <= set->size)
if (size <= set->size)
return;
size = SET_SIZE (x);
size = SET_SIZE (size - 1);
set->map = malloc (size / 8);
memcpy (set->map, map, set->size / 8);
memset (set->map + SET_WORDS (set), 0, (size - set->size) / 8);
@ -130,7 +129,7 @@ set_expand (set_t *set, unsigned x)
}
inline set_t *
set_new_size_r (set_pool_t *set_pool, int size)
set_new_size_r (set_pool_t *set_pool, unsigned size)
{
set_t *set;
@ -141,7 +140,7 @@ set_new_size_r (set_pool_t *set_pool, int size)
}
set_t *
set_new_size (int size)
set_new_size (unsigned size)
{
return set_new_size_r (&static_set_pool, size);
}

View file

@ -109,6 +109,27 @@ make_not_55 (void)
return set_invert (make_55 ());
}
static set_t *
expand_3xSIZEm1 (set_t *set, const set_t *x)
{
set_expand (set, 3 * SIZE - 1);
return set;
}
static set_t *
expand_3xSIZEp1 (set_t *set, const set_t *x)
{
set_expand (set, 3 * SIZE + 1);
return set;
}
static set_t *
expand_3xSIZE (set_t *set, const set_t *x)
{
set_expand (set, 3 * SIZE);
return set;
}
struct {
setup_func set1;
setup_func set2;
@ -135,6 +156,11 @@ struct {
{make_0_to_SIZEm1, make_everything, set_reverse_difference,
check_size, SIZE, "{64 ...}"
},
{make_SIZE, 0, expand_3xSIZEm1, check_size, 3 * SIZE,
"{64}"},
{make_SIZE, 0, expand_3xSIZE, check_size, 3 * SIZE, "{64}"},
{make_SIZE, 0, expand_3xSIZEp1, check_size,
3 * SIZE + SET_BITS, "{64}"},
{make_everything, make_empty, 0, set_is_subset, 1, 0},
{make_everything, make_empty, 0, set_is_equivalent, 0, 0},
{make_everything, make_empty, 0, set_is_intersecting, 0, 0},