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