]> jfr.im git - irc/quakenet/newserv.git/blob - lib/sstring-new.c
afaa9e7c3c1d78dfc016dec26923e2d21cb4bbf3
[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 __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
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 #ifndef SSTRING_MMAP
85 void finisstring() {
86 nsfreeall(POOL_SSTRING);
87 }
88
89 static void salloc(void) {
90 ssmem=(char *)nsmalloc(POOL_SSTRING, SSTRING_ALLOC);
91 ssmemfree=SSTRING_ALLOC;
92 }
93 #endif /* SSTRING_MMAP */
94
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)
100 if (!strcmp(str, sstring_content(ss)))
101 return ss;
102
103 return NULL;
104 }
105
106 void sstring_enhash(sstring *ss) {
107 unsigned int hash=crc32(sstring_content(ss))%SSTRING_HASHSIZE;
108
109 ss->next=sshash[hash];
110 sshash[hash]=ss;
111 }
112
113 void sstring_dehash(sstring *ss) {
114 unsigned int hash=crc32(sstring_content(ss))%SSTRING_HASHSIZE;
115 sstring *ssh, *ssp;
116
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 {
122 #ifndef SSTRING_MMAP
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 }
132 #endif
133 }
134 return;
135 }
136 }
137
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));
139 }
140
141 sstring *getsstring(const char *inputstr, int maxlen) {
142 int i;
143 sstring *retval=NULL;
144 int length, foreignblock;
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))) {
176 sunprotect(retval) {
177 retval->refcount++;
178 } sprotect(retval);
179
180 return retval;
181 }
182
183 foreignblock=0;
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;
193 sunprotect(retval);
194 foreignblock=1;
195
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;
208 sunprotectb(mblock);
209 #ifdef SSTRING_MMAP
210 retval->block=mblock;
211 #endif
212 retval->alloc=(ssmemfree-sizeof(sstring));
213 retval->refcount=0;
214 freesstring(retval);
215 }
216
217 allocs++;
218 salloc();
219 } else {
220 sunprotectb(mblock);
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);
243 strcpy(sstring_content(retval),strbuf);
244 retval->refcount=1;
245
246 #ifdef SSTRING_MMAP
247 if(!foreignblock)
248 retval->block = mblock;
249 #endif
250
251 sstring_enhash(retval);
252
253 #ifdef SSTRING_COMPAT
254 retval->content = retval->__content;
255 #endif
256
257 sprotect(retval);
258
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
272 if (inval->refcount)
273 sunprotect(inval);
274
275 if (inval->refcount > 1) {
276 inval->refcount--;
277 sprotect(inval);
278 return;
279 }
280
281 /* If refcount==0 it wasn't hashed, or protected */
282 if (inval->refcount)
283 sstring_dehash(inval);
284
285 alloc=inval->alloc;
286 assert(alloc<=SSTRING_MAXLEN);
287 inval->next=freelist[alloc];
288 freelist[alloc]=inval;
289 sprotect(inval);
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
309 #ifdef SSTRING_MMAP
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 }
330 #endif /* SSTRING_MMAP */