]>
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 | } | |
131 | } | |
132 | #endif | |
855548e6 | 133 | return; |
134 | } | |
135 | } | |
136 | ||
137 | Error("sstring",ERR_WARNING,"sstring_dehash(): Unable to find string (ref=%d, length=%d) in hash: %s",ss->refcount,ss->length,ss->content); | |
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 CP |
207 | sunprotectb(mblock); |
208 | retval->block=mblock; | |
855548e6 | 209 | retval->alloc=(ssmemfree-sizeof(sstring)); |
210 | retval->refcount=0; | |
211 | freesstring(retval); | |
212 | } | |
213 | ||
48866b49 CP |
214 | allocs++; |
215 | salloc(); | |
216 | } else { | |
217 | sunprotectb(mblock); | |
855548e6 | 218 | } |
219 | ||
220 | retval=(sstring *)ssmem; | |
221 | ssmem+=(sizeof(sstring)+length); | |
222 | ssmemfree-=(sizeof(sstring)+length); | |
223 | ||
224 | retval->alloc=length; | |
225 | ||
226 | /* If there's a fragment left over, lump it into this allocation */ | |
227 | if (ssmemfree < (sizeof(sstring)+1)) { | |
228 | retval->alloc += ssmemfree; | |
229 | ssmemfree=0; | |
230 | ssmem=NULL; | |
231 | } | |
232 | } | |
233 | ||
234 | /* | |
235 | * At this point, retval points to a valid structure which is at | |
236 | * least the right size and has the "alloc" value set correctly | |
237 | */ | |
238 | ||
239 | retval->length=(length-1); | |
240 | strcpy(retval->content,strbuf); | |
241 | retval->refcount=1; | |
242 | ||
48866b49 CP |
243 | #ifdef USE_VALGRIND |
244 | if(!foreignblock) | |
245 | retval->block = mblock; | |
246 | #endif | |
247 | ||
855548e6 | 248 | sstring_enhash(retval); |
48866b49 CP |
249 | sprotect(retval); |
250 | ||
855548e6 | 251 | return retval; |
252 | } | |
253 | ||
254 | void freesstring(sstring *inval) { | |
255 | int alloc; | |
256 | ||
257 | /* Allow people to call this with a NULL value */ | |
258 | if (inval==NULL) | |
259 | return; | |
260 | ||
261 | /* Only count calls that actually did something */ | |
262 | freecalls++; | |
263 | ||
37a40440 CP |
264 | if (inval->refcount) |
265 | sunprotect(inval); | |
266 | ||
855548e6 | 267 | if (inval->refcount > 1) { |
37a40440 CP |
268 | inval->refcount--; |
269 | sprotect(inval); | |
855548e6 | 270 | return; |
271 | } | |
272 | ||
48866b49 | 273 | /* If refcount==0 it wasn't hashed, or protected */ |
37a40440 | 274 | if (inval->refcount) |
855548e6 | 275 | sstring_dehash(inval); |
48866b49 | 276 | |
855548e6 | 277 | alloc=inval->alloc; |
278 | assert(alloc<=SSTRING_MAXLEN); | |
279 | inval->next=freelist[alloc]; | |
280 | freelist[alloc]=inval; | |
37a40440 | 281 | sprotect(inval); |
855548e6 | 282 | } |
283 | ||
284 | void sstringstats(int hooknum, void *arg) { | |
285 | char buf[512]; | |
286 | int i,j; | |
287 | sstring *ssp; | |
288 | long statslev=(long)arg; | |
289 | ||
290 | if (statslev>10) { | |
291 | for(i=0,j=0;i<=SSTRING_MAXLEN;i++) { | |
292 | for(ssp=freelist[i];ssp;ssp=ssp->next) | |
293 | j++; | |
294 | } | |
295 | ||
296 | snprintf(buf,512,"SString : %7d get calls, %7d free calls, %7d allocs, %7d strings free",getcalls,freecalls,allocs,j); | |
297 | triggerhook(HOOK_CORE_STATSREPLY,(void *)buf); | |
298 | } | |
299 | } | |
300 | ||
301 | int sstringcompare(sstring *ss1, sstring *ss2) { | |
302 | if (ss1->length != ss2->length) | |
303 | return -1; | |
304 | ||
305 | return strncmp(ss1->content, ss2->content, ss1->length); | |
306 | } | |
307 | ||
48866b49 CP |
308 | #ifdef USE_VALGRIND |
309 | void finisstring() { | |
310 | struct mblock_list *c, *n; | |
311 | for (c=mblock_head;c;c=n) { | |
312 | n=c->next; | |
313 | munmap(c->block, SSTRING_ALLOC); | |
314 | } | |
315 | } | |
316 | ||
317 | static void salloc(void) { | |
318 | struct mblock_list *n; | |
319 | mblock=mmap((void *)0, SSTRING_ALLOC, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0); | |
320 | ||
321 | n=(struct mblock_list *)mblock; | |
322 | n->block = mblock; | |
323 | n->next = mblock_head; | |
324 | mblock_head = n; | |
325 | ||
326 | ssmem=(char *)mblock + sizeof(struct mblock_list); | |
327 | ssmemfree=SSTRING_ALLOC-sizeof(struct mblock_list); | |
328 | } | |
329 | #endif /* USE_VALGRIND */ |