2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 10:10:06 +00:00
bind/lib/isc/symtab.c
Ondřej Surý bc1d4c9cb4 Clear the pointer to destroyed object early using the semantic patch
Also disable the semantic patch as the code needs tweaks here and there because
some destroy functions might not destroy the object and return early if the
object is still in use.
2020-02-09 18:00:17 -08:00

290 lines
6.6 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <ctype.h>
#include <stdbool.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/string.h>
#include <isc/symtab.h>
#include <isc/util.h>
typedef struct elt {
char * key;
unsigned int type;
isc_symvalue_t value;
LINK(struct elt) link;
} elt_t;
typedef LIST(elt_t) eltlist_t;
#define SYMTAB_MAGIC ISC_MAGIC('S', 'y', 'm', 'T')
#define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC)
struct isc_symtab {
/* Unlocked. */
unsigned int magic;
isc_mem_t * mctx;
unsigned int size;
unsigned int count;
unsigned int maxload;
eltlist_t * table;
isc_symtabaction_t undefine_action;
void * undefine_arg;
bool case_sensitive;
};
isc_result_t
isc_symtab_create(isc_mem_t *mctx, unsigned int size,
isc_symtabaction_t undefine_action,
void *undefine_arg,
bool case_sensitive,
isc_symtab_t **symtabp)
{
isc_symtab_t *symtab;
unsigned int i;
REQUIRE(mctx != NULL);
REQUIRE(symtabp != NULL && *symtabp == NULL);
REQUIRE(size > 0); /* Should be prime. */
symtab = isc_mem_get(mctx, sizeof(*symtab));
symtab->mctx = NULL;
isc_mem_attach(mctx, &symtab->mctx);
symtab->table = isc_mem_get(mctx, size * sizeof(eltlist_t));
for (i = 0; i < size; i++)
INIT_LIST(symtab->table[i]);
symtab->size = size;
symtab->count = 0;
symtab->maxload = size * 3 / 4;
symtab->undefine_action = undefine_action;
symtab->undefine_arg = undefine_arg;
symtab->case_sensitive = case_sensitive;
symtab->magic = SYMTAB_MAGIC;
*symtabp = symtab;
return (ISC_R_SUCCESS);
}
void
isc_symtab_destroy(isc_symtab_t **symtabp) {
isc_symtab_t *symtab;
unsigned int i;
elt_t *elt, *nelt;
REQUIRE(symtabp != NULL);
symtab = *symtabp;
*symtabp = NULL;
REQUIRE(VALID_SYMTAB(symtab));
for (i = 0; i < symtab->size; i++) {
for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
nelt = NEXT(elt, link);
if (symtab->undefine_action != NULL)
(symtab->undefine_action)(elt->key,
elt->type,
elt->value,
symtab->undefine_arg);
isc_mem_put(symtab->mctx, elt, sizeof(*elt));
}
}
isc_mem_put(symtab->mctx, symtab->table,
symtab->size * sizeof(eltlist_t));
symtab->magic = 0;
isc_mem_putanddetach(&symtab->mctx, symtab, sizeof(*symtab));
}
static inline unsigned int
hash(const char *key, bool case_sensitive) {
const char *s;
unsigned int h = 0;
int c;
/*
* This hash function is similar to the one Ousterhout
* uses in Tcl.
*/
if (case_sensitive) {
for (s = key; *s != '\0'; s++) {
h += (h << 3) + *s;
}
} else {
for (s = key; *s != '\0'; s++) {
c = *s;
c = tolower((unsigned char)c);
h += (h << 3) + c;
}
}
return (h);
}
#define FIND(s, k, t, b, e) \
b = hash((k), (s)->case_sensitive) % (s)->size; \
if ((s)->case_sensitive) { \
for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
if (((t) == 0 || e->type == (t)) && \
strcmp(e->key, (k)) == 0) \
break; \
} \
} else { \
for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \
if (((t) == 0 || e->type == (t)) && \
strcasecmp(e->key, (k)) == 0) \
break; \
} \
}
isc_result_t
isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type,
isc_symvalue_t *value)
{
unsigned int bucket;
elt_t *elt;
REQUIRE(VALID_SYMTAB(symtab));
REQUIRE(key != NULL);
FIND(symtab, key, type, bucket, elt);
if (elt == NULL)
return (ISC_R_NOTFOUND);
if (value != NULL)
*value = elt->value;
return (ISC_R_SUCCESS);
}
static void
grow_table(isc_symtab_t *symtab) {
eltlist_t *newtable;
unsigned int i, newsize, newmax;
REQUIRE(symtab != NULL);
newsize = symtab->size * 2;
newmax = newsize * 3 / 4;
INSIST(newsize > 0U && newmax > 0U);
newtable = isc_mem_get(symtab->mctx, newsize * sizeof(eltlist_t));
for (i = 0; i < newsize; i++)
INIT_LIST(newtable[i]);
for (i = 0; i < symtab->size; i++) {
elt_t *elt, *nelt;
for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) {
unsigned int hv;
nelt = NEXT(elt, link);
UNLINK(symtab->table[i], elt, link);
hv = hash(elt->key, symtab->case_sensitive);
APPEND(newtable[hv % newsize], elt, link);
}
}
isc_mem_put(symtab->mctx, symtab->table,
symtab->size * sizeof(eltlist_t));
symtab->table = newtable;
symtab->size = newsize;
symtab->maxload = newmax;
}
isc_result_t
isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type,
isc_symvalue_t value, isc_symexists_t exists_policy)
{
unsigned int bucket;
elt_t *elt;
REQUIRE(VALID_SYMTAB(symtab));
REQUIRE(key != NULL);
REQUIRE(type != 0);
FIND(symtab, key, type, bucket, elt);
if (exists_policy != isc_symexists_add && elt != NULL) {
if (exists_policy == isc_symexists_reject)
return (ISC_R_EXISTS);
INSIST(exists_policy == isc_symexists_replace);
UNLINK(symtab->table[bucket], elt, link);
if (symtab->undefine_action != NULL)
(symtab->undefine_action)(elt->key, elt->type,
elt->value,
symtab->undefine_arg);
} else {
elt = isc_mem_get(symtab->mctx, sizeof(*elt));
ISC_LINK_INIT(elt, link);
symtab->count++;
}
/*
* Though the "key" can be const coming in, it is not stored as const
* so that the calling program can easily have writable access to
* it in its undefine_action function. In the event that it *was*
* truly const coming in and then the caller modified it anyway ...
* well, don't do that!
*/
DE_CONST(key, elt->key);
elt->type = type;
elt->value = value;
/*
* We prepend so that the most recent definition will be found.
*/
PREPEND(symtab->table[bucket], elt, link);
if (symtab->count > symtab->maxload)
grow_table(symtab);
return (ISC_R_SUCCESS);
}
isc_result_t
isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type) {
unsigned int bucket;
elt_t *elt;
REQUIRE(VALID_SYMTAB(symtab));
REQUIRE(key != NULL);
FIND(symtab, key, type, bucket, elt);
if (elt == NULL)
return (ISC_R_NOTFOUND);
if (symtab->undefine_action != NULL)
(symtab->undefine_action)(elt->key, elt->type,
elt->value, symtab->undefine_arg);
UNLINK(symtab->table[bucket], elt, link);
isc_mem_put(symtab->mctx, elt, sizeof(*elt));
symtab->count--;
return (ISC_R_SUCCESS);
}
unsigned int
isc_symtab_count(isc_symtab_t *symtab) {
REQUIRE(VALID_SYMTAB(symtab));
return (symtab->count);
}