]>
Commit | Line | Data |
---|---|---|
855548e6 | 1 | /* sstring.h - Declaration of "static strings" functions */ |
2 | ||
3 | #define COMPILING_SSTRING | |
4 | #include "sstring.h" | |
5 | ||
6 | #include "../core/hooks.h" | |
7 | #include "../core/nsmalloc.h" | |
8 | #include "../core/error.h" | |
9 | #include "../lib/irc_string.h" | |
10 | ||
11 | #include <stdio.h> | |
12 | ||
13 | #include <assert.h> | |
14 | #include <stdlib.h> | |
15 | #define __USE_GNU | |
16 | #include <string.h> | |
17 | ||
18 | /* List of free stuff */ | |
19 | static sstring *freelist[SSTRING_MAXLEN+1]; | |
20 | static sstring *sshash[SSTRING_HASHSIZE]; | |
21 | ||
22 | /* Global variables to track allocated memory */ | |
23 | static char *ssmem; | |
24 | static int ssmemfree; | |
25 | ||
26 | /* Statistics counters */ | |
27 | static int getcalls; | |
28 | static int freecalls; | |
29 | static int allocs; | |
30 | ||
48866b49 | 31 | /* Internal functions */ |
855548e6 | 32 | static void sstringstats(int hooknum, void *arg); |
48866b49 CP |
33 | static void salloc(void); |
34 | ||
35 | #ifndef USE_VALGRIND | |
36 | ||
37 | #define sunprotect(x) | |
38 | #define sunprotectb(x) | |
39 | #define sprotect(x) | |
40 | ||
41 | #else | |
42 | ||
b91d4f9e CP |
43 | #define __USE_MISC |
44 | ||
48866b49 CP |
45 | #include <sys/mman.h> |
46 | static void *mblock; | |
47 | struct mblock_list { | |
48 | void *block; | |
49 | struct mblock_list *next; | |
50 | }; | |
51 | ||
52 | static void *mblock_head; | |
53 | ||
54 | #define sunprotectb(x) mprotect(x, SSTRING_ALLOC, PROT_READ|PROT_WRITE); | |
37a40440 CP |
55 | #define sunprotect(x) sunprotectb((x)->block); |
56 | #define sprotect(x) mprotect((x)->block, SSTRING_ALLOC, PROT_READ); | |
48866b49 | 57 | |
b91d4f9e CP |
58 | #ifndef MAP_ANON |
59 | #define MAP_ANON MAP_ANONYMOUS | |
60 | #endif | |
61 | ||
48866b49 | 62 | #endif /* USE_VALGRIND */ |
855548e6 | 63 | |
64 | void initsstring() { | |
65 | int i; | |
66 | for(i=0;i<=SSTRING_MAXLEN;i++) | |
67 | freelist[i]=NULL; | |
68 | ||
69 | for(i=0;i<SSTRING_HASHSIZE;i++) | |
70 | sshash[i]=NULL; | |
71 | ||
72 | ssmemfree=0; | |
73 | ssmem=NULL; | |
74 | ||
75 | /* Initialise statistics counters */ | |
76 | getcalls=0; | |
77 | freecalls=0; | |
78 | allocs=0; | |
79 | ||
80 | registerhook(HOOK_CORE_STATSREQUEST,&sstringstats); | |
81 | } | |
82 | ||
48866b49 | 83 | #ifndef USE_VALGRIND |
855548e6 | 84 | void finisstring() { |
85 | nsfreeall(POOL_SSTRING); | |
86 | } | |
87 | ||
48866b49 CP |
88 | static void salloc(void) { |
89 | ssmem=(char *)nsmalloc(POOL_SSTRING, SSTRING_ALLOC); | |
90 | ssmemfree=SSTRING_ALLOC; | |
91 | } | |
92 | #endif /* USE_VALGRIND */ | |
93 | ||
855548e6 | 94 | sstring *findsstring(const char *str) { |
95 | unsigned int hash=crc32(str)%SSTRING_HASHSIZE; | |
96 | sstring *ss; | |
97 | ||
98 | for (ss=sshash[hash];ss;ss=ss->next) | |
99 | if (!strcmp(str, ss->content)) | |
100 | return ss; | |
101 | ||
102 | return NULL; | |
103 | } | |
104 | ||
105 | void sstring_enhash(sstring *ss) { | |
106 | unsigned int hash=crc32(ss->content)%SSTRING_HASHSIZE; | |
107 | ||
108 | ss->next=sshash[hash]; | |
109 | sshash[hash]=ss; | |
110 | } | |
111 | ||
112 | void sstring_dehash(sstring *ss) { | |
113 | unsigned int hash=crc32(ss->content)%SSTRING_HASHSIZE; | |
37a40440 | 114 | sstring *ssh, *ssp; |
855548e6 | 115 | |
37a40440 CP |
116 | for (ssh=sshash[hash],ssp=NULL; ssh; ssp=ssh,ssh=ssh->next) { |
117 | if (ssh==ss) { | |
118 | if (!ssp) { | |
119 | sshash[hash]=ss->next; | |
120 | } else { | |
121 | #ifndef USE_VALGRIND | |
122 | ssp->next=ss->next; | |
123 | #else | |
124 | if (ssp->block!=ss->block) { | |
125 | sunprotect(ssp) { | |
126 | ssp->next=ss->next; | |
127 | } sprotect(ssp); | |
128 | } else { | |
129 | ssp->next=ss->next; | |
130 | } | |
37a40440 | 131 | #endif |
7901b05a | 132 | } |
855548e6 | 133 | return; |
134 | } | |
135 | } | |
136 | ||
7901b05a | 137 | Error("sstring",ERR_WARNING,"sstring_dehash(): Unable to find string (ref=%lu, length=%d) in hash: %s",ss->refcount,ss->length,ss->content); |
855548e6 | 138 | } |
139 | ||
140 | sstring *getsstring(const char *inputstr, int maxlen) { | |
141 | int i; | |
142 | sstring *retval=NULL; | |
48866b49 | 143 | int length, foreignblock; |
855548e6 | 144 | char strbuf[SSTRING_MAXLEN]; |
145 | ||
146 | /* getsstring() on a NULL pointer returns a NULL sstring.. */ | |
147 | if (inputstr==NULL) { | |
148 | return NULL; | |
149 | } | |
150 | ||
151 | if (inputstr[0]=='\0') { | |
152 | return NULL; | |
153 | } | |
154 | ||
155 | if (maxlen>SSTRING_MAX) { | |
156 | Error("sstring", ERR_ERROR, "Attempt to allocate overlength string (maxlen=%d)",maxlen); | |
157 | return NULL; | |
158 | } | |
159 | ||
160 | /* Only count calls that actually did something */ | |
161 | getcalls++; | |
162 | ||
163 | /* Make a copy of the desired string to make things easier later */ | |
164 | for (length=0;length<maxlen;length++) { | |
165 | strbuf[length]=inputstr[length]; | |
166 | if (!strbuf[length]) | |
167 | break; | |
168 | } | |
169 | strbuf[length]='\0'; | |
170 | ||
171 | length++; | |
172 | ||
173 | /* If it's hashed this is easy */ | |
174 | if ((retval=findsstring(strbuf))) { | |
48866b49 CP |
175 | sunprotect(retval) { |
176 | retval->refcount++; | |
177 | } sprotect(retval); | |
178 | ||
855548e6 | 179 | return retval; |
180 | } | |
48866b49 CP |
181 | |
182 | foreignblock=0; | |
855548e6 | 183 | /* Check to see if an approximately correct |
184 | * sized string is available */ | |
185 | for(i=0;i<SSTRING_SLACK;i++) { | |
186 | if (length+i>SSTRING_MAXLEN) | |
187 | break; | |
188 | ||
189 | if (freelist[length+i]!=NULL) { | |
190 | retval=freelist[length+i]; | |
191 | freelist[length+i]=retval->next; | |
48866b49 CP |
192 | sunprotect(retval); |
193 | foreignblock=1; | |
194 | ||
855548e6 | 195 | retval->alloc=(length+i); |
196 | break; | |
197 | } | |
198 | } | |
199 | ||
200 | /* None found, allocate a new one */ | |
201 | if (retval==NULL) { | |
202 | /* Check for free memory */ | |
203 | if (ssmemfree < (sizeof(sstring)+length)) { | |
204 | /* Not enough for us - turn the remaining memory into a free string for later */ | |
205 | if (ssmemfree>sizeof(sstring)) { | |
206 | retval=(sstring *)ssmem; | |
37a40440 | 207 | sunprotectb(mblock); |
7901b05a | 208 | #ifdef USE_VALGRIND |
37a40440 | 209 | retval->block=mblock; |
7901b05a | 210 | #endif |
855548e6 | 211 | retval->alloc=(ssmemfree-sizeof(sstring)); |
212 | retval->refcount=0; | |
213 | freesstring(retval); | |
214 | } | |
215 | ||
48866b49 CP |
216 | allocs++; |
217 | salloc(); | |
218 | } else { | |
219 | sunprotectb(mblock); | |
855548e6 | 220 | } |
221 | ||
222 | retval=(sstring *)ssmem; | |
223 | ssmem+=(sizeof(sstring)+length); | |
224 | ssmemfree-=(sizeof(sstring)+length); | |
225 | ||
226 | retval->alloc=length; | |
227 | ||
228 | /* If there's a fragment left over, lump it into this allocation */ | |
229 | if (ssmemfree < (sizeof(sstring)+1)) { | |
230 | retval->alloc += ssmemfree; | |
231 | ssmemfree=0; | |
232 | ssmem=NULL; | |
233 | } | |
234 | } | |
235 | ||
236 | /* | |
237 | * At this point, retval points to a valid structure which is at | |
238 | * least the right size and has the "alloc" value set correctly | |
239 | */ | |
240 | ||
241 | retval->length=(length-1); | |
242 | strcpy(retval->content,strbuf); | |
243 | retval->refcount=1; | |
244 | ||
48866b49 CP |
245 | #ifdef USE_VALGRIND |
246 | if(!foreignblock) | |
247 | retval->block = mblock; | |
248 | #endif | |
249 | ||
855548e6 | 250 | sstring_enhash(retval); |
48866b49 CP |
251 | sprotect(retval); |
252 | ||
855548e6 | 253 | return retval; |
254 | } | |
255 | ||
256 | void freesstring(sstring *inval) { | |
257 | int alloc; | |
258 | ||
259 | /* Allow people to call this with a NULL value */ | |
260 | if (inval==NULL) | |
261 | return; | |
262 | ||
263 | /* Only count calls that actually did something */ | |
264 | freecalls++; | |
265 | ||
37a40440 CP |
266 | if (inval->refcount) |
267 | sunprotect(inval); | |
268 | ||
855548e6 | 269 | if (inval->refcount > 1) { |
37a40440 CP |
270 | inval->refcount--; |
271 | sprotect(inval); | |
855548e6 | 272 | return; |
273 | } | |
274 | ||
48866b49 | 275 | /* If refcount==0 it wasn't hashed, or protected */ |
37a40440 | 276 | if (inval->refcount) |
855548e6 | 277 | sstring_dehash(inval); |
48866b49 | 278 | |
855548e6 | 279 | alloc=inval->alloc; |
280 | assert(alloc<=SSTRING_MAXLEN); | |
281 | inval->next=freelist[alloc]; | |
282 | freelist[alloc]=inval; | |
37a40440 | 283 | sprotect(inval); |
855548e6 | 284 | } |
285 | ||
286 | void sstringstats(int hooknum, void *arg) { | |
287 | char buf[512]; | |
288 | int i,j; | |
289 | sstring *ssp; | |
290 | long statslev=(long)arg; | |
291 | ||
292 | if (statslev>10) { | |
293 | for(i=0,j=0;i<=SSTRING_MAXLEN;i++) { | |
294 | for(ssp=freelist[i];ssp;ssp=ssp->next) | |
295 | j++; | |
296 | } | |
297 | ||
298 | snprintf(buf,512,"SString : %7d get calls, %7d free calls, %7d allocs, %7d strings free",getcalls,freecalls,allocs,j); | |
299 | triggerhook(HOOK_CORE_STATSREPLY,(void *)buf); | |
300 | } | |
301 | } | |
302 | ||
303 | int sstringcompare(sstring *ss1, sstring *ss2) { | |
304 | if (ss1->length != ss2->length) | |
305 | return -1; | |
306 | ||
307 | return strncmp(ss1->content, ss2->content, ss1->length); | |
308 | } | |
309 | ||
48866b49 CP |
310 | #ifdef USE_VALGRIND |
311 | void finisstring() { | |
312 | struct mblock_list *c, *n; | |
313 | for (c=mblock_head;c;c=n) { | |
314 | n=c->next; | |
315 | munmap(c->block, SSTRING_ALLOC); | |
316 | } | |
317 | } | |
318 | ||
319 | static void salloc(void) { | |
320 | struct mblock_list *n; | |
321 | mblock=mmap((void *)0, SSTRING_ALLOC, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0); | |
322 | ||
323 | n=(struct mblock_list *)mblock; | |
324 | n->block = mblock; | |
325 | n->next = mblock_head; | |
326 | mblock_head = n; | |
327 | ||
328 | ssmem=(char *)mblock + sizeof(struct mblock_list); | |
329 | ssmemfree=SSTRING_ALLOC-sizeof(struct mblock_list); | |
330 | } | |
331 | #endif /* USE_VALGRIND */ |