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.
20 * @brief Implementation of functions dealing with data buffers.
21 * @version $Id: dbuf.c 1271 2004-12-11 05:14:07Z klmitch $
26 #include "ircd_alloc.h"
27 #include "ircd_chattr.h"
28 #include "ircd_features.h"
31 #include "sys.h" /* MIN */
33 /* #include <assert.h> -- Now using assert in ircd_log.h */
37 * dbuf is a collection of functions which can be used to
38 * maintain a dynamic buffering of a byte stream.
39 * Functions allocate and release memory dynamically as
40 * required [Actually, there is nothing that prevents
41 * this package maintaining the buffer on disk, either]
44 /** Number of dbufs allocated.
45 * This should only be modified by dbuf.c.
47 int DBufAllocCount = 0;
48 /** Number of dbufs in use.
49 * This should only be modified by dbuf.c.
51 int DBufUsedCount = 0;
53 /** List of allocated but unused DBuf structures. */
54 static struct DBufBuffer *dbufFreeList = 0;
56 /** Size of data for a single DBufBuffer. */
57 #define DBUF_SIZE 2048
59 /** Single data buffer in a DBuf. */
61 struct DBufBuffer *next; /**< Next data buffer, NULL if last */
62 char *start; /**< data starts here */
63 char *end; /**< data ends here */
64 char data[DBUF_SIZE]; /**< Actual data stored here */
67 /** Return memory used by allocated data buffers.
68 * @param[out] allocated Receives number of bytes allocated to DBufs.
69 * @param[out] used Receives number of bytes for currently used DBufs.
71 void dbuf_count_memory(size_t *allocated, size_t *used)
73 assert(0 != allocated);
75 *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
76 *used = DBufUsedCount * sizeof(struct DBufBuffer);
79 /** Allocate a new DBufBuffer.
80 * If #dbufFreeList != NULL, use the head of that list; otherwise,
81 * allocate a new buffer.
82 * @return Newly allocated buffer list.
84 static struct DBufBuffer *dbuf_alloc(void)
86 struct DBufBuffer* db = dbufFreeList;
89 dbufFreeList = db->next;
92 else if (DBufAllocCount * DBUF_SIZE < feature_int(FEAT_BUFFERPOOL)) {
93 db = (struct DBufBuffer*) MyMalloc(sizeof(struct DBufBuffer));
101 /** Release a DBufBuffer back to the free list.
102 * @param[in] db Data buffer to release.
104 static void dbuf_free(struct DBufBuffer *db)
108 db->next = dbufFreeList;
112 /** Handle a memory allocation error on a DBuf.
113 * This frees all the buffers owned by the DBuf, since we have to
114 * close the associated connection.
115 * @param[in] dyn DBuf to clean out.
118 static int dbuf_malloc_error(struct DBuf *dyn)
120 struct DBufBuffer *db;
121 struct DBufBuffer *next;
123 for (db = dyn->head; db; db = next)
128 dyn->tail = dyn->head = 0;
133 /** Append bytes to a data buffer.
134 * @param[in] dyn Buffer to append to.
135 * @param[in] buf Data to append.
136 * @param[in] length Number of bytes to append.
137 * @return Non-zero on success, or zero on failure.
139 int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
141 struct DBufBuffer** h;
142 struct DBufBuffer* db;
148 * Locate the last non-empty buffer. If the last buffer is full,
149 * the loop will terminate with 'db==NULL'.
150 * This loop assumes that the 'dyn->length' field is correctly
151 * maintained, as it should--no other check really needed.
158 * Append users data to buffer, allocating buffers as needed
160 dyn->length += length;
162 for (; length > 0; h = &(db->next)) {
163 if (0 == (db = *h)) {
164 if (0 == (db = dbuf_alloc())) {
165 if (feature_bool(FEAT_HAS_FERGUSON_FLUSHER)) {
167 * from "Married With Children" episode were Al bought a REAL toilet
168 * on the black market because he was tired of the wimpy water
169 * conserving toilets they make these days --Bleep
172 * Apparently this doesn't work, the server _has_ to
173 * dump a few clients to handle the load. A fully loaded
174 * server cannot handle a net break without dumping some
175 * clients. If we flush the connections here under a full
176 * load we may end up starving the kernel for mbufs and
180 * attempt to recover from buffer starvation before
181 * bailing this may help servers running out of memory
183 flush_connections(0);
188 return dbuf_malloc_error(dyn);
193 db->start = db->end = db->data;
195 chunk = (db->data + DBUF_SIZE) - db->end;
200 memcpy(db->end, buf, chunk);
210 /** Get the first contiguous block of data from a DBuf.
211 * Generally a call to dbuf_map(dyn, &count) will be followed with a
212 * call to dbuf_delete(dyn, count).
213 * @param[in] dyn DBuf to retrieve data from.
214 * @param[out] length Receives number of bytes in block.
215 * @return Pointer to start of block (or NULL if the first block is empty).
217 const char *dbuf_map(const struct DBuf* dyn, unsigned int* length)
222 if (0 == dyn->length)
227 assert(0 != dyn->head);
229 *length = dyn->head->end - dyn->head->start;
230 return dyn->head->start;
233 /** Discard data from a DBuf.
234 * @param[in,out] dyn DBuf to drop data from.
235 * @param[in] length Number of bytes to discard.
237 void dbuf_delete(struct DBuf *dyn, unsigned int length)
239 struct DBufBuffer *db;
242 if (length > dyn->length)
243 length = dyn->length;
247 if (0 == (db = dyn->head))
249 chunk = db->end - db->start;
254 dyn->length -= chunk;
257 if (db->start == db->end)
259 dyn->head = db->next;
270 /** Copy data from a buffer and remove what was copied.
271 * @param[in,out] dyn Buffer to copy from.
272 * @param[out] buf Buffer to write to.
273 * @param[in] length Maximum number of bytes to copy.
274 * @return Number of bytes actually copied.
276 unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
278 unsigned int moved = 0;
285 while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
290 memcpy(buf, b, chunk);
291 dbuf_delete(dyn, chunk);
300 /** Flush empty lines from a buffer.
301 * @param[in,out] dyn Data buffer to flush.
302 * @return Number of bytes in first available block (or zero if none).
304 static unsigned int dbuf_flush(struct DBuf *dyn)
306 struct DBufBuffer *db = dyn->head;
311 assert(db->start < db->end);
313 * flush extra line terms
315 while (IsEol(*db->start))
317 if (++db->start == db->end)
319 dyn->head = db->next;
321 if (0 == (db = dyn->head))
333 /** Copy a single line from a data buffer.
334 * If the output buffer cannot hold the whole line, or if there is no
335 * EOL in the buffer, return 0.
336 * @param[in,out] dyn Data buffer to copy from.
337 * @param[out] buf Buffer to copy to.
338 * @param[in] length Maximum number of bytes to copy.
339 * @return Number of bytes copied to \a buf.
341 unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
343 struct DBufBuffer *db;
347 unsigned int copied = 0;
352 if (0 == dbuf_flush(dyn))
355 assert(0 != dyn->head);
360 assert(start < db->end);
362 if (length > dyn->length)
363 length = dyn->length;
365 * might as well copy it while we're here
369 end = IRCD_MIN(db->end, (start + length));
370 while (start < end && !IsEol(*start))
373 count = start - db->start;
378 dbuf_delete(dyn, copied);
382 if (0 == (db = db->next))