#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#if defined(__RCSID)
__RCSID("$NetBSD: mem1.c,v 1.77 2023/12/03 18:17:41 rillig Exp $");
#endif
#include <sys/param.h>
#include <stdlib.h>
#include <string.h>
#include "lint1.h"
struct filename {
const char *fn_name;
size_t fn_len;
int fn_id;
struct filename *fn_next;
};
static struct filename *filenames;
static int next_filename_id;
static const struct filename *
search_filename(const char *s, size_t len)
{
const struct filename *fn;
for (fn = filenames; fn != NULL; fn = fn->fn_next) {
if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0)
break;
}
return fn;
}
struct filename_replacement {
const char *orig;
size_t orig_len;
const char *repl;
const struct filename_replacement *next;
};
static struct filename_replacement *filename_replacements;
void
add_directory_replacement(char *arg)
{
struct filename_replacement *r = xmalloc(sizeof(*r));
char *sep = strchr(arg, '=');
if (sep == NULL)
err(1, "Bad replacement directory spec `%s'", arg);
*sep = '\0';
r->orig = arg;
r->orig_len = (size_t)(sep - arg);
r->repl = sep + 1;
r->next = filename_replacements;
filename_replacements = r;
}
const char *
transform_filename(const char *name, size_t len)
{
static char buf[MAXPATHLEN];
const struct filename_replacement *r;
for (r = filename_replacements; r != NULL; r = r->next)
if (r->orig_len < len &&
memcmp(name, r->orig, r->orig_len) == 0)
break;
if (r == NULL)
return name;
(void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len);
return buf;
}
const char *
record_filename(const char *s, size_t slen)
{
const struct filename *existing_fn = search_filename(s, slen);
if (existing_fn != NULL)
return existing_fn->fn_name;
char *name = xmalloc(slen + 1);
(void)memcpy(name, s, slen);
name[slen] = '\0';
struct filename *fn = xmalloc(sizeof(*fn));
fn->fn_name = name;
fn->fn_len = slen;
fn->fn_id = next_filename_id++;
fn->fn_next = filenames;
filenames = fn;
outint(fn->fn_id);
outchar('s');
outstrg(transform_filename(fn->fn_name, fn->fn_len));
outchar('\n');
return fn->fn_name;
}
int
get_filename_id(const char *s)
{
const struct filename *fn;
if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL)
return -1;
return fn->fn_id;
}
typedef struct memory_pools {
struct memory_pool *pools;
size_t cap;
} memory_pools;
static memory_pools mpools;
static memory_pool expr_pool;
static void
mpool_add(memory_pool *pool, struct memory_pool_item item)
{
if (pool->len >= pool->cap) {
pool->cap = 2 * pool->len + 16;
pool->items = xrealloc(pool->items,
sizeof(*pool->items) * pool->cap);
}
pool->items[pool->len++] = item;
}
#ifdef DEBUG_MEM
static void
debug_memory_pool_item(const struct memory_pool_item *item)
{
void *p = item->p;
size_t size = item->size;
const char *descr = item->descr;
if (strcmp(descr, "string") == 0) {
const char *str = p;
debug_step("%s: freeing string '%s'", __func__, str);
} else if (strcmp(descr, "sym") == 0) {
const sym_t *sym = p;
debug_step("%s: freeing symbol '%s'", __func__, sym->s_name);
} else if (strcmp(descr, "type") == 0) {
const type_t *tp = p;
debug_step("%s: freeing type '%s'", __func__, type_name(tp));
} else if (strcmp(descr, "tnode") == 0) {
const tnode_t *tn = p;
debug_step("%s: freeing node '%s' with type '%s'",
__func__, op_name(tn->tn_op), type_name(tn->tn_type));
} else
debug_step("%s: freeing '%s' with %zu bytes",
__func__, descr, size);
}
#endif
static void
mpool_free(memory_pool *pool)
{
#ifdef DEBUG_MEM
for (size_t i = pool->len; i-- > 0; )
debug_memory_pool_item(pool->items + i);
#endif
for (size_t i = pool->len; i-- > 0;) {
#ifdef DEBUG_MEM
static void *(*volatile set)(void *, int, size_t) = memset;
set(pool->items[i].p, 'Z', pool->items[i].size);
#endif
free(pool->items[i].p);
}
pool->len = 0;
}
static void *
#ifdef DEBUG_MEM
mpool_zero_alloc(memory_pool *pool, size_t size, const char *descr)
#else
mpool_zero_alloc(memory_pool *pool, size_t size)
#endif
{
void *mem = xmalloc(size);
memset(mem, 0, size);
#if DEBUG_MEM
mpool_add(pool, (struct memory_pool_item){ mem, size, descr });
#else
mpool_add(pool, (struct memory_pool_item){ mem });
#endif
return mem;
}
static memory_pool *
mpool_at(size_t level)
{
if (level >= mpools.cap) {
size_t prev_cap = mpools.cap;
mpools.cap = level + 16;
mpools.pools = xrealloc(mpools.pools,
sizeof(*mpools.pools) * mpools.cap);
for (size_t i = prev_cap; i < mpools.cap; i++)
mpools.pools[i] = (memory_pool){ NULL, 0, 0 };
}
return mpools.pools + level;
}
#ifdef DEBUG_MEM
void *
level_zero_alloc(size_t level, size_t size, const char *descr)
{
debug_step("%s: %s at level %zu", __func__, descr, level);
return mpool_zero_alloc(mpool_at(level), size, descr);
}
#else
void *
(level_zero_alloc)(size_t level, size_t size)
{
return mpool_zero_alloc(mpool_at(level), size);
}
#endif
#ifdef DEBUG_MEM
void *
block_zero_alloc(size_t size, const char *descr)
{
return level_zero_alloc(mem_block_level, size, descr);
}
#else
void *
(block_zero_alloc)(size_t size)
{
return (level_zero_alloc)(mem_block_level, size);
}
#endif
void
level_free_all(size_t level)
{
debug_step("+ %s %zu", __func__, level);
debug_indent_inc();
mpool_free(mpool_at(level));
debug_leave();
}
#if DEBUG_MEM
void *
expr_zero_alloc(size_t s, const char *descr)
{
return mpool_zero_alloc(&expr_pool, s, descr);
}
#else
void *
(expr_zero_alloc)(size_t size)
{
return mpool_zero_alloc(&expr_pool, size);
}
#endif
static bool
str_ends_with(const char *haystack, const char *needle)
{
size_t hlen = strlen(haystack);
size_t nlen = strlen(needle);
return nlen <= hlen &&
memcmp(haystack + hlen - nlen, needle, nlen) == 0;
}
tnode_t *
expr_alloc_tnode(void)
{
tnode_t *tn = expr_zero_alloc(sizeof(*tn), "tnode");
tn->tn_sys = in_system_header ||
(curr_pos.p_file != csrc_pos.p_file &&
str_ends_with(curr_pos.p_file, ".c"));
return tn;
}
void
expr_free_all(void)
{
debug_step("%s", __func__);
mpool_free(&expr_pool);
}
memory_pool
expr_save_memory(void)
{
memory_pool saved_pool = expr_pool;
expr_pool = (memory_pool){ NULL, 0, 0 };
return saved_pool;
}
void
expr_restore_memory(memory_pool saved_pool)
{
expr_free_all();
free(expr_pool.items);
expr_pool = saved_pool;
}