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