]> jfr.im git - irc/quakenet/newserv.git/blob - lib/sstring-new.c
90581f93af0b93ae2c0cea65d9e5637b26424f8b
[irc/quakenet/newserv.git] / lib / sstring-new.c
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 */