reduce big arrays on the stack, report allies with new pump_paragraph function (WIP)

This commit is contained in:
Enno Rehling 2018-11-26 22:01:18 +01:00
parent 8c02d14f13
commit 304bebf291
19 changed files with 235 additions and 79 deletions

View File

@ -54,6 +54,7 @@ static const terrain_type *chaosterrain(void)
}
if (numtypes > 0) {
types = malloc(sizeof(terrain_type *) * numtypes);
if (!types) abort();
numtypes = 0;
for (terrain = terrains(); terrain != NULL; terrain = terrain->next) {
if ((terrain->flags & LAND_REGION) && terrain->herbs) {

View File

@ -17,9 +17,12 @@ without prior permission by the authors of Eressea.
/* tweakable features */
#define RENDER_CRMESSAGES
#define BUFFERSIZE 32768
#define RESOURCECOMPAT
#define BUFFERSIZE 32768
/* riesig, wegen spionage-messages :-( */
static char g_bigbuf[BUFFERSIZE];
#include <spells/regioncurse.h>
/* modules include */
@ -132,9 +135,12 @@ static const char *translate(const char *key, const char *value)
t = junkyard;
junkyard = junkyard->next;
}
else
else {
t = malloc(sizeof(translation));
if (!t) abort();
}
t->key = str_strdup(key);
if (!t->key) abort();
t->value = value;
t->next = translation_table[kk];
translation_table[kk] = t;
@ -264,13 +270,12 @@ cr_output_curses(struct stream *out, const faction * viewer, const void *obj, ob
msg = msg_curse(c, obj, typ, self);
if (msg) {
char buf[BUFFERSIZE];
if (!header) {
header = 1;
stream_printf(out, "EFFECTS\n");
}
nr_render(msg, viewer->locale, buf, sizeof(buf), viewer);
stream_printf(out, "\"%s\"\n", buf);
nr_render(msg, viewer->locale, g_bigbuf, sizeof(g_bigbuf), viewer);
stream_printf(out, "\"%s\"\n", g_bigbuf);
msg_release(msg);
}
a = a->next;
@ -571,28 +576,26 @@ static void render_messages(FILE * F, faction * f, message_list * msgs)
{
struct mlist *m = msgs->begin;
while (m) {
char crbuffer[BUFFERSIZE]; /* gross, wegen spionage-messages :-( */
bool printed = false;
const struct message_type *mtype = m->msg->type;
unsigned int hash = mtype->key;
#ifdef RENDER_CRMESSAGES
char nrbuffer[1024 * 32];
nrbuffer[0] = '\0';
if (nr_render(m->msg, f->locale, nrbuffer, sizeof(nrbuffer), f) > 0) {
g_bigbuf[0] = '\0';
if (nr_render(m->msg, f->locale, g_bigbuf, sizeof(g_bigbuf), f) > 0) {
fprintf(F, "MESSAGE %d\n", message_id(m->msg));
fprintf(F, "%u;type\n", hash);
fwritestr(F, nrbuffer);
fwritestr(F, g_bigbuf);
fputs(";rendered\n", F);
printed = true;
}
#endif
crbuffer[0] = '\0';
if (cr_render(m->msg, crbuffer, (const void *)f) == 0) {
if (crbuffer[0]) {
g_bigbuf[0] = '\0';
if (cr_render(m->msg, g_bigbuf, (const void *)f) == 0) {
if (g_bigbuf[0]) {
if (!printed) {
fprintf(F, "MESSAGE %d\n", message_id(m->msg));
}
fputs(crbuffer, F);
fputs(g_bigbuf, F);
}
}
else {
@ -605,6 +608,7 @@ static void render_messages(FILE * F, faction * f, message_list * msgs)
kmt = kmt->nexthash;
if (kmt == NULL) {
kmt = (struct known_mtype *)malloc(sizeof(struct known_mtype));
if (!kmt) abort();
kmt->nexthash = mtypehash[ihash];
kmt->mtype = mtype;
mtypehash[ihash] = kmt;
@ -1218,7 +1222,7 @@ cr_borders(const region * r, const faction * f, seen_mode mode, FILE * F)
void cr_output_resources(stream *out, const faction * f, const region *r, bool see_unit)
{
char cbuf[BUFFERSIZE], *pos = cbuf;
char *pos = g_bigbuf;
resource_report result[MAX_RAWMATERIALS];
int n, size = report_resources(r, result, MAX_RAWMATERIALS, f, see_unit);
@ -1251,8 +1255,8 @@ void cr_output_resources(stream *out, const faction * f, const region *r, bool s
result[n].level);
}
}
if (pos != cbuf) {
swrite(cbuf, 1, pos - cbuf, out);
if (pos != g_bigbuf) {
swrite(g_bigbuf, 1, pos - g_bigbuf, out);
}
}

View File

@ -31,8 +31,8 @@ extern "C" {
struct param;
struct _dictionary_;
#define DISPLAYSIZE 8192 /* max. L<>nge einer Beschreibung, incl trailing 0 */
#define ORDERSIZE (DISPLAYSIZE*2) /* max. length of an order */
#define DISPLAYSIZE 4096 /* max. L<>nge einer Beschreibung, incl trailing 0 */
#define ORDERSIZE 4096 /* max. length of an order */
#define NAMESIZE 128 /* max. L<>nge eines Namens, incl trailing 0 */
#define IDSIZE 16 /* max. L<>nge einer no (als String), incl trailing 0 */
#define OBJECTIDSIZE (NAMESIZE+5+IDSIZE) /* max. L<>nge der Strings, die

View File

@ -1521,29 +1521,78 @@ static int count_allies_cb(struct allies *all, faction *af, int status, void *ud
struct show_s {
sbstring sbs;
stream *out;
const faction *f;
int num_allies;
int num_listed;
size_t maxlen;
};
void pump_paragraph(sbstring *sbp, stream *out, size_t maxlen, bool isfinal)
{
while (sbs_length(sbp) > maxlen) {
char *pos, *begin = sbp->begin;
while (*begin && isspace(*begin)) {
/* eat whitespace */
++begin;
}
pos = begin;
while (pos) {
char *next = strchr(pos+1, ' ');
if (next == NULL) {
if (isfinal) {
swrite(begin, 1, sbp->end - begin, out);
newline(out);
}
return;
}
else if (next > begin + maxlen) {
size_t len = pos - begin;
swrite(begin, 1, len, out);
newline(out);
while (*pos && isspace(*pos)) {
++pos;
++len;
}
assert(len <= INT_MAX);
sbs_cut(sbp, (int)len);
break;
}
pos = next;
}
}
if (isfinal) {
char *pos = sbp->begin;
while (*pos && isspace(*pos)) {
/* eat whitespace */
++pos;
}
swrite(pos, 1, sbp->end - pos, out);
newline(out);
}
}
static int show_allies_cb(struct allies *all, faction *af, int status, void *udata) {
struct show_s * show = (struct show_s *)udata;
const faction * f = show->f;
sbstring *sbp = &show->sbs;
int mode = alliance_status(f, af, status);
--show->num_allies;
if (sbs_length(&show->sbs) > 0) {
/* not the first entry */
if (0 == show->num_allies) {
sbs_strcat(&show->sbs, LOC(f->locale, "list_and"));
if (show->num_listed++ != 0) {
if (show->num_listed == show->num_allies) {
/* last entry */
sbs_strcat(sbp, LOC(f->locale, "list_and"));
}
else {
sbs_strcat(&show->sbs, ", ");
/* neither first entry nor last*/
sbs_strcat(sbp, ", ");
}
}
sbs_strcat(&show->sbs, factionname(af));
sbs_strcat(&show->sbs, " (");
sbs_strcat(sbp, factionname(af));
sbs_strcat(sbp, " (");
if ((mode & HELP_ALL) == HELP_ALL) {
sbs_strcat(&show->sbs, LOC(f->locale, parameters[P_ANY]));
sbs_strcat(sbp, LOC(f->locale, parameters[P_ANY]));
}
else {
int h, hh = 0;
@ -1573,58 +1622,60 @@ static int show_allies_cb(struct allies *all, faction *af, int status, void *uda
}
if (p != MAXPARAMS) {
if (hh) {
sbs_strcat(&show->sbs, ", ");
sbs_strcat(sbp, ", ");
}
sbs_strcat(&show->sbs, LOC(f->locale, parameters[p]));
sbs_strcat(sbp, LOC(f->locale, parameters[p]));
hh = 1;
}
}
}
sbs_strcat(&show->sbs, ")");
if (show->num_allies == show->num_listed) {
sbs_strcat(sbp, ").");
pump_paragraph(sbp, show->out, show->maxlen, true);
}
else {
sbs_strcat(sbp, ")");
pump_paragraph(sbp, show->out, show->maxlen, false);
}
return 0;
}
static void
show_allies(const faction * f, struct allies * allies, char *buf, size_t size)
void report_allies(struct stream *out, size_t maxlen, const struct faction * f, struct allies * allies, const char *prefix)
{
int num_allies = 0;
assert(maxlen <= REPORTWIDTH);
allies_walk(allies, count_allies_cb, &num_allies);
if (num_allies > 0) {
struct show_s show;
char buf[REPORTWIDTH * 2];
show.f = f;
show.out = out;
show.num_allies = num_allies;
sbs_init(&show.sbs, buf, size);
show.num_listed = 0;
show.maxlen = maxlen;
sbs_init(&show.sbs, buf, sizeof(buf));
sbs_strcpy(&show.sbs, prefix);
allies_walk(allies, show_allies_cb, &show);
sbs_strcat(&show.sbs, ".");
}
}
static void allies(struct stream *out, const faction * f)
{
const group *g = f->groups;
char buf[16384];
char prefix[64];
if (f->allies) {
int bytes;
size_t size = sizeof(buf);
bytes = snprintf(buf, size, "%s ", LOC(f->locale, "faction_help"));
size -= bytes;
show_allies(f, f->allies, buf + bytes, size);
paragraph(out, buf, 0, 0, 0);
newline(out);
snprintf(prefix, sizeof(prefix), "%s ", LOC(f->locale, "faction_help"));
report_allies(out, REPORTWIDTH, f, f->allies, prefix);
}
while (g) {
if (g->allies) {
int bytes;
size_t size = sizeof(buf);
bytes = snprintf(buf, size, "%s %s ", g->name, LOC(f->locale, "group_help"));
size -= bytes;
show_allies(f, g->allies, buf + bytes, size);
paragraph(out, buf, 0, 0, 0);
newline(out);
snprintf(prefix, sizeof(prefix), "%s %s ", g->name, LOC(f->locale, "group_help"));
report_allies(out, REPORTWIDTH, f, g->allies, prefix);
}
g = g->next;
}

View File

@ -14,21 +14,27 @@
#define H_GC_REPORT
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct stream;
struct sbstring;
struct spellbook_entry;
struct region;
struct faction;
struct locale;
struct allies;
void register_nr(void);
void report_cleanup(void);
void write_spaces(struct stream *out, size_t num);
void report_travelthru(struct stream *out, struct region * r, const struct faction * f);
void report_region(struct stream *out, const struct region * r, struct faction * f);
void report_allies(struct stream *out, size_t maxlen, const struct faction * f, struct allies * allies, const char *prefix);
void pump_paragraph(struct sbstring *sbp, struct stream *out, size_t maxlen, bool isfinal);
void nr_spell_syntax(char *buf, size_t size, struct spellbook_entry * sbe, const struct locale *lang);
void nr_spell(struct stream *out, struct spellbook_entry * sbe, const struct locale *lang);

View File

@ -3,6 +3,7 @@
#include "move.h"
#include "travelthru.h"
#include <kernel/ally.h>
#include <kernel/building.h>
#include <kernel/faction.h>
#include <kernel/item.h>
@ -143,6 +144,58 @@ static void test_report_region(CuTest *tc) {
test_teardown();
}
static void test_report_allies(CuTest *tc) {
stream out = { 0 };
char buf[1024];
char exp[1024];
size_t len, linebreak = 72;
struct locale *lang;
faction *f, *f1, *f2, *f3;
test_setup();
lang = test_create_locale();
locale_setstring(lang, "list_and", " und ");
mstream_init(&out);
f = test_create_faction(NULL);
f->locale = lang;
f1 = test_create_faction(NULL);
f2 = test_create_faction(NULL);
f3 = test_create_faction(NULL);
snprintf(exp, sizeof(exp), "Wir helfen %s (%s).\n",
factionname(f1),
LOC(lang, parameters[P_GUARD]));
ally_set(&f->allies, f1, HELP_GUARD);
report_allies(&out, linebreak, f, f->allies, "Wir helfen ");
out.api->rewind(out.handle);
len = out.api->read(out.handle, buf, sizeof(buf));
buf[len] = 0;
CuAssertStrEquals(tc, exp, buf);
out.api->rewind(out.handle);
ally_set(&f->allies, f2, HELP_GIVE);
ally_set(&f->allies, f3, HELP_ALL);
snprintf(exp, sizeof(exp), "Wir helfen %s (%s), %s (%s)",
factionname(f1),
LOC(lang, parameters[P_GUARD]),
factionname(f2),
LOC(lang, parameters[P_GIVE]));
linebreak = strlen(exp);
snprintf(exp, sizeof(exp), "Wir helfen %s (%s), %s (%s)\nund %s (%s).\n",
factionname(f1),
LOC(lang, parameters[P_GUARD]),
factionname(f2),
LOC(lang, parameters[P_GIVE]),
factionname(f3),
LOC(lang, parameters[P_ANY]));
report_allies(&out, linebreak, f, f->allies, "Wir helfen ");
out.api->rewind(out.handle);
len = out.api->read(out.handle, buf, sizeof(buf));
buf[len] = 0;
CuAssertStrEquals(tc, exp, buf);
test_teardown();
}
static void test_report_travelthru(CuTest *tc) {
stream out = { 0 };
char buf[1024];
@ -302,6 +355,7 @@ CuSuite *get_report_suite(void)
SUITE_ADD_TEST(suite, test_write_many_spaces);
SUITE_ADD_TEST(suite, test_report_travelthru);
SUITE_ADD_TEST(suite, test_report_region);
SUITE_ADD_TEST(suite, test_report_allies);
SUITE_ADD_TEST(suite, test_write_spell_syntax);
return suite;
}

View File

@ -63,6 +63,7 @@ void tsf_register(const char *name, tostring_f fun)
}
if (tsf == NULL) {
tsf = malloc(sizeof(tsf_list));
if (!tsf) abort();
tsf->fun = fun;
tsf->name = name;
tsf->next = tostringfs;
@ -102,12 +103,14 @@ void crt_register(const struct message_type *mtype)
}
if (!crt) {
crt = malloc(sizeof(crmessage_type));
if (!crt) abort();
crt->mtype = mtype;
crt->next = crtypes[hash];
crtypes[hash] = crt;
if (mtype->nparameters > 0) {
int i;
crt->renderers = malloc(sizeof(tostring_f) * mtype->nparameters);
if (!crt->renderers) abort();
/* can be scrapped for memory vs. speed */
for (i = 0; i != mtype->nparameters; ++i) {
crt->renderers[i] = tsf_find(mtype->types[i]->name);

View File

@ -32,8 +32,11 @@ int *intlist_init(void)
int *intlist_add(int *i_p, int i)
{
void *tmp;
i_p[0]++;
i_p = realloc(i_p, (i_p[0] + 1) * sizeof(int));
tmp = realloc(i_p, (i_p[0] + 1) * sizeof(int));
if (!tmp) abort();
i_p = (int *)tmp;
i_p[i_p[0]] = i;
return (i_p);

View File

@ -83,7 +83,7 @@ locale *get_or_create_locale(const char *name)
return *lp;
}
}
*lp = l = (locale *)calloc(sizeof(locale), 1);
*lp = l = (locale *)calloc(1, sizeof(locale));
assert_alloc(l);
l->hashkey = hkey;
l->name = str_strdup(name);
@ -206,6 +206,7 @@ void locale_setstring(locale * lang, const char *key, const char *value)
}
if (!find) {
find = calloc(1, sizeof(struct locale_str));
if (!find) abort();
find->nexthash = lang->strings[id];
lang->strings[id] = find;
find->hashkey = hkey;

View File

@ -120,6 +120,7 @@ unsigned int listlen(void *l)
void addstrlist(strlist ** SP, const char *s)
{
strlist *slist = malloc(sizeof(strlist));
if (!slist) abort();
slist->next = NULL;
slist->s = str_strdup(s);
addlist(SP, slist);

View File

@ -52,6 +52,7 @@ static log_t *loggers;
log_t *log_create(int flags, void *data, log_fun call) {
log_t *lgr = malloc(sizeof(log_t));
if (!lgr) abort();
lgr->log = call;
lgr->flags = flags;
lgr->data = data;
@ -147,7 +148,7 @@ static const char *log_prefix(int level) {
static int check_dupe(const char *format, int level)
{
static int last_type; /* STATIC_XCALL: used across calls */
static char last_message[32]; /* STATIC_XCALL: used across calls */
static char last_message[32] = { 0 }; /* STATIC_XCALL: used across calls */
static int dupes = 0; /* STATIC_XCALL: used across calls */
if (strncmp(last_message, format, sizeof(last_message)) == 0) {
/* TODO: C6054: String 'last_message' might not be zero - terminated. */

View File

@ -41,6 +41,7 @@ register_argtype(const char *name, void(*free_arg) (variant),
variant(*copy_arg) (variant), variant_type type)
{
arg_type *atype = (arg_type *)malloc(sizeof(arg_type));
if (!atype) abort();
atype->name = name;
atype->next = argtypes;
atype->release = free_arg;
@ -90,7 +91,9 @@ message_type *mt_create(message_type * mtype, const char *args[], int nparameter
int i;
mtype->nparameters = nparameters;
mtype->pnames = (char **)malloc(sizeof(char *) * nparameters);
if (!mtype->pnames) abort();
mtype->types = (arg_type **)malloc(sizeof(arg_type *) * nparameters);
if (!mtype->types) abort();
for (i = 0; args[i]; ++i) {
const char *x = args[i];
const char *spos = strchr(x, ':');
@ -103,8 +106,8 @@ message_type *mt_create(message_type * mtype, const char *args[], int nparameter
assert(atype);
}
else {
char *cp;
cp = malloc(spos - x + 1);
char *cp = malloc(spos - x + 1);
if (!cp) abort();
memcpy(cp, x, spos - x);
cp[spos - x] = '\0';
mtype->pnames[i] = cp;
@ -162,8 +165,10 @@ message_type *mt_new(const char *name, const char *section)
return NULL;
}
mtype = (message_type *)malloc(sizeof(message_type));
if (!mtype) abort();
mtype->key = 0;
mtype->name = str_strdup(name);
if (!mtype->name) abort();
mtype->section = section_find(section);
if (!mtype->section) {
mtype->section = section_add(section);
@ -219,7 +224,6 @@ static void free_arg(const arg_type * atype, variant data)
message *msg_create(const struct message_type *mtype, variant args[])
{
int i;
message *msg;
assert(mtype != NULL);
@ -228,11 +232,17 @@ message *msg_create(const struct message_type *mtype, variant args[])
return NULL;
}
msg = (message *)malloc(sizeof(message));
if (!msg) abort();
msg->type = mtype;
msg->parameters = (variant *)(mtype->nparameters ? calloc(mtype->nparameters, sizeof(variant)) : NULL);
msg->refcount = 1;
for (i = 0; i != mtype->nparameters; ++i) {
msg->parameters[i] = copy_arg(mtype->types[i], args[i]);
msg->parameters = NULL;
if (mtype->nparameters > 0) {
int i;
msg->parameters = (variant *)(mtype->nparameters ? calloc(mtype->nparameters, sizeof(variant)) : NULL);
if (!msg->parameters) abort();
for (i = 0; i != mtype->nparameters; ++i) {
msg->parameters[i] = copy_arg(mtype->types[i], args[i]);
}
}
if (msg_log_create)
msg_log_create(msg);

View File

@ -33,7 +33,19 @@ typedef struct nrmessage_type {
} nrmessage_type;
#define NRT_MAXHASH 1021
static nrmessage_type *nrtypes[NRT_MAXHASH];
static nrmessage_type *nrtypes[NRT_MAXHASH] = { 0 };
void free_nrmesssages(void) {
int i;
for (i = 0; i != NRT_MAXHASH; ++i) {
while (nrtypes[i]) {
nrmessage_type *nr = nrtypes[i];
nrtypes[i] = nr->next;
free(nr->vars);
free(nr);
}
}
}
const char *nrt_string(const struct message_type *mtype,
const struct locale *lang)
@ -81,7 +93,8 @@ nrt_register(const struct message_type *mtype)
int i;
char zNames[256];
char *c = zNames;
nrt = malloc(sizeof(nrmessage_type));
nrt = calloc(1, sizeof(nrmessage_type));
if (!nrt) abort();
nrt->mtype = mtype;
nrt->next = nrtypes[hash];
nrtypes[hash] = nrt;
@ -92,6 +105,7 @@ nrt_register(const struct message_type *mtype)
c += str_strlcpy(c, mtype->pnames[i], sizeof(zNames)-(c-zNames));
}
nrt->vars = str_strdup(zNames);
if (!nrt->vars) abort();
}
}
@ -115,16 +129,3 @@ size_t size, const void *userdata)
buffer[0] = 0;
return 0;
}
void free_nrmesssages(void) {
int i;
for (i = 0; i != NRT_MAXHASH; ++i) {
while (nrtypes[i]) {
nrmessage_type *nr = nrtypes[i];
nrtypes[i] = nr->next;
free(nr->vars);
free(nr);
}
}
}

View File

@ -58,6 +58,7 @@ void init_tokens_ex(const char *initstr, void *data, void (*dtor)(void *))
{
if (states == NULL) {
states = calloc(1, sizeof(parser_state));
if (!states) abort();
}
else if (states->dtor) {
states->dtor(states->data);
@ -74,6 +75,7 @@ void init_tokens_str(const char *initstr) {
void parser_pushstate(void)
{
parser_state *new_state = calloc(1, sizeof(parser_state));
if (!new_state) abort();
new_state->current_token = NULL;
new_state->next = states;
states = new_state;

View File

@ -74,7 +74,8 @@ int pofile_read(const char *filename, int (*callback)(const char *msgid, const c
line = read_line(F);
while (line) {
char token[8];
int err = sscanf(line, "%8s", token);
int err = sscanf(line, "%7s", token);
token[7] = 0;
if (err == 1) {
char *text = NULL;
size_t size = 0, len = strlen(token);

View File

@ -132,6 +132,7 @@ void random_source_inject_array(double inject[], int size) {
if (values)
free(values);
values = malloc(sizeof(double) * size);
if (!values) abort();
for (i=0; i < size; ++i) {
values[i] = inject[i];
}

View File

@ -300,6 +300,19 @@ void sbs_strcpy(struct sbstring *sbs, const char *str)
sbs->end = sbs->begin + len;
}
void sbs_cut(sbstring *sbp, int bytes)
{
if (bytes > 0) {
size_t len = sbs_length(sbp) - bytes;
memmove(sbp->begin, sbp->begin + bytes, len + 1);
sbp->end = sbp->begin + len;
}
else if (bytes < 0) {
size_t len = sbs_length(sbp) + bytes;
sbp->end = sbp->begin + len;
}
}
size_t sbs_length(const struct sbstring *sbs)
{
return sbs->end - sbs->begin;

View File

@ -53,6 +53,7 @@ extern "C" {
void sbs_strcat(struct sbstring *sbs, const char *str);
void sbs_strncat(struct sbstring *sbs, const char *str, size_t size);
void sbs_strcpy(struct sbstring *sbs, const char *str);
void sbs_cut(struct sbstring *sbp, int bytes);
size_t sbs_length(const struct sbstring *sbs);
/* benchmark for units:

View File

@ -118,6 +118,7 @@ char * transliterate(char * out, size_t size, const char * in)
tnode * mknode(void) {
tnode * node = (tnode *)calloc(1, sizeof(tnode));
if (!node) abort();
node->refcount = 1;
return node;
}
@ -179,6 +180,7 @@ void addtoken(tnode ** root, const char *str, variant id)
}
ref = (tref *)malloc(sizeof(tref));
if (!ref) abort();
ref->ucs = ucs;
ref->node = node;
ref->nexthash = tk->next[index];
@ -221,11 +223,10 @@ void addtoken(tnode ** root, const char *str, variant id)
}
}
void freetokens(tnode * root)
void freetokens(tnode * node)
{
tnode * node = root;
int i;
for (i = 0; node && i != NODEHASHSIZE; ++i) {
for (i = 0; i != NODEHASHSIZE; ++i) {
if (node->next[i]) {
tref * ref = node->next[i];
while (ref) {
@ -237,6 +238,7 @@ void freetokens(tnode * root)
node->next[i] = 0;
}
}
/* TODO: warning C6011: Dereferencing NULL pointer 'node'. */
if (--node->refcount == 0) {
free(node);
}