]>
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> | |
f8ba827c | 16 | #define _GNU_SOURCE |
855548e6 | 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 | ||
84 | void finisstring() { | |
84d0b6e7 P |
85 | deregisterhook(HOOK_CORE_STATSREQUEST,&sstringstats); |
86 | ||
87 | #ifndef SSTRING_MMAP | |
855548e6 | 88 | nsfreeall(POOL_SSTRING); |
84d0b6e7 | 89 | #endif /* SSTRING_MMAP */ |
855548e6 | 90 | } |
91 | ||
84d0b6e7 | 92 | #ifndef SSTRING_MMAP |
48866b49 CP |
93 | static void salloc(void) { |
94 | ssmem=(char *)nsmalloc(POOL_SSTRING, SSTRING_ALLOC); | |
95 | ssmemfree=SSTRING_ALLOC; | |
96 | } | |
66297844 | 97 | #endif /* SSTRING_MMAP */ |
48866b49 | 98 | |
855548e6 | 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) | |
c5ec4806 | 104 | if (!strcmp(str, sstring_content(ss))) |
855548e6 | 105 | return ss; |
106 | ||
107 | return NULL; | |
108 | } | |
109 | ||
110 | void sstring_enhash(sstring *ss) { | |
c5ec4806 | 111 | unsigned int hash=crc32(sstring_content(ss))%SSTRING_HASHSIZE; |
855548e6 | 112 | |
113 | ss->next=sshash[hash]; | |
114 | sshash[hash]=ss; | |
115 | } | |
116 | ||
117 | void sstring_dehash(sstring *ss) { | |
c5ec4806 | 118 | unsigned int hash=crc32(sstring_content(ss))%SSTRING_HASHSIZE; |
37a40440 | 119 | sstring *ssh, *ssp; |
855548e6 | 120 | |
37a40440 CP |
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 { | |
66297844 | 126 | #ifndef SSTRING_MMAP |
37a40440 CP |
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 | } | |
37a40440 | 136 | #endif |
7901b05a | 137 | } |
855548e6 | 138 | return; |
139 | } | |
140 | } | |
141 | ||
c5ec4806 | 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)); |
855548e6 | 143 | } |
144 | ||
145 | sstring *getsstring(const char *inputstr, int maxlen) { | |
146 | int i; | |
147 | sstring *retval=NULL; | |
48866b49 | 148 | int length, foreignblock; |
855548e6 | 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))) { | |
48866b49 CP |
180 | sunprotect(retval) { |
181 | retval->refcount++; | |
182 | } sprotect(retval); | |
183 | ||
855548e6 | 184 | return retval; |
185 | } | |
48866b49 CP |
186 | |
187 | foreignblock=0; | |
855548e6 | 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; | |
48866b49 CP |
197 | sunprotect(retval); |
198 | foreignblock=1; | |
199 | ||
855548e6 | 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; | |
37a40440 | 212 | sunprotectb(mblock); |
66297844 | 213 | #ifdef SSTRING_MMAP |
37a40440 | 214 | retval->block=mblock; |
7901b05a | 215 | #endif |
855548e6 | 216 | retval->alloc=(ssmemfree-sizeof(sstring)); |
217 | retval->refcount=0; | |
218 | freesstring(retval); | |
219 | } | |
220 | ||
48866b49 CP |
221 | allocs++; |
222 | salloc(); | |
223 | } else { | |
224 | sunprotectb(mblock); | |
855548e6 | 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); | |
c5ec4806 | 247 | strcpy(sstring_content(retval),strbuf); |
855548e6 | 248 | retval->refcount=1; |
c5ec4806 | 249 | |
66297844 | 250 | #ifdef SSTRING_MMAP |
48866b49 CP |
251 | if(!foreignblock) |
252 | retval->block = mblock; | |
253 | #endif | |
254 | ||
855548e6 | 255 | sstring_enhash(retval); |
c5ec4806 CP |
256 | |
257 | #ifdef SSTRING_COMPAT | |
258 | retval->content = retval->__content; | |
259 | #endif | |
260 | ||
48866b49 CP |
261 | sprotect(retval); |
262 | ||
855548e6 | 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 | ||
37a40440 CP |
276 | if (inval->refcount) |
277 | sunprotect(inval); | |
278 | ||
855548e6 | 279 | if (inval->refcount > 1) { |
37a40440 CP |
280 | inval->refcount--; |
281 | sprotect(inval); | |
855548e6 | 282 | return; |
283 | } | |
284 | ||
48866b49 | 285 | /* If refcount==0 it wasn't hashed, or protected */ |
37a40440 | 286 | if (inval->refcount) |
855548e6 | 287 | sstring_dehash(inval); |
48866b49 | 288 | |
855548e6 | 289 | alloc=inval->alloc; |
290 | assert(alloc<=SSTRING_MAXLEN); | |
291 | inval->next=freelist[alloc]; | |
292 | freelist[alloc]=inval; | |
37a40440 | 293 | sprotect(inval); |
855548e6 | 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 | ||
66297844 | 313 | #ifdef SSTRING_MMAP |
48866b49 CP |
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 | } | |
66297844 | 334 | #endif /* SSTRING_MMAP */ |