source: trunk/src/kmk/strcache.c@ 1844

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

one terminator is sufficient.

  • 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 *(tmp++) = '\0';
119
120 best->bytesfree -= len + 1;
121 ++best->count;
122
123 assert (tmp <= best->end);
124 assert (!((size_t)res & (sizeof(void *) - 1)));
125#endif /* CONFIG_WITH_VALUE_LENGTH */
126 return res;
127}
128
129
130/* Hash table of strings in the cache. */
131
132static unsigned long
133str_hash_1 (const void *key)
134{
135 return_ISTRING_HASH_1 ((const char *) key);
136}
137
138static unsigned long
139str_hash_2 (const void *key)
140{
141 return_ISTRING_HASH_2 ((const char *) key);
142}
143
144static int
145str_hash_cmp (const void *x, const void *y)
146{
147 return_ISTRING_COMPARE ((const char *) x, (const char *) y);
148}
149
150static struct hash_table strings;
151static unsigned long total_adds = 0;
152
153static const char *
154add_hash (const char *str, int len)
155{
156 /* Look up the string in the hash. If it's there, return it. */
157 char *const *slot = (char *const *) hash_find_slot (&strings, str);
158 const char *key = *slot;
159
160 /* Count the total number of adds we performed. */
161 ++total_adds;
162
163 if (!HASH_VACANT (key))
164 return key;
165
166 /* Not there yet so add it to a buffer, then into the hash table. */
167 key = add_string (str, len);
168 hash_insert_at (&strings, key, slot);
169 return key;
170}
171
172/* Returns true if the string is in the cache; false if not. */
173int
174strcache_iscached (const char *str)
175{
176 struct strcache *sp;
177
178 for (sp = strcache; sp != 0; sp = sp->next)
179 if (str >= sp->buffer && str < sp->end)
180 return 1;
181
182 return 0;
183}
184
185/* If the string is already in the cache, return a pointer to the cached
186 version. If not, add it then return a pointer to the cached version.
187 Note we do NOT take control of the string passed in. */
188const char *
189strcache_add (const char *str)
190{
191 return add_hash (str, strlen (str));
192}
193
194const char *
195strcache_add_len (const char *str, int len)
196{
197 /* If we're not given a nul-terminated string we have to create one, because
198 the hashing functions expect it. */
199 if (str[len] != '\0')
200 {
201 char *key = alloca (len + 1);
202 memcpy (key, str, len);
203 key[len] = '\0';
204 str = key;
205 }
206
207 return add_hash (str, len);
208}
209
210int
211strcache_setbufsize(int size)
212{
213 if (size > bufsize)
214 bufsize = size;
215 return bufsize;
216}
217
218void
219strcache_init (void)
220{
221#ifdef KMK
222 hash_init (&strings, 65535, str_hash_1, str_hash_2, str_hash_cmp);
223#else
224 hash_init (&strings, 8000, str_hash_1, str_hash_2, str_hash_cmp);
225#endif
226}
227
228
229/* Generate some stats output. */
230
231void
232strcache_print_stats (const char *prefix)
233{
234 int numbuffs = 0, numstrs = 0;
235 int totsize = 0, avgsize, maxsize = 0, minsize = bufsize;
236 int totfree = 0, avgfree, maxfree = 0, minfree = bufsize;
237 int lastused = 0, lastfree = 0;
238
239 if (strcache)
240 {
241 const struct strcache *sp;
242
243 /* Count the first buffer separately since it's not full. */
244 lastused = strcache->end - strcache->buffer;
245 lastfree = strcache->bytesfree;
246
247 for (sp = strcache->next; sp != NULL; sp = sp->next)
248 {
249 int bf = sp->bytesfree;
250 int sz = sp->end - sp->buffer;
251
252 ++numbuffs;
253 numstrs += sp->count;
254
255 totsize += sz;
256 maxsize = (sz > maxsize ? sz : maxsize);
257 minsize = (sz < minsize ? sz : minsize);
258
259 totfree += bf;
260 maxfree = (bf > maxfree ? bf : maxfree);
261 minfree = (bf < minfree ? bf : minfree);
262 }
263 }
264
265 avgsize = numbuffs ? (int)(totsize / numbuffs) : 0;
266 avgfree = numbuffs ? (int)(totfree / numbuffs) : 0;
267
268 printf (_("\n%s # of strings in strcache: %d / lookups = %lu / hits = %lu\n"),
269 prefix, numstrs, total_adds, (total_adds - numstrs));
270 printf (_("%s # of strcache buffers: %d (* %d B/buffer = %d B)\n"),
271 prefix, (numbuffs + 1), bufsize, ((numbuffs + 1) * bufsize));
272 printf (_("%s strcache used: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
273 prefix, totsize, lastused, maxsize, minsize, avgsize);
274 printf (_("%s strcache free: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
275 prefix, totfree, lastfree, maxfree, minfree, avgfree);
276
277 fputs (_("\n# strcache hash-table stats:\n# "), stdout);
278 hash_print_stats (&strings, stdout);
279}
Note: See TracBrowser for help on using the repository browser.