Add a changestoreload() that checks the capacity of the current changestore, and loads in a new change by its hash.
This allows loading changes dynamically as needed so there is no need to preload anything. On our worstcase (check2) test target, the memory usage drops from 140MB to around 50MB, and the number allocations drop from 1204 to 326.
The xrealloc() function cannot zero out new memory, which can cause segfaults because garbage data is treated as non-NULL pointer values and passed to free(). To work around this, we initialize optional values to NULL as needed.
QSQNGA5KZ4BVWE4FUJUMOP6SJRIXAC2PYU6JSRG5KXWLYWZZMAJQC
EHVLRMEF6YK5FRIV5V7V6P5RHJPCYKMSW5OVKDXLUEWHEM624FBAC
P5CSMRVSVJVOUGGWY7UVP4B3XXNLZDEQVXNL5XOPT2GGB63HXA4QC
RRYWNHFE3EIRQYBGMSZRLDGKWHVMDAO42QNKFQ2KVYPDC4IHANTAC
PC54CREYZ6MOAPTPUUFV5VD3FMRTT6FGOUMOO25GD6BFZJMSS6BAC
CWII6DASI3JOJAA4PXSSVJOEUBT32KTGB3VZXKTUCFXK3KNRH2DAC
UM5XFLGIZYSFAP6TFEZR4APDSSHRHZJDQKXPFGKT6LUAK7QXN33QC
5D2IYPL75HEP6JUEILEADSZRRSV72NECT6UQR3PORNDR35I2P5GQC
Q7TKZCJP2Z75EICZYKCEZDHKGERSOKZGMTSU3UXETBHTF663T66AC
Y26WT3ZFN7KSVXOZ26B5Y2OR4M4VQYQLPMAHPC4O5VIT3ENBISXAC
YFBKBUKBX3JB7OCB4D3ZKSLWTP6QWZ36HEE5GZROJE55RK33MZCAC
LVX6U4EKYXM6H32X5SZS4UBIM5PW4ND4F4QQOF5EQDX3DUHBYFGAC
YDQLW2ZOAH7PZ7HHVTSFUO5IWE6O7FDNXVNIN7GG4TJ3P2B2BM4AC
XTVLIC243WZF52NVTNRA3SV3WLE2ZZ5GTFQOK45IOLMXYQMTLOSAC
}
}
}
struct change *
changestoreget(struct changestore *store, struct hash *hash)
{
size_t i;
for (i = 0; i < store->len; i++) {
if (hasheq(hash, &store->entries[i].hash))
return &store->entries[i].change;
}
return NULL;
}
void
changestoreinit(struct changestore *s, size_t cap)
{
s->entries = xmalloc(sizeof(struct changeentry) * cap);
s->cap = cap;
s->len = 0;
}
void
changestorefree(struct changestore *s)
{
size_t i;
struct change *c;
/* Free all the entries first */
for (i = 0; i < s->len; i++) {
c = &s->entries[i].change;
hashedfree(&c->hashed);
if (c->contents)
free(c->contents);
}
free(s->entries);
}
void
authorsfree(struct authors *authors)
{
size_t i, j;
struct author *author;
for (i = 0; i < authors->len; i++) {
author = &authors->map[i];
for (j = 0; j < author->len; j++) {
free(author->entries[j].key);
free(author->entries[j].value);
void
hashedfree(struct hashed *h)
{
free(h->header.message);
if (h->header.description)
free(h->header.description);
free(h->header.timestamp);
authorsfree(&h->header.authors);
hashlist_free(&h->dependencies);
hashlist_free(&h->extraknown);
if (h->metadata)
free(h->metadata);
hunklistfree(&h->hunks);
}
}
struct change *
changestoreget(struct changestore *store, struct hash *hash)
{
size_t i;
for (i = 0; i < store->len; i++) {
if (hasheq(hash, &store->entries[i].hash))
return &store->entries[i].change;
}
return NULL;
}
// loading changes as needed: ==15877== total heap usage: 326 allocs, 326 frees, 50,066,715 bytes allocated
/**
* load a change by the given hash into the changestore
*/
int
changestoreload(struct changestore *store, struct hash *hash)
{
struct changeentry *ch;
size_t i;
int err;
if (store->len == store->cap) {
/* resize */
size_t newcap = store->cap << 1;
store->entries = xrealloc(
store->entries, sizeof(struct changeentry) * newcap
);
store->cap = newcap;
}
i = store->len;
ch = &store->entries[i];
err = loadchangeh(&ch->change, &ch->hash, store->repodir, hash, 0);
if (err)
return err;
store->len++;
return err;
}
void
changestoreinit(struct changestore *s, size_t cap, const char *repodir)
{
s->repodir = xstrdup(repodir);
s->entries = xmalloc(sizeof(struct changeentry) * cap);
s->cap = cap;
s->len = 0;
}
void
changestorefree(struct changestore *s)
{
size_t i;
struct change *c;
/* Free all the entries first */
for (i = 0; i < s->len; i++) {
c = &s->entries[i].change;
hashedfree(&c->hashed);
if (c->contents)
free(c->contents);
}
free(s->repodir);
free(s->entries);
}
void
authorsfree(struct authors *authors)
{
size_t i, j;
struct author *author;
for (i = 0; i < authors->len; i++) {
author = &authors->map[i];
for (j = 0; j < author->len; j++) {
free(author->entries[j].key);
free(author->entries[j].value);
}
free(author->entries);
}
free(authors->map);
void
hashedfree(struct hashed *h)
{
free(h->header.message);
if (h->header.description)
free(h->header.description);
free(h->header.timestamp);
authorsfree(&h->header.authors);
hashlist_free(&h->dependencies);
hashlist_free(&h->extraknown);
if (h->metadata)
free(h->metadata);
hunklistfree(&h->hunks);
}
for (i = 0; i < ch->change.hashed.dependencies.len; i++) {
err = loadchangeh(
&changestore.entries[x].change,
&changestore.entries[x].hash, repodir,
&ch->change.hashed.dependencies.entries[i], verbose
);
if (err)
goto changeout;
changestore.len++;
changestore.entries[x].num = x + 1;
x++;
}
for (i = 0; i < ch->change.hashed.extraknown.len; i++) {
err = loadchangeh(
&changestore.entries[x].change,
&changestore.entries[x].hash, repodir,
&ch->change.hashed.extraknown.entries[i], verbose
);
if (err)
goto changeout;
changestore.len++;
changestore.entries[x].num = x + 1;
x++;
}