Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / dbuf.c
1 /*
2  * IRC - Internet Relay Chat, common/dbuf.c
3  * Copyright (C) 1990 Markku Savela
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20  */
21 #include "config.h"
22
23 #include "dbuf.h"
24 #include "ircd_alloc.h"
25 #include "ircd_chattr.h"
26 #include "ircd_features.h"
27 #include "send.h"
28 #include "sys.h"       /* MIN */
29
30 #include <assert.h>
31 #include <string.h>
32
33 /*
34  * dbuf is a collection of functions which can be used to
35  * maintain a dynamic buffering of a byte stream.
36  * Functions allocate and release memory dynamically as
37  * required [Actually, there is nothing that prevents
38  * this package maintaining the buffer on disk, either]
39  */
40
41 int DBufAllocCount = 0;
42 int DBufUsedCount = 0;
43
44 static struct DBufBuffer *dbufFreeList = 0;
45
46 #define DBUF_SIZE 2048
47
48 struct DBufBuffer {
49   struct DBufBuffer *next;      /* Next data buffer, NULL if last */
50   char *start;                  /* data starts here */
51   char *end;                    /* data ends here */
52   char data[DBUF_SIZE];         /* Actual data stored here */
53 };
54
55 void dbuf_count_memory(size_t *allocated, size_t *used)
56 {
57   assert(0 != allocated);
58   assert(0 != used);
59   *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
60   *used = DBufUsedCount * sizeof(struct DBufBuffer);
61 }
62
63 /*
64  * dbuf_alloc - allocates a DBufBuffer structure from the free list or
65  * creates a new one.
66  */
67 static struct DBufBuffer *dbuf_alloc(void)
68 {
69   struct DBufBuffer* db = dbufFreeList;
70
71   if (db) {
72     dbufFreeList = db->next;
73     ++DBufUsedCount;
74   }
75   else if (DBufAllocCount * DBUF_SIZE < feature_int(FEAT_BUFFERPOOL)) {
76     db = (struct DBufBuffer*) MyMalloc(sizeof(struct DBufBuffer));
77     assert(0 != db);
78     ++DBufAllocCount;
79     ++DBufUsedCount;
80   }
81   return db;
82 }
83
84 /*
85  * dbuf_free - return a struct DBufBuffer structure to the freelist
86  */
87 static void dbuf_free(struct DBufBuffer *db)
88 {
89   assert(0 != db);
90   --DBufUsedCount;
91   db->next = dbufFreeList;
92   dbufFreeList = db;
93 }
94
95 /*
96  * This is called when malloc fails. Scrap the whole content
97  * of dynamic buffer. (malloc errors are FATAL, there is no
98  * reason to continue this buffer...).
99  * After this the "dbuf" has consistent EMPTY status.
100  */
101 static int dbuf_malloc_error(struct DBuf *dyn)
102 {
103   struct DBufBuffer *db;
104   struct DBufBuffer *next;
105
106   for (db = dyn->head; db; db = next)
107   {
108     next = db->next;
109     dbuf_free(db);
110   }
111   dyn->tail = dyn->head = 0;
112   dyn->length = 0;
113   return 0;
114 }
115
116 /*
117  * dbuf_put - Append the number of bytes to the buffer, allocating memory 
118  * as needed. Bytes are copied into internal buffers from users buffer.
119  *
120  * Returns > 0, if operation successful
121  *         < 0, if failed (due memory allocation problem)
122  *
123  * dyn:         Dynamic buffer header
124  * buf:         Pointer to data to be stored
125  * length:      Number of bytes to store
126  */
127 int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
128 {
129   struct DBufBuffer** h;
130   struct DBufBuffer*  db;
131   unsigned int chunk;
132
133   assert(0 != dyn);
134   assert(0 != buf);
135   /*
136    * Locate the last non-empty buffer. If the last buffer is full,
137    * the loop will terminate with 'db==NULL'.
138    * This loop assumes that the 'dyn->length' field is correctly
139    * maintained, as it should--no other check really needed.
140    */
141   if (!dyn->length)
142     h = &(dyn->head);
143   else
144     h = &(dyn->tail);
145   /*
146    * Append users data to buffer, allocating buffers as needed
147    */
148   dyn->length += length;
149
150   for (; length > 0; h = &(db->next)) {
151     if (0 == (db = *h)) {
152       if (0 == (db = dbuf_alloc())) {
153         if (feature_bool(FEAT_HAS_FERGUSON_FLUSHER)) {
154           /*
155            * from "Married With Children" episode were Al bought a REAL toilet
156            * on the black market because he was tired of the wimpy water
157            * conserving toilets they make these days --Bleep
158            */
159           /*
160            * Apparently this doesn't work, the server _has_ to
161            * dump a few clients to handle the load. A fully loaded
162            * server cannot handle a net break without dumping some
163            * clients. If we flush the connections here under a full
164            * load we may end up starving the kernel for mbufs and
165            * crash the machine
166            */
167           /*
168            * attempt to recover from buffer starvation before
169            * bailing this may help servers running out of memory
170            */
171           flush_connections(0);
172           db = dbuf_alloc();
173         }
174
175         if (0 == db)
176           return dbuf_malloc_error(dyn);
177       }
178       dyn->tail = db;
179       *h = db;
180       db->next = 0;
181       db->start = db->end = db->data;
182     }
183     chunk = (db->data + DBUF_SIZE) - db->end;
184     if (chunk) {
185       if (chunk > length)
186         chunk = length;
187
188       memcpy(db->end, buf, chunk);
189
190       length -= chunk;
191       buf += chunk;
192       db->end += chunk;
193     }
194   }
195   return 1;
196 }
197
198 /*
199  * dbuf_map, dbuf_delete
200  *
201  * These functions are meant to be used in pairs and offer a more efficient
202  * way of emptying the buffer than the normal 'dbuf_get' would allow--less
203  * copying needed.
204  *
205  *    map     returns a pointer to a largest contiguous section
206  *            of bytes in front of the buffer, the length of the
207  *            section is placed into the indicated "long int"
208  *            variable. Returns NULL *and* zero length, if the
209  *            buffer is empty.
210  *
211  *    delete  removes the specified number of bytes from the
212  *            front of the buffer releasing any memory used for them.
213  *
214  *    Example use (ignoring empty condition here ;)
215  *
216  *            buf = dbuf_map(&dyn, &count);
217  *            <process N bytes (N <= count) of data pointed by 'buf'>
218  *            dbuf_delete(&dyn, N);
219  *
220  *    Note:   delete can be used alone, there is no real binding
221  *            between map and delete functions...
222  *
223  * dyn:         Dynamic buffer header
224  * length:      Return number of bytes accessible
225  */
226 const char *dbuf_map(const struct DBuf* dyn, unsigned int* length)
227 {
228   assert(0 != dyn);
229   assert(0 != length);
230
231   if (0 == dyn->length)
232   {
233     *length = 0;
234     return 0;
235   }
236   assert(0 != dyn->head);
237
238   *length = dyn->head->end - dyn->head->start;
239   return dyn->head->start;
240 }
241
242 /*
243  * dbuf_delete - delete length bytes from DBuf
244  *
245  * dyn:         Dynamic buffer header
246  * length:      Number of bytes to delete
247  */
248 void dbuf_delete(struct DBuf *dyn, unsigned int length)
249 {
250   struct DBufBuffer *db;
251   unsigned int chunk;
252
253   if (length > dyn->length)
254     length = dyn->length;
255
256   while (length > 0)
257   {
258     if (0 == (db = dyn->head))
259       break;
260     chunk = db->end - db->start;
261     if (chunk > length)
262       chunk = length;
263
264     length -= chunk;
265     dyn->length -= chunk;
266     db->start += chunk;
267
268     if (db->start == db->end)
269     {
270       dyn->head = db->next;
271       dbuf_free(db);
272     }
273   }
274   if (0 == dyn->head)
275   {
276     dyn->length = 0;
277     dyn->tail = 0;
278   }
279 }
280
281 /*
282  * dbuf_get
283  *
284  * Remove number of bytes from the buffer, releasing dynamic memory,
285  * if applicaple. Bytes are copied from internal buffers to users buffer.
286  *
287  * Returns the number of bytes actually copied to users buffer,
288  * if >= 0, any value less than the size of the users
289  * buffer indicates the dbuf became empty by this operation.
290  *
291  * Return 0 indicates that buffer was already empty.
292  *
293  * dyn:         Dynamic buffer header
294  * buf:         Pointer to buffer to receive the data
295  * length:      Max amount of bytes that can be received
296  */
297 unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
298 {
299   unsigned int moved = 0;
300   unsigned int chunk;
301   const char *b;
302
303   assert(0 != dyn);
304   assert(0 != buf);
305
306   while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
307   {
308     if (chunk > length)
309       chunk = length;
310
311     memcpy(buf, b, chunk);
312     dbuf_delete(dyn, chunk);
313
314     buf += chunk;
315     length -= chunk;
316     moved += chunk;
317   }
318   return moved;
319 }
320
321 static unsigned int dbuf_flush(struct DBuf *dyn)
322 {
323   struct DBufBuffer *db = dyn->head;
324
325   if (0 == db)
326     return 0;
327
328   assert(db->start < db->end);
329   /*
330    * flush extra line terms
331    */
332   while (IsEol(*db->start))
333   {
334     if (++db->start == db->end)
335     {
336       dyn->head = db->next;
337       dbuf_free(db);
338       if (0 == (db = dyn->head))
339       {
340         dyn->tail = 0;
341         dyn->length = 0;
342         break;
343       }
344     }
345     --dyn->length;
346   }
347   return dyn->length;
348 }
349
350
351 /*
352  * dbuf_getmsg - Check the buffers to see if there is a string which is
353  * terminated with either a \r or \n present.  If so, copy as much as 
354  * possible (determined by length) into buf and return the amount copied 
355  * else return 0.
356  */
357 unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
358 {
359   struct DBufBuffer *db;
360   char *start;
361   char *end;
362   unsigned int count;
363   unsigned int copied = 0;
364
365   assert(0 != dyn);
366   assert(0 != buf);
367
368   if (0 == dbuf_flush(dyn))
369     return 0;
370
371   assert(0 != dyn->head);
372
373   db = dyn->head;
374   start = db->start;
375
376   assert(start < db->end);
377
378   if (length > dyn->length)
379     length = dyn->length;
380   /*
381    * might as well copy it while we're here
382    */
383   while (length > 0)
384   {
385     end = IRCD_MIN(db->end, (start + length));
386     while (start < end && !IsEol(*start))
387       *buf++ = *start++;
388
389     count = start - db->start;
390     if (start < end)
391     {
392       *buf = '\0';
393       copied += count;
394       dbuf_delete(dyn, copied);
395       dbuf_flush(dyn);
396       return copied;
397     }
398     if (0 == (db = db->next))
399       break;
400     copied += count;
401     length -= count;
402     start = db->start;
403   }
404   return 0;
405 }