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