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