diff --git a/include/QF/set.h b/include/QF/set.h index 57c8342de..1d59ed395 100644 --- a/include/QF/set.h +++ b/include/QF/set.h @@ -162,6 +162,15 @@ void set_delete_r (set_pool_t *set_pool, set_t *set); */ void set_expand (set_t *set, unsigned size); +/** Shrink the set's backing memory to the minimum required to hold the set. + + This does not affect (nor is affected by) whether the set is an infinite + set. + + \param set The set to trim +*/ +void set_trim (set_t *set); + /** Add an element to a set. It is not an error to add an element that is already a member of the set. diff --git a/libs/util/set.c b/libs/util/set.c index baabe1057..9b59172a1 100644 --- a/libs/util/set.c +++ b/libs/util/set.c @@ -128,6 +128,30 @@ set_expand (set_t *set, unsigned size) free (map); } +void +set_trim (set_t *set) +{ + if (set->map == set->defmap) { + return; + } + unsigned words = SET_WORDS (set); + + while (words > SET_DEFMAP_SIZE && !set->map[words - 1]) { + words--; + set->size -= SET_BITS; + } + if (words > SET_DEFMAP_SIZE) { + set_bits_t *map = realloc (set->map, words * sizeof (set_bits_t)); + if (map && map != set->map) { + set->map = map; + } + } else { + memcpy (set->defmap, set->map, sizeof (set->defmap)); + free (set->map); + set->map = set->defmap; + } +} + inline set_t * set_new_size_r (set_pool_t *set_pool, unsigned size) { @@ -204,13 +228,14 @@ _set_union (set_t *dst, const set_t *src) static set_t * _set_intersection (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + for (i = 0; i < words; i++) dst->map[i] &= src->map[i]; + // if dst is larger than src, then none of the excess elements in dst + // can be in the intersection for ( ; i < SET_WORDS (dst); i++) dst->map[i] = 0; return dst; @@ -219,26 +244,37 @@ _set_intersection (set_t *dst, const set_t *src) static set_t * _set_difference (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + for (i = 0; i < words; i++) dst->map[i] &= ~src->map[i]; + // if src is larger than dst, excess elements in src cannot be in dst thus + // there is nothing to remove + // if dst is larger than src, there is nothing to remove regardless of what + // is in src return dst; } static set_t * _set_reverse_difference (set_t *dst, const set_t *src) { - unsigned size; + unsigned words; unsigned i; - size = max (dst->size, src->size); - set_expand (dst, size); - for (i = 0; i < SET_WORDS (src); i++) + words = min (SET_WORDS (dst), SET_WORDS (src)); + set_expand (dst, src->size); + for (i = 0; i < words; i++) dst->map[i] = ~dst->map[i] & src->map[i]; + // if src is larger than dst, then dst cannot remove the excess elements + // from src and thus the src elements must be copied + for ( ; i < SET_WORDS (src); i++) + dst->map[i] = src->map[i]; + // if dst is larger than src, then the excess elements in dst must be + // removed + for ( ; i < SET_WORDS (dst); i++) + dst->map[i] = 0; return dst; } diff --git a/libs/util/test/test-set.c b/libs/util/test/test-set.c index 4569c0046..412517d22 100644 --- a/libs/util/test/test-set.c +++ b/libs/util/test/test-set.c @@ -250,7 +250,30 @@ struct { {make_not_1_2, make_0_1, set_union, check_count, 1, "{0 1 3 ...}"}, {make_not_1_2, make_0_1, set_intersection, check_count, 1, "{0}"}, {make_not_1_2, make_0_1, set_difference, check_count, 3, "{3 ...}"}, - {make_not_1_2, make_0_1, set_reverse_difference, check_count, 1, "{1}"}, + {make_not_1_2, make_0_1, set_reverse_difference, check_count, 1, "{1}"},//76 + {make_SIZE, make_not_1_2, set_union, check_size, SIZE + SET_BITS, + "{0 3 ...}"}, + {make_SIZE, make_not_1_2, set_intersection, check_size, SIZE + SET_BITS, + "{64}"}, + {make_SIZE, make_not_1_2, set_difference, check_size, SIZE + SET_BITS, + "{}"}, + {make_SIZE, make_not_1_2, set_reverse_difference, check_size, + SIZE + SET_BITS, + "{0 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}" + },//80 + {make_not_1_2, make_SIZE, set_union, check_size, SIZE, "{0 3 ...}"}, + {make_not_1_2, make_SIZE, set_intersection, check_size, SIZE + SET_BITS, + "{64}"}, + {make_not_1_2, make_SIZE, set_difference, check_size, SIZE + SET_BITS, + "{0 3 4 5 6 7 8 9 10 11 12 13 14 15" + " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" + " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" + " 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}" + }, + {make_not_1_2, make_SIZE, set_reverse_difference, check_size, SIZE, "{}"}, }; #define num_tests (sizeof (tests) / sizeof (tests[0])) @@ -270,6 +293,8 @@ main (int argc, const char **argv) tests[9].str_expect = tests[5].str_expect; tests[10].str_expect = tests[5].str_expect; tests[11].str_expect = tests[5].str_expect; + tests[78].str_expect = tests[5].str_expect; + tests[82].str_expect = tests[5].str_expect; str = dstring_new (); for (i = 0; i < SIZE; i++) { @@ -278,6 +303,15 @@ main (int argc, const char **argv) dstring_appendstr (str, "}"); tests[6].str_expect = dstring_freeze (str); + str = dstring_new (); + dasprintf (str, "{0"); + for (i = 3; i < SIZE; i++) { + dasprintf (str, " %zd", i); + } + dasprintf (str, " %zd ...}", SIZE + 1); + tests[80].str_expect = dstring_freeze (str); + tests[83].str_expect = tests[80].str_expect; + for (i = 0; i < num_tests; i++) { set_t *s1, *s2 = 0; const char *set_str;