mirror of
https://github.com/openbsd/src.git
synced 2025-01-04 15:25:38 -08:00
e386eeb169
Again malloc(0) is not portable and calling memcpy with a NULL pointer and a 0 length is not allowed by the C standard. OK tb@
239 lines
4.7 KiB
C
239 lines
4.7 KiB
C
/* $OpenBSD: rde_sets.c,v 1.13 2024/09/10 09:38:45 claudio Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2018 Claudio Jeker <claudio@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "rde.h"
|
|
|
|
struct set_table {
|
|
void *set;
|
|
size_t nmemb;
|
|
size_t size;
|
|
size_t max;
|
|
};
|
|
|
|
struct as_set *
|
|
as_sets_new(struct as_set_head *as_sets, const char *name, size_t nmemb,
|
|
size_t size)
|
|
{
|
|
struct as_set *aset;
|
|
size_t len;
|
|
|
|
aset = calloc(1, sizeof(*aset));
|
|
if (aset == NULL)
|
|
return NULL;
|
|
|
|
len = strlcpy(aset->name, name, sizeof(aset->name));
|
|
assert(len < sizeof(aset->name));
|
|
|
|
aset->set = set_new(nmemb, size);
|
|
if (aset->set == NULL) {
|
|
free(aset);
|
|
return NULL;
|
|
}
|
|
|
|
SIMPLEQ_INSERT_TAIL(as_sets, aset, entry);
|
|
return aset;
|
|
}
|
|
|
|
struct as_set *
|
|
as_sets_lookup(struct as_set_head *as_sets, const char *name)
|
|
{
|
|
struct as_set *aset;
|
|
|
|
SIMPLEQ_FOREACH(aset, as_sets, entry) {
|
|
if (strcmp(aset->name, name) == 0)
|
|
return aset;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
as_sets_free(struct as_set_head *as_sets)
|
|
{
|
|
struct as_set *aset;
|
|
|
|
if (as_sets == NULL)
|
|
return;
|
|
while (!SIMPLEQ_EMPTY(as_sets)) {
|
|
aset = SIMPLEQ_FIRST(as_sets);
|
|
SIMPLEQ_REMOVE_HEAD(as_sets, entry);
|
|
set_free(aset->set);
|
|
free(aset);
|
|
}
|
|
}
|
|
|
|
void
|
|
as_sets_mark_dirty(struct as_set_head *old, struct as_set_head *new)
|
|
{
|
|
struct as_set *n, *o;
|
|
|
|
SIMPLEQ_FOREACH(n, new, entry) {
|
|
if (old == NULL || (o = as_sets_lookup(old, n->name)) == NULL ||
|
|
!set_equal(n->set, o->set)) {
|
|
n->dirty = 1;
|
|
n->lastchange = getmonotime();
|
|
} else
|
|
n->lastchange = o->lastchange;
|
|
}
|
|
}
|
|
|
|
int
|
|
as_set_match(const struct as_set *aset, uint32_t asnum)
|
|
{
|
|
return set_match(aset->set, asnum) != NULL;
|
|
}
|
|
|
|
struct set_table *
|
|
set_new(size_t nmemb, size_t size)
|
|
{
|
|
struct set_table *set;
|
|
|
|
set = calloc(1, sizeof(*set));
|
|
if (set == NULL)
|
|
return NULL;
|
|
|
|
if (nmemb == 0)
|
|
nmemb = 4;
|
|
|
|
set->size = size;
|
|
set->max = nmemb;
|
|
set->set = calloc(nmemb, set->size);
|
|
if (set->set == NULL) {
|
|
free(set);
|
|
return NULL;
|
|
}
|
|
|
|
rdemem.aset_cnt++;
|
|
rdemem.aset_size += sizeof(*set);
|
|
rdemem.aset_size += set->size * set->max;
|
|
return set;
|
|
}
|
|
|
|
void
|
|
set_free(struct set_table *set)
|
|
{
|
|
if (set == NULL)
|
|
return;
|
|
rdemem.aset_cnt--;
|
|
rdemem.aset_size -= sizeof(*set);
|
|
rdemem.aset_size -= set->size * set->max;
|
|
rdemem.aset_nmemb -= set->nmemb;
|
|
free(set->set);
|
|
free(set);
|
|
}
|
|
|
|
int
|
|
set_add(struct set_table *set, void *elms, size_t nelms)
|
|
{
|
|
if (nelms == 0) /* nothing todo */
|
|
return 0;
|
|
|
|
if (set->max < nelms || set->max - nelms < set->nmemb) {
|
|
uint32_t *s;
|
|
size_t new_size;
|
|
|
|
if (set->nmemb >= SIZE_MAX - 4096 - nelms) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
for (new_size = set->max; new_size < set->nmemb + nelms; )
|
|
new_size += (new_size < 4096 ? new_size : 4096);
|
|
|
|
s = reallocarray(set->set, new_size, set->size);
|
|
if (s == NULL)
|
|
return -1;
|
|
rdemem.aset_size += set->size * (new_size - set->max);
|
|
set->set = s;
|
|
set->max = new_size;
|
|
}
|
|
|
|
memcpy((uint8_t *)set->set + set->nmemb * set->size, elms,
|
|
nelms * set->size);
|
|
set->nmemb += nelms;
|
|
rdemem.aset_nmemb += nelms;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *
|
|
set_get(struct set_table *set, size_t *nelms)
|
|
{
|
|
*nelms = set->nmemb;
|
|
return set->set;
|
|
}
|
|
|
|
static int
|
|
set_cmp(const void *ap, const void *bp)
|
|
{
|
|
const uint32_t *a = ap;
|
|
const uint32_t *b = bp;
|
|
|
|
if (*a > *b)
|
|
return 1;
|
|
else if (*a < *b)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
set_prep(struct set_table *set)
|
|
{
|
|
if (set == NULL)
|
|
return;
|
|
qsort(set->set, set->nmemb, set->size, set_cmp);
|
|
}
|
|
|
|
void *
|
|
set_match(const struct set_table *a, uint32_t asnum)
|
|
{
|
|
if (a == NULL)
|
|
return NULL;
|
|
return bsearch(&asnum, a->set, a->nmemb, a->size, set_cmp);
|
|
}
|
|
|
|
int
|
|
set_equal(const struct set_table *a, const struct set_table *b)
|
|
{
|
|
/* allow NULL pointers to be passed */
|
|
if (a == NULL && b == NULL)
|
|
return 1;
|
|
if (a == NULL || b == NULL)
|
|
return 0;
|
|
|
|
if (a->nmemb != b->nmemb)
|
|
return 0;
|
|
if (memcmp(a->set, b->set, a->nmemb * a->size) != 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
size_t
|
|
set_nmemb(const struct set_table *set)
|
|
{
|
|
return set->nmemb;
|
|
}
|