VirtualBox

source: kBuild/trunk/src/kmk/strcache.c@ 1843

Last change on this file since 1843 was 1843, checked in by bird, 17 years ago

kmk/strcache: align the strings on a (somewhat) natural boundrary and offer a service for retrieving their length (which we also store now).

  • Property svn:eol-style set to native
File size: 7.7 KB
Line 
1/* Constant string caching for GNU Make.
2Copyright (C) 2006 Free Software Foundation, Inc.
3This file is part of GNU Make.
4
5GNU Make is free software; you can redistribute it and/or modify it under the
6terms of the GNU General Public License as published by the Free Software
7Foundation; either version 2, or (at your option) any later version.
8
9GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13You should have received a copy of the GNU General Public License along with
14GNU Make; see the file COPYING. If not, write to the Free Software
15Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
16
17#include "make.h"
18
19#include <assert.h>
20
21#include "hash.h"
22
23/* The size (in bytes) of each cache buffer.
24 Try to pick something that will map well into the heap. */
25#define CACHE_BUFFER_SIZE (8192 - 16)
26
27
28/* A string cached here will never be freed, so we don't need to worry about
29 reference counting. We just store the string, and then remember it in a
30 hash so it can be looked up again. */
31
32struct strcache {
33 struct strcache *next; /* The next block of strings. */
34 char *end; /* Pointer to the beginning of the free space. */
35 int count; /* # of strings in this buffer (for stats). */
36 int bytesfree; /* The amount of the buffer that is free. */
37 char buffer[1]; /* The buffer comes after this. */
38};
39
40static int bufsize = CACHE_BUFFER_SIZE;
41static struct strcache *strcache = NULL;
42
43/* Add a new buffer to the cache. Add it at the front to reduce search time.
44 This can also increase the overhead, since it's less likely that older
45 buffers will be filled in. However, GNU make has so many smaller strings
46 that this doesn't seem to be much of an issue in practice.
47 */
48static struct strcache *
49new_cache()
50{
51 struct strcache *new;
52 new = xmalloc (sizeof (*new) + bufsize);
53 new->end = new->buffer;
54 new->count = 0;
55 new->bytesfree = bufsize;
56
57 new->next = strcache;
58 strcache = new;
59
60 return new;
61}
62
63static const char *
64add_string(const char *str, int len)
65{
66 struct strcache *best = NULL;
67 struct strcache *sp;
68 const char *res;
69#ifdef CONFIG_WITH_VALUE_LENGTH
70 int str_len = len;
71 char *tmp;
72
73 /* Add space a length prefix and the terminator and assure
74 (somewhat) natural alignment. */
75 len += sizeof(unsigned int) + 1;
76 len = (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
77 --len;
78#endif
79
80 /* If the string we want is too large to fit into a single buffer, then
81 we're screwed; nothing will ever fit! Change the maximum size of the
82 cache to be big enough. */
83 if (len > bufsize)
84 bufsize = len * 2;
85
86 /* First, find a cache with enough free space. We always look through all
87 the blocks and choose the one with the best fit (the one that leaves the
88 least amount of space free). */
89 for (sp = strcache; sp != NULL; sp = sp->next)
90 if (sp->bytesfree > len && (!best || best->bytesfree > sp->bytesfree))
91 best = sp;
92
93 /* If nothing is big enough, make a new cache. */
94 if (!best)
95 best = new_cache();
96
97 assert (best->bytesfree > len);
98
99 /* Add the string to the best cache. */
100#ifndef CONFIG_WITH_VALUE_LENGTH
101 res = best->end;
102 memcpy (best->end, str, len);
103 best->end += len;
104 *(best->end++) = '\0';
105 best->bytesfree -= len + 1;
106 ++best->count;
107#else /* CONFIG_WITH_VALUE_LENGTH */
108 tmp = best->end;
109 best->end += len + 1;
110 assert(!((size_t)tmp & (sizeof(void *) - 1)));
111
112 *(unsigned int *)tmp = str_len; /* length prefix */
113 tmp += sizeof(unsigned int);
114
115 res = tmp;
116 memcpy (tmp, str, str_len);
117 tmp += str_len;
118 do
119 *(tmp++) = '\0';
120 while (tmp < best->end);
121
122 best->bytesfree -= len + 1;
123 ++best->count;
124
125 assert (tmp == best->end);
126 assert (!((size_t)res & (sizeof(void *) - 1)));
127#endif /* CONFIG_WITH_VALUE_LENGTH */
128 return res;
129}
130
131
132/* Hash table of strings in the cache. */
133
134static unsigned long
135str_hash_1 (const void *key)
136{
137 return_ISTRING_HASH_1 ((const char *) key);
138}
139
140static unsigned long
141str_hash_2 (const void *key)
142{
143 return_ISTRING_HASH_2 ((const char *) key);
144}
145
146static int
147str_hash_cmp (const void *x, const void *y)
148{
149 return_ISTRING_COMPARE ((const char *) x, (const char *) y);
150}
151
152static struct hash_table strings;
153static unsigned long total_adds = 0;
154
155static const char *
156add_hash (const char *str, int len)
157{
158 /* Look up the string in the hash. If it's there, return it. */
159 char *const *slot = (char *const *) hash_find_slot (&strings, str);
160 const char *key = *slot;
161
162 /* Count the total number of adds we performed. */
163 ++total_adds;
164
165 if (!HASH_VACANT (key))
166 return key;
167
168 /* Not there yet so add it to a buffer, then into the hash table. */
169 key = add_string (str, len);
170 hash_insert_at (&strings, key, slot);
171 return key;
172}
173
174/* Returns true if the string is in the cache; false if not. */
175int
176strcache_iscached (const char *str)
177{
178 struct strcache *sp;
179
180 for (sp = strcache; sp != 0; sp = sp->next)
181 if (str >= sp->buffer && str < sp->end)
182 return 1;
183
184 return 0;
185}
186
187/* If the string is already in the cache, return a pointer to the cached
188 version. If not, add it then return a pointer to the cached version.
189 Note we do NOT take control of the string passed in. */
190const char *
191strcache_add (const char *str)
192{
193 return add_hash (str, strlen (str));
194}
195
196const char *
197strcache_add_len (const char *str, int len)
198{
199 /* If we're not given a nul-terminated string we have to create one, because
200 the hashing functions expect it. */
201 if (str[len] != '\0')
202 {
203 char *key = alloca (len + 1);
204 memcpy (key, str, len);
205 key[len] = '\0';
206 str = key;
207 }
208
209 return add_hash (str, len);
210}
211
212int
213strcache_setbufsize(int size)
214{
215 if (size > bufsize)
216 bufsize = size;
217 return bufsize;
218}
219
220void
221strcache_init (void)
222{
223#ifdef KMK
224 hash_init (&strings, 65535, str_hash_1, str_hash_2, str_hash_cmp);
225#else
226 hash_init (&strings, 8000, str_hash_1, str_hash_2, str_hash_cmp);
227#endif
228}
229
230
231/* Generate some stats output. */
232
233void
234strcache_print_stats (const char *prefix)
235{
236 int numbuffs = 0, numstrs = 0;
237 int totsize = 0, avgsize, maxsize = 0, minsize = bufsize;
238 int totfree = 0, avgfree, maxfree = 0, minfree = bufsize;
239 int lastused = 0, lastfree = 0;
240
241 if (strcache)
242 {
243 const struct strcache *sp;
244
245 /* Count the first buffer separately since it's not full. */
246 lastused = strcache->end - strcache->buffer;
247 lastfree = strcache->bytesfree;
248
249 for (sp = strcache->next; sp != NULL; sp = sp->next)
250 {
251 int bf = sp->bytesfree;
252 int sz = sp->end - sp->buffer;
253
254 ++numbuffs;
255 numstrs += sp->count;
256
257 totsize += sz;
258 maxsize = (sz > maxsize ? sz : maxsize);
259 minsize = (sz < minsize ? sz : minsize);
260
261 totfree += bf;
262 maxfree = (bf > maxfree ? bf : maxfree);
263 minfree = (bf < minfree ? bf : minfree);
264 }
265 }
266
267 avgsize = numbuffs ? (int)(totsize / numbuffs) : 0;
268 avgfree = numbuffs ? (int)(totfree / numbuffs) : 0;
269
270 printf (_("\n%s # of strings in strcache: %d / lookups = %lu / hits = %lu\n"),
271 prefix, numstrs, total_adds, (total_adds - numstrs));
272 printf (_("%s # of strcache buffers: %d (* %d B/buffer = %d B)\n"),
273 prefix, (numbuffs + 1), bufsize, ((numbuffs + 1) * bufsize));
274 printf (_("%s strcache used: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
275 prefix, totsize, lastused, maxsize, minsize, avgsize);
276 printf (_("%s strcache free: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
277 prefix, totfree, lastfree, maxfree, minfree, avgfree);
278
279 fputs (_("\n# strcache hash-table stats:\n# "), stdout);
280 hash_print_stats (&strings, stdout);
281}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette