1 /* C string table handling.
2 Copyright (C) 2000-2024 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
27 #include <sys/cdefs.h>
28 #include <sys/param.h>
45 struct memoryblock
*next
;
53 struct memoryblock
*memory
;
62 /* Cache for the pagesize. We correct this value a bit so that `malloc'
63 is not allocating more than a page. */
67 #include <programs/xmalloc.h>
69 /* Prototypes for our functions that are used from iconvconfig.c. If
70 you change these, change also iconvconfig.c. */
71 /* Create new C string table object in memory. */
72 extern struct Strtab
*strtabinit (void);
74 /* Free resources allocated for C string table ST. */
75 extern void strtabfree (struct Strtab
*st
);
77 /* Add string STR (length LEN is != 0) to C string table ST. */
78 extern struct Strent
*strtabadd (struct Strtab
*st
, const char *str
,
81 /* Finalize string table ST and store size in *SIZE and return a pointer. */
82 extern void *strtabfinalize (struct Strtab
*st
, size_t *size
);
84 /* Get offset in string table for string associated with SE. */
85 extern size_t strtaboffset (struct Strent
*se
);
95 ps
= sysconf (_SC_PAGESIZE
) - 2 * sizeof (void *);
96 assert (sizeof (struct memoryblock
) < ps
);
99 ret
= (struct Strtab
*) calloc (1, sizeof (struct Strtab
));
103 ret
->null
.string
= "";
110 morememory (struct Strtab
*st
, size_t len
)
112 struct memoryblock
*newmem
;
116 newmem
= (struct memoryblock
*) malloc (len
);
120 newmem
->next
= st
->memory
;
122 st
->backp
= newmem
->memory
;
123 st
->left
= len
- offsetof (struct memoryblock
, memory
);
128 strtabfree (struct Strtab
*st
)
130 struct memoryblock
*mb
= st
->memory
;
143 static struct Strent
*
144 newstring (struct Strtab
*st
, const char *str
, size_t len
)
146 struct Strent
*newstr
;
150 /* Compute the amount of padding needed to make the structure aligned. */
151 align
= ((__alignof__ (struct Strent
)
152 - (((uintptr_t) st
->backp
)
153 & (__alignof__ (struct Strent
) - 1)))
154 & (__alignof__ (struct Strent
) - 1));
156 /* Make sure there is enough room in the memory block. */
157 if (st
->left
< align
+ sizeof (struct Strent
) + len
)
159 morememory (st
, sizeof (struct Strent
) + len
);
163 /* Create the reserved string. */
164 newstr
= (struct Strent
*) (st
->backp
+ align
);
165 newstr
->string
= str
;
169 newstr
->right
= NULL
;
171 for (i
= len
- 2; i
>= 0; --i
)
172 newstr
->reverse
[i
] = str
[len
- 2 - i
];
173 newstr
->reverse
[len
- 1] = '\0';
174 st
->backp
+= align
+ sizeof (struct Strent
) + len
;
175 st
->left
-= align
+ sizeof (struct Strent
) + len
;
181 /* XXX This function should definitely be rewritten to use a balancing
182 tree algorithm (AVL, red-black trees). For now a simple, correct
183 implementation is enough. */
184 static struct Strent
**
185 searchstring (struct Strent
**sep
, struct Strent
*newstr
)
196 /* Compare the strings. */
197 cmpres
= memcmp ((*sep
)->reverse
, newstr
->reverse
,
198 MIN ((*sep
)->len
, newstr
->len
) - 1);
200 /* We found a matching string. */
203 return searchstring (&(*sep
)->left
, newstr
);
205 return searchstring (&(*sep
)->right
, newstr
);
209 /* Add new string. The actual string is assumed to be permanent. */
211 strtabadd (struct Strtab
*st
, const char *str
, size_t len
)
213 struct Strent
*newstr
;
216 /* Compute the string length if the caller doesn't know it. */
218 len
= strlen (str
) + 1;
220 /* Make sure all "" strings get offset 0. */
224 /* Allocate memory for the new string and its associated information. */
225 newstr
= newstring (st
, str
, len
);
227 /* Search in the array for the place to insert the string. If there
228 is no string with matching prefix and no string with matching
229 leading substring, create a new entry. */
230 sep
= searchstring (&st
->root
, newstr
);
233 /* This is not the same entry. This means we have a prefix match. */
234 if ((*sep
)->len
> newstr
->len
)
238 for (subs
= (*sep
)->next
; subs
; subs
= subs
->next
)
239 if (subs
->len
== newstr
->len
)
241 /* We have an exact match with a substring. Free the memory
243 st
->left
+= st
->backp
- (char *) newstr
;
244 st
->backp
= (char *) newstr
;
249 /* We have a new substring. This means we don't need the reverse
250 string of this entry anymore. */
251 st
->backp
-= newstr
->len
;
252 st
->left
+= newstr
->len
;
254 newstr
->next
= (*sep
)->next
;
255 (*sep
)->next
= newstr
;
257 else if ((*sep
)->len
!= newstr
->len
)
259 /* When we get here it means that the string we are about to
260 add has a common prefix with a string we already have but
261 it is longer. In this case we have to put it first. */
262 st
->total
+= newstr
->len
- (*sep
)->len
;
264 newstr
->left
= (*sep
)->left
;
265 newstr
->right
= (*sep
)->right
;
270 /* We have an exact match. Free the memory we allocated. */
271 st
->left
+= st
->backp
- (char *) newstr
;
272 st
->backp
= (char *) newstr
;
278 st
->total
+= newstr
->len
;
285 copystrings (struct Strent
*nodep
, char **freep
, size_t *offsetp
)
289 if (nodep
->left
!= NULL
)
290 copystrings (nodep
->left
, freep
, offsetp
);
292 /* Process the current node. */
293 nodep
->offset
= *offsetp
;
294 *freep
= (char *) mempcpy (*freep
, nodep
->string
, nodep
->len
);
295 *offsetp
+= nodep
->len
;
297 for (subs
= nodep
->next
; subs
!= NULL
; subs
= subs
->next
)
299 assert (subs
->len
< nodep
->len
);
300 subs
->offset
= nodep
->offset
+ nodep
->len
- subs
->len
;
303 if (nodep
->right
!= NULL
)
304 copystrings (nodep
->right
, freep
, offsetp
);
309 strtabfinalize (struct Strtab
*st
, size_t *size
)
315 /* Fill in the information. */
316 endp
= retval
= (char *) xmalloc (st
->total
+ 1);
318 /* Always put an empty string at the beginning so that a zero offset
322 /* Now run through the tree and add all the string while also updating
323 the offset members of the elfstrent records. */
325 copystrings (st
->root
, &endp
, ©len
);
326 assert (copylen
== st
->total
+ 1);
327 assert (endp
== retval
+ st
->total
+ 1);
335 strtaboffset (struct Strent
*se
)