1 /*
2 * mpatrol
3 * A library for controlling and tracing dynamic memory allocations.
4 * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * MA 02111-1307, USA.
20 */
21
22
23 /*
24 * String tables. All memory allocations are performed by the heap
25 * manager and all strnodes are stored at the beginning of their
26 * respective blocks of memory. The hash function comes from P. J.
27 * Weinberger's C compiler and was published in Compilers: Principles,
28 * Techniques and Tools, First Edition by Aho, Sethi and Ullman
29 * (Addison-Wesley, 1986, ISBN 0-201-10194-7).
30 */
31
32
33 #include "strtab.h"
34 #include "utils.h"
35 #include <string.h>
36
37
38 #if MP_IDENT_SUPPORT
39 #ident "$Id: strtab.c,v 1.17 2002/01/08 20:13:59 graeme Exp $"
40 #else /* MP_IDENT_SUPPORT */
41 static MP_CONST MP_VOLATILE char *strtab_id = "$Id: strtab.c,v 1.17 2002/01/08 20:13:59 graeme Exp $";
42 #endif /* MP_IDENT_SUPPORT */
43
44
45 #ifdef __cplusplus
46 extern "C"
47 {
48 #endif /* __cplusplus */
49
50
51 /* Initialise the fields of a strtab so that the string table becomes empty.
52 */
53
54 MP_GLOBAL
55 void
__mp_newstrtab(strtab * t,heaphead * h)56 __mp_newstrtab(strtab *t, heaphead *h)
57 {
58 struct { char x; hashentry y; } w;
59 struct { char x; strnode y; } z;
60 size_t i;
61 long n;
62
63 t->heap = h;
64 n = (char *) &w.y - &w.x;
65 __mp_newslots(&t->table, sizeof(hashentry), __mp_poweroftwo(n));
66 for (i = 0; i < MP_HASHTAB_SIZE; i++)
67 __mp_newlist(&t->slots[i]);
68 __mp_newlist(&t->list);
69 __mp_newtree(&t->tree);
70 t->size = 0;
71 /* Determine the minimum alignment for a strnode on this system
72 * and force the alignment to be a power of two.
73 */
74 n = (char *) &z.y - &z.x;
75 t->align = __mp_poweroftwo(n);
76 t->prot = MA_NOACCESS;
77 t->protrecur = 0;
78 }
79
80
81 /* Forget all data currently in the string table.
82 */
83
84 MP_GLOBAL
85 void
__mp_deletestrtab(strtab * t)86 __mp_deletestrtab(strtab *t)
87 {
88 size_t i;
89
90 /* We don't need to explicitly free any memory as this is dealt with
91 * at a lower level by the heap manager.
92 */
93 t->heap = NULL;
94 t->table.free = NULL;
95 t->table.size = 0;
96 for (i = 0; i < MP_HASHTAB_SIZE; i++)
97 __mp_newlist(&t->slots[i]);
98 __mp_newlist(&t->list);
99 __mp_newtree(&t->tree);
100 t->size = 0;
101 t->prot = MA_NOACCESS;
102 t->protrecur = 0;
103 }
104
105
106 /* Calculate the hash bucket a string should be placed in.
107 */
108
109 static
110 unsigned long
hash(char * s)111 hash(char *s)
112 {
113 unsigned long g, h;
114
115 for (h = 0; *s != '\0'; s++)
116 {
117 h = (h << 4) + *s;
118 if (g = h & (0xFUL << ((sizeof(unsigned long) << 3) - 4)))
119 {
120 h ^= g >> ((sizeof(unsigned long) << 3) - 8);
121 h ^= g;
122 }
123 }
124 return h % MP_HASHTAB_SIZE;
125 }
126
127
128 /* Allocate a new hash entry.
129 */
130
131 static
132 hashentry *
gethashentry(strtab * t)133 gethashentry(strtab *t)
134 {
135 hashentry *e;
136 heapnode *p;
137
138 /* If we have no more hash entry slots left then we must allocate
139 * some more memory for them. An extra MP_ALLOCFACTOR pages of memory
140 * should suffice.
141 */
142 if ((e = (hashentry *) __mp_getslot(&t->table)) == NULL)
143 {
144 if ((p = __mp_heapalloc(t->heap, t->heap->memory.page * MP_ALLOCFACTOR,
145 t->table.entalign, 1)) == NULL)
146 return NULL;
147 __mp_initslots(&t->table, p->block, p->size);
148 e = (hashentry *) __mp_getslot(&t->table);
149 __mp_addtail(&t->list, &e->node);
150 e->data.block = p->block;
151 e->size = p->size;
152 t->size += p->size;
153 e = (hashentry *) __mp_getslot(&t->table);
154 }
155 return e;
156 }
157
158
159 /* Add a new string to the string table.
160 */
161
162 MP_GLOBAL
163 char *
__mp_addstring(strtab * t,char * s)164 __mp_addstring(strtab *t, char *s)
165 {
166 hashentry *e;
167 strnode *n;
168 heapnode *p;
169 char *r;
170 size_t k, l, m;
171
172 k = hash(s);
173 l = strlen(s) + 1;
174 /* Search to see if the string already exists in the hash table and
175 * return it if it does.
176 */
177 for (e = (hashentry *) t->slots[k].head; e->node.next != NULL;
178 e = (hashentry *) e->node.next)
179 if ((e->size == l) && (strcmp(e->data.key, s) == 0))
180 return e->data.key;
181 if ((e = gethashentry(t)) == NULL)
182 return NULL;
183 /* If we have no suitable space left then we must allocate some more
184 * memory for the string table in order for it to be able to hold the
185 * new string. The size of the new string is rounded up to a multiple
186 * of the system page size and is then multiplied by MP_ALLOCFACTOR.
187 */
188 if ((n = (strnode *) __mp_searchhigher(t->tree.root, l)) == NULL)
189 {
190 m = __mp_roundup(sizeof(strnode) + l, t->heap->memory.page);
191 if ((p = __mp_heapalloc(t->heap, m * MP_ALLOCFACTOR, t->align, 1)) ==
192 NULL)
193 {
194 __mp_freeslot(&t->table, e);
195 return NULL;
196 }
197 n = (strnode *) p->block;
198 n->block = p->block;
199 n->next = (char *) p->block + sizeof(strnode);
200 n->avail = p->size - sizeof(strnode);
201 n->size = p->size;
202 t->size += p->size;
203 }
204 else
205 __mp_treeremove(&t->tree, &n->node);
206 r = n->next;
207 __mp_memcopy(r, s, l);
208 n->next += l;
209 n->avail -= l;
210 /* We have already removed the strnode from the allocation tree since
211 * we have altered the number of available characters within it. We now
212 * insert the strnode back into the tree, reflecting its new status.
213 */
214 __mp_treeinsert(&t->tree, &n->node, n->avail);
215 /* We now add the string to the hash table.
216 */
217 __mp_addhead(&t->slots[k], &e->node);
218 e->data.key = r;
219 e->size = l;
220 return r;
221 }
222
223
224 /* Protect the memory blocks used by the string table with the
225 * supplied access permission.
226 */
227
228 MP_GLOBAL
229 int
__mp_protectstrtab(strtab * t,memaccess a)230 __mp_protectstrtab(strtab *t, memaccess a)
231 {
232 hashentry *e;
233 strnode *n;
234
235 /* The library already knows what its protection status is so we don't
236 * need to do anything if the request has already been done.
237 */
238 if (t->prot == a)
239 {
240 t->protrecur++;
241 return 1;
242 }
243 else if (t->protrecur > 0)
244 {
245 t->protrecur--;
246 return 1;
247 }
248 t->prot = a;
249 for (n = (strnode *) __mp_minimum(t->tree.root); n != NULL;
250 n = (strnode *) __mp_successor(&n->node))
251 if (!__mp_memprotect(&t->heap->memory, n->block, n->size, a))
252 return 0;
253 for (e = (hashentry *) t->list.head; e->node.next != NULL;
254 e = (hashentry *) e->node.next)
255 if (!__mp_memprotect(&t->heap->memory, e->data.block, e->size, a))
256 return 0;
257 return 1;
258 }
259
260
261 #ifdef __cplusplus
262 }
263 #endif /* __cplusplus */
264