strtab.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /* C string table handling.
  2. Copyright (C) 2000-2026 Free Software Foundation, Inc.
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2, or (at your option)
  6. any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, see <https://www.gnu.org/licenses/>. */
  13. #ifdef HAVE_CONFIG_H
  14. # include <config.h>
  15. #endif
  16. #include <assert.h>
  17. #include <inttypes.h>
  18. #include <stddef.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <sys/cdefs.h>
  23. #include <sys/param.h>
  24. struct Strent
  25. {
  26. const char *string;
  27. size_t len;
  28. struct Strent *next;
  29. struct Strent *left;
  30. struct Strent *right;
  31. size_t offset;
  32. char reverse[0];
  33. };
  34. struct memoryblock
  35. {
  36. struct memoryblock *next;
  37. char memory[0];
  38. };
  39. struct Strtab
  40. {
  41. struct Strent *root;
  42. struct memoryblock *memory;
  43. char *backp;
  44. size_t left;
  45. size_t total;
  46. struct Strent null;
  47. };
  48. /* Cache for the pagesize. We correct this value a bit so that `malloc'
  49. is not allocating more than a page. */
  50. static size_t ps;
  51. #include <programs/xmalloc.h>
  52. /* Prototypes for our functions that are used from iconvconfig.c. If
  53. you change these, change also iconvconfig.c. */
  54. /* Create new C string table object in memory. */
  55. extern struct Strtab *strtabinit (void);
  56. /* Free resources allocated for C string table ST. */
  57. extern void strtabfree (struct Strtab *st);
  58. /* Add string STR (length LEN is != 0) to C string table ST. */
  59. extern struct Strent *strtabadd (struct Strtab *st, const char *str,
  60. size_t len);
  61. /* Finalize string table ST and store size in *SIZE and return a pointer. */
  62. extern void *strtabfinalize (struct Strtab *st, size_t *size);
  63. /* Get offset in string table for string associated with SE. */
  64. extern size_t strtaboffset (struct Strent *se);
  65. struct Strtab *
  66. strtabinit (void)
  67. {
  68. struct Strtab *ret;
  69. if (ps == 0)
  70. {
  71. ps = sysconf (_SC_PAGESIZE) - 2 * sizeof (void *);
  72. assert (sizeof (struct memoryblock) < ps);
  73. }
  74. ret = (struct Strtab *) calloc (1, sizeof (struct Strtab));
  75. if (ret != NULL)
  76. {
  77. ret->null.len = 1;
  78. ret->null.string = "";
  79. }
  80. return ret;
  81. }
  82. static void
  83. morememory (struct Strtab *st, size_t len)
  84. {
  85. struct memoryblock *newmem;
  86. if (len < ps)
  87. len = ps;
  88. newmem = (struct memoryblock *) malloc (len);
  89. if (newmem == NULL)
  90. abort ();
  91. newmem->next = st->memory;
  92. st->memory = newmem;
  93. st->backp = newmem->memory;
  94. st->left = len - offsetof (struct memoryblock, memory);
  95. }
  96. void
  97. strtabfree (struct Strtab *st)
  98. {
  99. struct memoryblock *mb = st->memory;
  100. while (mb != NULL)
  101. {
  102. void *old = mb;
  103. mb = mb->next;
  104. free (old);
  105. }
  106. free (st);
  107. }
  108. static struct Strent *
  109. newstring (struct Strtab *st, const char *str, size_t len)
  110. {
  111. struct Strent *newstr;
  112. size_t align;
  113. int i;
  114. /* Compute the amount of padding needed to make the structure aligned. */
  115. align = ((__alignof__ (struct Strent)
  116. - (((uintptr_t) st->backp)
  117. & (__alignof__ (struct Strent) - 1)))
  118. & (__alignof__ (struct Strent) - 1));
  119. /* Make sure there is enough room in the memory block. */
  120. if (st->left < align + sizeof (struct Strent) + len)
  121. {
  122. morememory (st, sizeof (struct Strent) + len);
  123. align = 0;
  124. }
  125. /* Create the reserved string. */
  126. newstr = (struct Strent *) (st->backp + align);
  127. newstr->string = str;
  128. newstr->len = len;
  129. newstr->next = NULL;
  130. newstr->left = NULL;
  131. newstr->right = NULL;
  132. newstr->offset = 0;
  133. for (i = len - 2; i >= 0; --i)
  134. newstr->reverse[i] = str[len - 2 - i];
  135. newstr->reverse[len - 1] = '\0';
  136. st->backp += align + sizeof (struct Strent) + len;
  137. st->left -= align + sizeof (struct Strent) + len;
  138. return newstr;
  139. }
  140. /* XXX This function should definitely be rewritten to use a balancing
  141. tree algorithm (AVL, red-black trees). For now a simple, correct
  142. implementation is enough. */
  143. static struct Strent **
  144. searchstring (struct Strent **sep, struct Strent *newstr)
  145. {
  146. int cmpres;
  147. /* More strings? */
  148. if (*sep == NULL)
  149. {
  150. *sep = newstr;
  151. return sep;
  152. }
  153. /* Compare the strings. */
  154. cmpres = memcmp ((*sep)->reverse, newstr->reverse,
  155. MIN ((*sep)->len, newstr->len) - 1);
  156. if (cmpres == 0)
  157. /* We found a matching string. */
  158. return sep;
  159. else if (cmpres > 0)
  160. return searchstring (&(*sep)->left, newstr);
  161. else
  162. return searchstring (&(*sep)->right, newstr);
  163. }
  164. /* Add new string. The actual string is assumed to be permanent. */
  165. struct Strent *
  166. strtabadd (struct Strtab *st, const char *str, size_t len)
  167. {
  168. struct Strent *newstr;
  169. struct Strent **sep;
  170. /* Compute the string length if the caller doesn't know it. */
  171. if (len == 0)
  172. len = strlen (str) + 1;
  173. /* Make sure all "" strings get offset 0. */
  174. if (len == 1)
  175. return &st->null;
  176. /* Allocate memory for the new string and its associated information. */
  177. newstr = newstring (st, str, len);
  178. /* Search in the array for the place to insert the string. If there
  179. is no string with matching prefix and no string with matching
  180. leading substring, create a new entry. */
  181. sep = searchstring (&st->root, newstr);
  182. if (*sep != newstr)
  183. {
  184. /* This is not the same entry. This means we have a prefix match. */
  185. if ((*sep)->len > newstr->len)
  186. {
  187. struct Strent *subs;
  188. for (subs = (*sep)->next; subs; subs = subs->next)
  189. if (subs->len == newstr->len)
  190. {
  191. /* We have an exact match with a substring. Free the memory
  192. we allocated. */
  193. st->left += st->backp - (char *) newstr;
  194. st->backp = (char *) newstr;
  195. return subs;
  196. }
  197. /* We have a new substring. This means we don't need the reverse
  198. string of this entry anymore. */
  199. st->backp -= newstr->len;
  200. st->left += newstr->len;
  201. newstr->next = (*sep)->next;
  202. (*sep)->next = newstr;
  203. }
  204. else if ((*sep)->len != newstr->len)
  205. {
  206. /* When we get here it means that the string we are about to
  207. add has a common prefix with a string we already have but
  208. it is longer. In this case we have to put it first. */
  209. st->total += newstr->len - (*sep)->len;
  210. newstr->next = *sep;
  211. newstr->left = (*sep)->left;
  212. newstr->right = (*sep)->right;
  213. *sep = newstr;
  214. }
  215. else
  216. {
  217. /* We have an exact match. Free the memory we allocated. */
  218. st->left += st->backp - (char *) newstr;
  219. st->backp = (char *) newstr;
  220. newstr = *sep;
  221. }
  222. }
  223. else
  224. st->total += newstr->len;
  225. return newstr;
  226. }
  227. static void
  228. copystrings (struct Strent *nodep, char **freep, size_t *offsetp)
  229. {
  230. struct Strent *subs;
  231. if (nodep->left != NULL)
  232. copystrings (nodep->left, freep, offsetp);
  233. /* Process the current node. */
  234. nodep->offset = *offsetp;
  235. *freep = (char *) mempcpy (*freep, nodep->string, nodep->len);
  236. *offsetp += nodep->len;
  237. for (subs = nodep->next; subs != NULL; subs = subs->next)
  238. {
  239. assert (subs->len < nodep->len);
  240. subs->offset = nodep->offset + nodep->len - subs->len;
  241. }
  242. if (nodep->right != NULL)
  243. copystrings (nodep->right, freep, offsetp);
  244. }
  245. void *
  246. strtabfinalize (struct Strtab *st, size_t *size)
  247. {
  248. size_t copylen;
  249. char *endp;
  250. char *retval;
  251. /* Fill in the information. */
  252. endp = retval = (char *) xmalloc (st->total + 1);
  253. /* Always put an empty string at the beginning so that a zero offset
  254. can mean error. */
  255. *endp++ = '\0';
  256. /* Now run through the tree and add all the string while also updating
  257. the offset members of the elfstrent records. */
  258. copylen = 1;
  259. copystrings (st->root, &endp, &copylen);
  260. assert (copylen == st->total + 1);
  261. assert (endp == retval + st->total + 1);
  262. *size = copylen;
  263. return retval;
  264. }
  265. size_t
  266. strtaboffset (struct Strent *se)
  267. {
  268. return se->offset;
  269. }