1 /* Generic string table handling.
2 Copyright (C) 2000, 2001, 2002, 2005 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2000.
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
19 or both in parallel, as here.
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
41 #include <sys/param.h>
46 # define MIN(a, b) ((a) < (b) ? (a) : (b))
54 struct Ebl_GStrent *next;
55 struct Ebl_GStrent *left;
56 struct Ebl_GStrent *right;
65 struct memoryblock *next;
72 struct Ebl_GStrent *root;
73 struct memoryblock *memory;
80 struct Ebl_GStrent null;
84 /* Cache for the pagesize. We correct this value a bit so that `malloc'
85 is not allocating more than a page. */
90 ebl_gstrtabinit (unsigned int width, bool nullstr)
92 struct Ebl_GStrtab *ret;
96 ps = sysconf (_SC_PAGESIZE) - 2 * sizeof (void *);
97 assert (sizeof (struct memoryblock) < ps);
100 ret = (struct Ebl_GStrtab *) calloc (1, sizeof (struct Ebl_GStrtab));
104 ret->nullstr = nullstr;
109 ret->null.string = (char *) calloc (1, width);
118 morememory (struct Ebl_GStrtab *st, size_t len)
120 struct memoryblock *newmem;
124 newmem = (struct memoryblock *) malloc (len);
128 newmem->next = st->memory;
130 st->backp = newmem->memory;
131 st->left = len - offsetof (struct memoryblock, memory);
136 ebl_gstrtabfree (struct Ebl_GStrtab *st)
138 struct memoryblock *mb = st->memory;
147 if (st->null.string != NULL)
148 free ((char *) st->null.string);
154 static struct Ebl_GStrent *
155 newstring (struct Ebl_GStrtab *st, const char *str, size_t len)
157 /* Compute the amount of padding needed to make the structure aligned. */
158 size_t align = ((__alignof__ (struct Ebl_GStrent)
159 - (((uintptr_t) st->backp)
160 & (__alignof__ (struct Ebl_GStrent) - 1)))
161 & (__alignof__ (struct Ebl_GStrent) - 1));
163 /* Make sure there is enough room in the memory block. */
164 if (st->left < align + sizeof (struct Ebl_GStrent) + len * st->width)
166 morememory (st, sizeof (struct Ebl_GStrent) + len * st->width);
170 /* Create the reserved string. */
171 struct Ebl_GStrent *newstr = (struct Ebl_GStrent *) (st->backp + align);
172 newstr->string = str;
174 newstr->width = st->width;
177 newstr->right = NULL;
179 for (int i = len - 2; i >= 0; --i)
180 for (int j = st->width - 1; j >= 0; --j)
181 newstr->reverse[i * st->width + j] = str[(len - 2 - i) * st->width + j];
182 for (size_t j = 0; j < st->width; ++j)
183 newstr->reverse[(len - 1) * st->width + j] = '\0';
184 st->backp += align + sizeof (struct Ebl_GStrent) + len * st->width;
185 st->left -= align + sizeof (struct Ebl_GStrent) + len * st->width;
191 /* XXX This function should definitely be rewritten to use a balancing
192 tree algorith (AVL, red-black trees). For now a simple, correct
193 implementation is enough. */
194 static struct Ebl_GStrent **
195 searchstring (struct Ebl_GStrent **sep, struct Ebl_GStrent *newstr)
206 /* Compare the strings. */
207 cmpres = memcmp ((*sep)->reverse, newstr->reverse,
208 (MIN ((*sep)->len, newstr->len) - 1) * (*sep)->width);
210 /* We found a matching string. */
213 return searchstring (&(*sep)->left, newstr);
215 return searchstring (&(*sep)->right, newstr);
219 /* Add new string. The actual string is assumed to be permanent. */
221 ebl_gstrtabadd (struct Ebl_GStrtab *st, const char *str, size_t len)
223 struct Ebl_GStrent *newstr;
224 struct Ebl_GStrent **sep;
226 /* Compute the string length if the caller doesn't know it. */
232 for (j = 0; j < st->width; ++j)
233 if (str[len * st->width + j] != '\0')
235 while (j == st->width && ++len);
238 /* Make sure all "" strings get offset 0 but only if the table was
239 created with a special null entry in mind. */
240 if (len == 1 && st->null.string != NULL)
243 /* Allocate memory for the new string and its associated information. */
244 newstr = newstring (st, str, len);
246 /* Search in the array for the place to insert the string. If there
247 is no string with matching prefix and no string with matching
248 leading substring, create a new entry. */
249 sep = searchstring (&st->root, newstr);
252 /* This is not the same entry. This means we have a prefix match. */
253 if ((*sep)->len > newstr->len)
255 struct Ebl_GStrent *subs;
257 /* Check whether we already know this string. */
258 for (subs = (*sep)->next; subs != NULL; subs = subs->next)
259 if (subs->len == newstr->len)
261 /* We have an exact match with a substring. Free the memory
263 st->left += (st->backp - (char *) newstr) * st->width;
264 st->backp = (char *) newstr;
269 /* We have a new substring. This means we don't need the reverse
270 string of this entry anymore. */
271 st->backp -= newstr->len;
272 st->left += newstr->len;
274 newstr->next = (*sep)->next;
275 (*sep)->next = newstr;
277 else if ((*sep)->len != newstr->len)
279 /* When we get here it means that the string we are about to
280 add has a common prefix with a string we already have but
281 it is longer. In this case we have to put it first. */
282 st->total += newstr->len - (*sep)->len;
284 newstr->left = (*sep)->left;
285 newstr->right = (*sep)->right;
290 /* We have an exact match. Free the memory we allocated. */
291 st->left += (st->backp - (char *) newstr) * st->width;
292 st->backp = (char *) newstr;
298 st->total += newstr->len;
305 copystrings (struct Ebl_GStrent *nodep, char **freep, size_t *offsetp)
307 struct Ebl_GStrent *subs;
309 if (nodep->left != NULL)
310 copystrings (nodep->left, freep, offsetp);
312 /* Process the current node. */
313 nodep->offset = *offsetp;
314 *freep = (char *) mempcpy (*freep, nodep->string, nodep->len * nodep->width);
315 *offsetp += nodep->len * nodep->width;
317 for (subs = nodep->next; subs != NULL; subs = subs->next)
319 assert (subs->len < nodep->len);
320 subs->offset = nodep->offset + (nodep->len - subs->len) * nodep->width;
321 assert (subs->offset != 0 || subs->string[0] == '\0');
324 if (nodep->right != NULL)
325 copystrings (nodep->right, freep, offsetp);
330 ebl_gstrtabfinalize (struct Ebl_GStrtab *st, Elf_Data *data)
334 size_t nulllen = st->nullstr ? st->width : 0;
336 /* Fill in the information. */
337 data->d_buf = malloc (st->total + nulllen);
338 if (data->d_buf == NULL)
341 /* The first byte must always be zero if we created the table with a
344 memset (data->d_buf, '\0', st->width);
346 data->d_type = ELF_T_BYTE;
347 data->d_size = st->total + nulllen;
350 data->d_version = EV_CURRENT;
352 /* Now run through the tree and add all the string while also updating
353 the offset members of the elfstrent records. */
354 endp = (char *) data->d_buf + nulllen;
356 copystrings (st->root, &endp, ©len);
357 assert (copylen == st->total * st->width + nulllen);
362 ebl_gstrtaboffset (struct Ebl_GStrent *se)