#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <inttypes.h>
#include <libelf.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "libdwelfP.h"
#include <system.h>
struct Dwelf_Strent
{
const char *string;
size_t len;
struct Dwelf_Strent *next;
struct Dwelf_Strent *left;
struct Dwelf_Strent *right;
size_t offset;
char reverse[0];
};
struct memoryblock
{
struct memoryblock *next;
char memory[0];
};
struct Dwelf_Strtab
{
struct Dwelf_Strent *root;
struct memoryblock *memory;
char *backp;
size_t left;
size_t total;
bool nullstr;
struct Dwelf_Strent null;
};
static size_t ps;
#define MALLOC_OVERHEAD (2 * sizeof (void *))
Dwelf_Strtab *
dwelf_strtab_init (bool nullstr)
{
if (ps == 0)
{
ps = sysconf (_SC_PAGESIZE);
assert (sizeof (struct memoryblock) < ps - MALLOC_OVERHEAD);
}
Dwelf_Strtab *ret = calloc (1, sizeof (struct Dwelf_Strtab));
if (ret != NULL)
{
ret->nullstr = nullstr;
if (nullstr)
{
ret->null.len = 1;
ret->null.string = "";
}
}
return ret;
}
static int
morememory (Dwelf_Strtab *st, size_t len)
{
size_t overhead = offsetof (struct memoryblock, memory);
len += overhead + MALLOC_OVERHEAD;
len = ((len / ps) + (len % ps != 0)) * ps - MALLOC_OVERHEAD;
struct memoryblock *newmem = malloc (len);
if (newmem == NULL)
return 1;
newmem->next = st->memory;
st->memory = newmem;
st->backp = newmem->memory;
st->left = len - overhead;
return 0;
}
void
dwelf_strtab_free (Dwelf_Strtab *st)
{
struct memoryblock *mb = st->memory;
while (mb != NULL)
{
void *old = mb;
mb = mb->next;
free (old);
}
free (st);
}
static Dwelf_Strent *
newstring (Dwelf_Strtab *st, const char *str, size_t len)
{
size_t align = ((__alignof__ (struct Dwelf_Strent)
- (((uintptr_t) st->backp)
& (__alignof__ (struct Dwelf_Strent) - 1)))
& (__alignof__ (struct Dwelf_Strent) - 1));
if (st->left < align + sizeof (struct Dwelf_Strent) + len)
{
if (morememory (st, sizeof (struct Dwelf_Strent) + len))
return NULL;
align = 0;
}
Dwelf_Strent *newstr = (Dwelf_Strent *) (st->backp + align);
newstr->string = str;
newstr->len = len;
newstr->next = NULL;
newstr->left = NULL;
newstr->right = NULL;
newstr->offset = 0;
for (int i = len - 2; i >= 0; --i)
newstr->reverse[i] = str[len - 2 - i];
newstr->reverse[len - 1] = '\0';
st->backp += align + sizeof (struct Dwelf_Strent) + len;
st->left -= align + sizeof (struct Dwelf_Strent) + len;
return newstr;
}
static Dwelf_Strent **
searchstring (Dwelf_Strent **sep, Dwelf_Strent *newstr)
{
if (*sep == NULL)
{
*sep = newstr;
return sep;
}
int cmpres = memcmp ((*sep)->reverse, newstr->reverse,
MIN ((*sep)->len, newstr->len) - 1);
if (cmpres == 0)
return sep;
else if (cmpres > 0)
return searchstring (&(*sep)->left, newstr);
else
return searchstring (&(*sep)->right, newstr);
}
static Dwelf_Strent *
strtab_add (Dwelf_Strtab *st, const char *str, size_t len)
{
if (len == 1 && st->null.string != NULL)
return &st->null;
Dwelf_Strent *newstr = newstring (st, str, len);
if (newstr == NULL)
return NULL;
Dwelf_Strent **sep = searchstring (&st->root, newstr);
if (*sep != newstr)
{
if ((*sep)->len > newstr->len)
{
for (Dwelf_Strent *subs = (*sep)->next; subs != NULL;
subs = subs->next)
if (subs->len == newstr->len)
{
st->left += st->backp - (char *) newstr;
st->backp = (char *) newstr;
return subs;
}
st->backp -= newstr->len;
st->left += newstr->len;
newstr->next = (*sep)->next;
(*sep)->next = newstr;
}
else if ((*sep)->len != newstr->len)
{
st->total += newstr->len - (*sep)->len;
newstr->next = *sep;
newstr->left = (*sep)->left;
newstr->right = (*sep)->right;
*sep = newstr;
}
else
{
st->left += st->backp - (char *) newstr;
st->backp = (char *) newstr;
newstr = *sep;
}
}
else
st->total += newstr->len;
return newstr;
}
Dwelf_Strent *
dwelf_strtab_add (Dwelf_Strtab *st, const char *str)
{
return strtab_add (st, str, strlen (str) + 1);
}
Dwelf_Strent *
dwelf_strtab_add_len (Dwelf_Strtab *st, const char *str, size_t len)
{
return strtab_add (st, str, len);
}
static void
copystrings (Dwelf_Strent *nodep, char **freep, size_t *offsetp)
{
if (nodep->left != NULL)
copystrings (nodep->left, freep, offsetp);
nodep->offset = *offsetp;
*freep = (char *) mempcpy (*freep, nodep->string, nodep->len);
*offsetp += nodep->len;
for (Dwelf_Strent *subs = nodep->next; subs != NULL; subs = subs->next)
{
assert (subs->len < nodep->len);
subs->offset = nodep->offset + nodep->len - subs->len;
assert (subs->offset != 0 || subs->string[0] == '\0');
}
if (nodep->right != NULL)
copystrings (nodep->right, freep, offsetp);
}
Elf_Data *
dwelf_strtab_finalize (Dwelf_Strtab *st, Elf_Data *data)
{
size_t nulllen = st->nullstr ? 1 : 0;
data->d_buf = malloc (st->total + nulllen);
if (data->d_buf == NULL)
return NULL;
if (st->nullstr)
*((char *) data->d_buf) = '\0';
data->d_type = ELF_T_BYTE;
data->d_size = st->total + nulllen;
data->d_off = 0;
data->d_align = 1;
data->d_version = EV_CURRENT;
char *endp = (char *) data->d_buf + nulllen;
size_t copylen = nulllen;
if (st->root)
copystrings (st->root, &endp, ©len);
assert (copylen == st->total + nulllen);
return data;
}
size_t
dwelf_strent_off (Dwelf_Strent *se)
{
return se->offset;
}
const char *
dwelf_strent_str (Dwelf_Strent *se)
{
return se->string;
}