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