2 * IRC - Internet Relay Chat, common/dbuf.c
3 * Copyright (C) 1990 Markku Savela
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)
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.
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.
22 #include "ircd_alloc.h"
23 #include "ircd_chattr.h"
25 #include "sys.h" /* MIN */
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]
38 int DBufAllocCount = 0;
39 int DBufUsedCount = 0;
41 static struct DBufBuffer *dbufFreeList = 0;
43 #define DBUF_SIZE 2048
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 */
52 void dbuf_count_memory(size_t *allocated, size_t *used)
54 assert(0 != allocated);
56 *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
57 *used = DBufUsedCount * sizeof(struct DBufBuffer);
61 * dbuf_alloc - allocates a DBufBuffer structure from the free list or
64 static struct DBufBuffer *dbuf_alloc(void)
66 struct DBufBuffer* db = dbufFreeList;
69 dbufFreeList = db->next;
72 else if (DBufAllocCount * DBUF_SIZE < BUFFERPOOL) {
73 db = (struct DBufBuffer*) MyMalloc(sizeof(struct DBufBuffer));
82 * dbuf_free - return a struct DBufBuffer structure to the freelist
84 static void dbuf_free(struct DBufBuffer *db)
88 db->next = dbufFreeList;
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.
98 static int dbuf_malloc_error(struct DBuf *dyn)
100 struct DBufBuffer *db;
101 struct DBufBuffer *next;
103 for (db = dyn->head; db; db = next)
108 dyn->tail = dyn->head = 0;
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.
117 * Returns > 0, if operation successful
118 * < 0, if failed (due memory allocation problem)
120 * dyn: Dynamic buffer header
121 * buf: Pointer to data to be stored
122 * length: Number of bytes to store
124 int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
126 struct DBufBuffer** h;
127 struct DBufBuffer* db;
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.
143 * Append users data to buffer, allocating buffers as needed
145 dyn->length += length;
147 for (; length > 0; h = &(db->next)) {
148 if (0 == (db = *h)) {
149 if (0 == (db = dbuf_alloc())) {
150 #if defined(HAS_FERGUSON_FLUSHER)
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
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
165 * attempt to recover from buffer starvation before
166 * bailing this may help servers running out of memory
168 flush_sendq_except(dyn);
169 if (0 == (db = dbuf_alloc()))
171 return dbuf_malloc_error(dyn);
176 db->start = db->end = db->data;
178 chunk = (db->data + DBUF_SIZE) - db->end;
183 memcpy(db->end, buf, chunk);
194 * dbuf_map, dbuf_delete
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
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
206 * delete removes the specified number of bytes from the
207 * front of the buffer releasing any memory used for them.
209 * Example use (ignoring empty condition here ;)
211 * buf = dbuf_map(&dyn, &count);
212 * <process N bytes (N <= count) of data pointed by 'buf'>
213 * dbuf_delete(&dyn, N);
215 * Note: delete can be used alone, there is no real binding
216 * between map and delete functions...
218 * dyn: Dynamic buffer header
219 * length: Return number of bytes accessible
221 const char *dbuf_map(const struct DBuf* dyn, unsigned int* length)
226 if (0 == dyn->length)
231 assert(0 != dyn->head);
233 *length = dyn->head->end - dyn->head->start;
234 return dyn->head->start;
238 * dbuf_delete - delete length bytes from DBuf
240 * dyn: Dynamic buffer header
241 * length: Number of bytes to delete
243 void dbuf_delete(struct DBuf *dyn, unsigned int length)
245 struct DBufBuffer *db;
248 if (length > dyn->length)
249 length = dyn->length;
253 if (0 == (db = dyn->head))
255 chunk = db->end - db->start;
260 dyn->length -= chunk;
263 if (db->start == db->end)
265 dyn->head = db->next;
279 * Remove number of bytes from the buffer, releasing dynamic memory,
280 * if applicaple. Bytes are copied from internal buffers to users buffer.
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.
286 * Return 0 indicates that buffer was already empty.
288 * dyn: Dynamic buffer header
289 * buf: Pointer to buffer to receive the data
290 * length: Max amount of bytes that can be received
292 unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
294 unsigned int moved = 0;
301 while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
306 memcpy(buf, b, chunk);
307 dbuf_delete(dyn, chunk);
316 static unsigned int dbuf_flush(struct DBuf *dyn)
318 struct DBufBuffer *db = dyn->head;
323 assert(db->start < db->end);
325 * flush extra line terms
327 while (IsEol(*db->start))
329 if (++db->start == db->end)
331 dyn->head = db->next;
333 if (0 == (db = dyn->head))
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
352 unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
354 struct DBufBuffer *db;
358 unsigned int copied = 0;
363 if (0 == dbuf_flush(dyn))
366 assert(0 != dyn->head);
371 assert(start < db->end);
373 if (length > dyn->length)
374 length = dyn->length;
376 * might as well copy it while we're here
380 end = IRCD_MIN(db->end, (start + length));
381 while (start < end && !IsEol(*start))
384 count = start - db->start;
389 dbuf_delete(dyn, copied);
393 if (0 == (db = db->next))