mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 21:02:50 +00:00
34bcf7faab
I always wanted these, but as gcc now provides warnings for functions that could do with such attributes, finding all the functions is much easier.
639 lines
12 KiB
C
639 lines
12 KiB
C
/*
|
|
set.c
|
|
|
|
Set manipulation.
|
|
|
|
Copyright (C) 2012 Bill Currie <bill@taniwha.org>
|
|
|
|
Author: Bill Currie <bill@taniwha.org>
|
|
Date: 2012/8/4
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "QF/alloc.h"
|
|
#include "QF/dstring.h"
|
|
#include "QF/mathlib.h"
|
|
#include "QF/set.h"
|
|
|
|
static set_pool_t static_set_pool = {0, 0};
|
|
|
|
static set_iter_t *
|
|
new_setiter (set_pool_t *set_pool)
|
|
{
|
|
set_iter_t *set_iter;
|
|
ALLOC (16, set_iter_t, set_pool->set_iter, set_iter);
|
|
return set_iter;
|
|
}
|
|
|
|
static void
|
|
delete_setiter (set_pool_t *set_pool, set_iter_t *set_iter)
|
|
{
|
|
FREE (set_pool->set_iter, set_iter);
|
|
}
|
|
|
|
void
|
|
set_del_iter (set_iter_t *set_iter)
|
|
{
|
|
delete_setiter (&static_set_pool, set_iter);
|
|
}
|
|
|
|
void
|
|
set_del_iter_r (set_pool_t *set_pool, set_iter_t *set_iter)
|
|
{
|
|
delete_setiter (set_pool, set_iter);
|
|
}
|
|
|
|
void
|
|
set_pool_init (set_pool_t *set_pool)
|
|
{
|
|
set_pool->set_freelist = 0;
|
|
set_pool->set_iter_freelist = 0;
|
|
}
|
|
|
|
inline set_t *
|
|
set_new_r (set_pool_t *set_pool)
|
|
{
|
|
set_t *set;
|
|
|
|
ALLOC (16, set_t, set_pool->set, set);
|
|
set->size = sizeof (set->defmap) * 8;
|
|
set->map = set->defmap;
|
|
return set;
|
|
}
|
|
|
|
set_t *
|
|
set_new (void)
|
|
{
|
|
return set_new_r (&static_set_pool);
|
|
}
|
|
|
|
void
|
|
set_delete_r (set_pool_t *set_pool, set_t *set)
|
|
{
|
|
if (set->map != set->defmap)
|
|
free (set->map);
|
|
FREE (set_pool->set, set);
|
|
}
|
|
|
|
void
|
|
set_delete (set_t *set)
|
|
{
|
|
set_delete_r (&static_set_pool, set);
|
|
}
|
|
|
|
static void
|
|
set_expand (set_t *set, unsigned x)
|
|
{
|
|
set_bits_t *map = set->map;
|
|
size_t size;
|
|
|
|
if (x <= set->size)
|
|
return;
|
|
|
|
size = SET_SIZE (x);
|
|
set->map = malloc (size / 8);
|
|
memcpy (set->map, map, set->size / 8);
|
|
memset (set->map + SET_WORDS (set), 0, (size - set->size) / 8);
|
|
set->size = size;
|
|
if (map != set->defmap)
|
|
free (map);
|
|
}
|
|
|
|
inline set_t *
|
|
set_new_size_r (set_pool_t *set_pool, int size)
|
|
{
|
|
set_t *set;
|
|
|
|
set = set_new_r (set_pool);
|
|
set_expand (set, size);
|
|
|
|
return set;
|
|
}
|
|
|
|
set_t *
|
|
set_new_size (int size)
|
|
{
|
|
return set_new_size_r (&static_set_pool, size);
|
|
}
|
|
|
|
static inline void
|
|
_set_add (set_t *set, unsigned x)
|
|
{
|
|
if (x >= set->size)
|
|
set_expand (set, x + 1);
|
|
set->map[x / SET_BITS] |= SET_ONE << (x % SET_BITS);
|
|
}
|
|
|
|
static inline void
|
|
_set_remove (set_t *set, unsigned x)
|
|
{
|
|
if (x >= set->size)
|
|
return;
|
|
set->map[x / SET_BITS] &= ~(SET_ONE << (x % SET_BITS));
|
|
}
|
|
|
|
set_t *
|
|
set_add (set_t *set, unsigned x)
|
|
{
|
|
if (set->inverted)
|
|
_set_remove (set, x);
|
|
else
|
|
_set_add (set, x);
|
|
return set;
|
|
}
|
|
|
|
set_t *
|
|
set_remove (set_t *set, unsigned x)
|
|
{
|
|
if (set->inverted)
|
|
_set_add (set, x);
|
|
else
|
|
_set_remove (set, x);
|
|
return set;
|
|
}
|
|
|
|
set_t *
|
|
set_invert (set_t *set)
|
|
{
|
|
set->inverted = !set->inverted;
|
|
return set;
|
|
}
|
|
|
|
static set_t *
|
|
_set_union (set_t *dst, const set_t *src)
|
|
{
|
|
unsigned size;
|
|
unsigned i;
|
|
|
|
size = max (dst->size, src->size);
|
|
set_expand (dst, size);
|
|
for (i = 0; i < SET_WORDS (src); i++)
|
|
dst->map[i] |= src->map[i];
|
|
return dst;
|
|
}
|
|
|
|
static set_t *
|
|
_set_intersection (set_t *dst, const set_t *src)
|
|
{
|
|
unsigned size;
|
|
unsigned i;
|
|
|
|
size = max (dst->size, src->size);
|
|
set_expand (dst, size);
|
|
for (i = 0; i < SET_WORDS (src); i++)
|
|
dst->map[i] &= src->map[i];
|
|
for ( ; i < SET_WORDS (dst); i++)
|
|
dst->map[i] = 0;
|
|
return dst;
|
|
}
|
|
|
|
static set_t *
|
|
_set_difference (set_t *dst, const set_t *src)
|
|
{
|
|
unsigned size;
|
|
unsigned i;
|
|
|
|
size = max (dst->size, src->size);
|
|
set_expand (dst, size);
|
|
for (i = 0; i < SET_WORDS (src); i++)
|
|
dst->map[i] &= ~src->map[i];
|
|
return dst;
|
|
}
|
|
|
|
static set_t *
|
|
_set_reverse_difference (set_t *dst, const set_t *src)
|
|
{
|
|
unsigned size;
|
|
unsigned i;
|
|
|
|
size = max (dst->size, src->size);
|
|
set_expand (dst, size);
|
|
for (i = 0; i < SET_WORDS (src); i++)
|
|
dst->map[i] = ~dst->map[i] & src->map[i];
|
|
return dst;
|
|
}
|
|
|
|
set_t *
|
|
set_union (set_t *dst, const set_t *src)
|
|
{
|
|
if (dst->inverted && src->inverted) {
|
|
return _set_intersection (dst, src);
|
|
} else if (src->inverted) {
|
|
dst->inverted = 1;
|
|
return _set_difference (dst, src);
|
|
} else if (dst->inverted) {
|
|
return _set_reverse_difference (dst, src);
|
|
} else {
|
|
return _set_union (dst, src);
|
|
}
|
|
}
|
|
|
|
set_t *
|
|
set_intersection (set_t *dst, const set_t *src)
|
|
{
|
|
if (dst->inverted && src->inverted) {
|
|
return _set_union (dst, src);
|
|
} else if (src->inverted) {
|
|
return _set_difference (dst, src);
|
|
} else if (dst->inverted) {
|
|
dst->inverted = 0;
|
|
return _set_reverse_difference (dst, src);
|
|
} else {
|
|
return _set_intersection (dst, src);
|
|
}
|
|
}
|
|
|
|
set_t *
|
|
set_difference (set_t *dst, const set_t *src)
|
|
{
|
|
if (dst->inverted && src->inverted) {
|
|
dst->inverted = 0;
|
|
return _set_reverse_difference (dst, src);
|
|
} else if (src->inverted) {
|
|
return _set_intersection (dst, src);
|
|
} else if (dst->inverted) {
|
|
return _set_union (dst, src);
|
|
} else {
|
|
return _set_difference (dst, src);
|
|
}
|
|
}
|
|
|
|
set_t *
|
|
set_reverse_difference (set_t *dst, const set_t *src)
|
|
{
|
|
if (dst->inverted && src->inverted) {
|
|
dst->inverted = 0;
|
|
return _set_difference (dst, src);
|
|
} else if (src->inverted) {
|
|
dst->inverted = 1;
|
|
return _set_union (dst, src);
|
|
} else if (dst->inverted) {
|
|
dst->inverted = 0;
|
|
return _set_intersection (dst, src);
|
|
} else {
|
|
return _set_reverse_difference (dst, src);
|
|
}
|
|
}
|
|
|
|
set_t *
|
|
set_assign (set_t *dst, const set_t *src)
|
|
{
|
|
unsigned size;
|
|
unsigned i;
|
|
|
|
size = max (dst->size, src->size);
|
|
set_expand (dst, size);
|
|
dst->inverted = src->inverted;
|
|
for (i = 0; i < SET_WORDS (src); i++)
|
|
dst->map[i] = src->map[i];
|
|
for ( ; i < SET_WORDS (dst); i++)
|
|
dst->map[i] = 0;
|
|
return dst;
|
|
}
|
|
|
|
set_t *
|
|
set_empty (set_t *set)
|
|
{
|
|
unsigned i;
|
|
|
|
set->inverted = 0;
|
|
for (i = 0; i < SET_WORDS (set); i++)
|
|
set->map[i] = 0;
|
|
return set;
|
|
}
|
|
|
|
set_t *
|
|
set_everything (set_t *set)
|
|
{
|
|
unsigned i;
|
|
|
|
set->inverted = 1;
|
|
for (i = 0; i < SET_WORDS (set); i++)
|
|
set->map[i] = 0;
|
|
return set;
|
|
}
|
|
|
|
static inline __attribute__((pure)) int
|
|
_set_is_empty (const set_t *set)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < SET_WORDS (set); i++)
|
|
if (set->map[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
set_is_empty (const set_t *set)
|
|
{
|
|
if (set->inverted)
|
|
return 0;
|
|
return _set_is_empty (set);
|
|
}
|
|
|
|
int
|
|
set_is_everything (const set_t *set)
|
|
{
|
|
if (!set->inverted)
|
|
return 0;
|
|
return _set_is_empty (set);
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
set_test_n_n (const set_t *s1, const set_t *s2)
|
|
{
|
|
unsigned i, end;
|
|
set_bits_t intersection = 0;
|
|
set_bits_t difference = 0;
|
|
|
|
end = min (s1->size, s2->size) / SET_BITS;
|
|
for (i = 0; i < end; i++) {
|
|
set_bits_t m1 = s1->map[i];
|
|
set_bits_t m2 = s2->map[i];
|
|
|
|
intersection |= m1 & m2;
|
|
difference |= m1 ^ m2;
|
|
}
|
|
for ( ; i < SET_WORDS (s1); i++) {
|
|
difference |= s1->map[i];
|
|
}
|
|
for ( ; i < SET_WORDS (s2); i++) {
|
|
difference |= s2->map[i];
|
|
}
|
|
return (difference != 0) | ((intersection != 0) << 1);
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
set_test_n_i (const set_t *s1, const set_t *s2)
|
|
{
|
|
unsigned i, end;
|
|
set_bits_t intersection = 0;
|
|
set_bits_t difference = 0;
|
|
|
|
end = min (s1->size, s2->size) / SET_BITS;
|
|
for (i = 0; i < end; i++) {
|
|
set_bits_t m1 = s1->map[i];
|
|
set_bits_t m2 = ~s2->map[i];
|
|
|
|
intersection |= m1 & m2;
|
|
difference |= m1 ^ m2;
|
|
}
|
|
for ( ; i < SET_WORDS (s1); i++) {
|
|
intersection |= s1->map[i];
|
|
difference |= ~s1->map[i];
|
|
}
|
|
for ( ; i < SET_WORDS (s2); i++) {
|
|
difference |= ~s2->map[i];
|
|
}
|
|
return (difference != 0) | ((intersection != 0) << 1);
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
set_test_i_n (const set_t *s1, const set_t *s2)
|
|
{
|
|
unsigned i, end;
|
|
set_bits_t intersection = 0;
|
|
set_bits_t difference = 0;
|
|
|
|
end = min (s1->size, s2->size) / SET_BITS;
|
|
for (i = 0; i < end; i++) {
|
|
set_bits_t m1 = ~s1->map[i];
|
|
set_bits_t m2 = s2->map[i];
|
|
|
|
intersection |= m1 & m2;
|
|
difference |= m1 ^ m2;
|
|
}
|
|
for ( ; i < SET_WORDS (s1); i++) {
|
|
difference |= ~s1->map[i];
|
|
}
|
|
for ( ; i < SET_WORDS (s2); i++) {
|
|
intersection |= s2->map[i];
|
|
difference |= ~s2->map[i];
|
|
}
|
|
return (difference != 0) | ((intersection != 0) << 1);
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
set_test_i_i (const set_t *s1, const set_t *s2)
|
|
{
|
|
unsigned i, end;
|
|
set_bits_t intersection = 0;
|
|
set_bits_t difference = 0;
|
|
|
|
end = min (s1->size, s2->size) / SET_BITS;
|
|
for (i = 0; i < end; i++) {
|
|
set_bits_t m1 = ~s1->map[i];
|
|
set_bits_t m2 = ~s2->map[i];
|
|
|
|
intersection |= m1 & m2;
|
|
difference |= m1 ^ m2;
|
|
}
|
|
for ( ; i < SET_WORDS (s1); i++) {
|
|
difference |= ~s1->map[i];
|
|
}
|
|
for ( ; i < SET_WORDS (s2); i++) {
|
|
intersection |= s2->map[i];
|
|
difference |= ~s2->map[i];
|
|
}
|
|
intersection |= ~0; // two inverted sets can never be disjoint
|
|
return (difference != 0) | ((intersection != 0) << 1);
|
|
}
|
|
|
|
static __attribute__((pure)) int
|
|
set_test (const set_t *s1, const set_t *s2)
|
|
{
|
|
if (s1->inverted && s2->inverted)
|
|
return set_test_i_i (s1, s2);
|
|
else if (s2->inverted)
|
|
return set_test_n_i (s1, s2);
|
|
else if (s1->inverted)
|
|
return set_test_i_n (s1, s2);
|
|
else
|
|
return set_test_n_n (s1, s2);
|
|
}
|
|
|
|
int
|
|
set_is_disjoint (const set_t *s1, const set_t *s2)
|
|
{
|
|
return !(set_test (s1, s2) & 2);
|
|
}
|
|
|
|
int
|
|
set_is_intersecting (const set_t *s1, const set_t *s2)
|
|
{
|
|
return !!(set_test (s1, s2) & 2);
|
|
}
|
|
|
|
int
|
|
set_is_equivalent (const set_t *s1, const set_t *s2)
|
|
{
|
|
return !(set_test (s1, s2) & 1);
|
|
}
|
|
|
|
int
|
|
set_is_subset (const set_t *set, const set_t *sub)
|
|
{
|
|
unsigned i, end;
|
|
|
|
end = min (set->size, sub->size) / SET_BITS;
|
|
if (set->inverted && sub->inverted) {
|
|
for (i = 0; i < end; i++) {
|
|
if (~sub->map[i] & set->map[i])
|
|
return 0;
|
|
}
|
|
for ( ; i < SET_WORDS (set); i++)
|
|
if (set->map[i])
|
|
return 0;
|
|
} else if (set->inverted) {
|
|
for (i = 0; i < end; i++) {
|
|
if (sub->map[i] & set->map[i])
|
|
return 0;
|
|
}
|
|
} else if (sub->inverted) {
|
|
// an inverted set cannot be a subset of a set that is not inverted
|
|
return 0;
|
|
} else {
|
|
for (i = 0; i < end; i++) {
|
|
if (sub->map[i] & ~set->map[i])
|
|
return 0;
|
|
}
|
|
for ( ; i < SET_WORDS (sub); i++)
|
|
if (sub->map[i])
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static inline int
|
|
_set_is_member (const set_t *set, unsigned x)
|
|
{
|
|
if (x >= set->size)
|
|
return 0;
|
|
return (set->map[x / SET_BITS] & (SET_ONE << (x % SET_BITS))) != 0;
|
|
}
|
|
|
|
int
|
|
set_is_member (const set_t *set, unsigned x)
|
|
{
|
|
if (set->inverted)
|
|
return !_set_is_member (set, x);
|
|
return _set_is_member (set, x);
|
|
}
|
|
|
|
unsigned
|
|
set_size (const set_t *set)
|
|
{
|
|
unsigned count = 0;
|
|
unsigned i;
|
|
|
|
for (i = 0; i < set->size; i++)
|
|
if (_set_is_member (set, i))
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
set_iter_t *
|
|
set_first_r (set_pool_t *set_pool, const set_t *set)
|
|
{
|
|
unsigned x;
|
|
set_iter_t *set_iter;
|
|
|
|
for (x = 0; x < set->size; x++) {
|
|
if (_set_is_member (set, x)) {
|
|
set_iter = new_setiter (set_pool);
|
|
set_iter->set = set;
|
|
set_iter->element = x;
|
|
return set_iter;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
set_iter_t *
|
|
set_first (const set_t *set)
|
|
{
|
|
return set_first_r (&static_set_pool, set);
|
|
}
|
|
|
|
set_iter_t *
|
|
set_next_r (set_pool_t *set_pool, set_iter_t *set_iter)
|
|
{
|
|
unsigned x;
|
|
|
|
for (x = set_iter->element + 1; x < set_iter->set->size; x++) {
|
|
if (_set_is_member (set_iter->set, x)) {
|
|
set_iter->element = x;
|
|
return set_iter;
|
|
}
|
|
}
|
|
delete_setiter (set_pool, set_iter);
|
|
return 0;
|
|
}
|
|
|
|
set_iter_t *
|
|
set_next (set_iter_t *set_iter)
|
|
{
|
|
return set_next_r (&static_set_pool, set_iter);
|
|
}
|
|
|
|
const char *
|
|
set_as_string (const set_t *set)
|
|
{
|
|
static dstring_t *str;
|
|
unsigned i;
|
|
|
|
if (!str)
|
|
str = dstring_new ();
|
|
if (set_is_empty (set)) {
|
|
dstring_copystr (str, "{}");
|
|
return str->str;
|
|
}
|
|
if (set_is_everything (set)) {
|
|
dstring_copystr (str, "{...}");
|
|
return str->str;
|
|
}
|
|
dstring_copystr (str, "{");
|
|
for (i = 0; i < set->size; i++) {
|
|
if (set_is_member (set, i)) {
|
|
if (str->str[1])
|
|
dasprintf (str, " %d", i);
|
|
else
|
|
dasprintf (str, "%d", i);
|
|
}
|
|
}
|
|
if (set->inverted)
|
|
dasprintf (str, "%s%d ...", str->str[1] ? " " : "", i);
|
|
dstring_appendstr (str, "}");
|
|
return str->str;
|
|
}
|