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