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.
26 #include "ircd_alloc.h"
27 #include "ircd_chattr.h"
28 #include "ircd_features.h"
30 #include "sys.h" /* MIN */
36 * dbuf is a collection of functions which can be used to
37 * maintain a dynamic buffering of a byte stream.
38 * Functions allocate and release memory dynamically as
39 * required [Actually, there is nothing that prevents
40 * this package maintaining the buffer on disk, either]
43 /** Number of dbufs allocated.
44 * This should only be modified by dbuf.c.
46 int DBufAllocCount = 0;
47 /** Number of dbufs in use.
48 * This should only be modified by dbuf.c.
50 int DBufUsedCount = 0;
52 /** List of allocated but unused DBuf structures. */
53 static struct DBufBuffer *dbufFreeList = 0;
55 /** Size of data for a single DBufBuffer. */
56 #define DBUF_SIZE 2048
58 /** Single data buffer in a DBuf. */
60 struct DBufBuffer *next; /**< Next data buffer, NULL if last */
61 char *start; /**< data starts here */
62 char *end; /**< data ends here */
63 char data[DBUF_SIZE]; /**< Actual data stored here */
66 /** Return memory used by allocated data buffers.
67 * @param[out] allocated Receives number of bytes allocated to DBufs.
68 * @param[out] used Receives number of bytes for currently used DBufs.
70 void dbuf_count_memory(size_t *allocated, size_t *used)
72 assert(0 != allocated);
74 *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
75 *used = DBufUsedCount * sizeof(struct DBufBuffer);
78 /** Allocate a new DBufBuffer.
79 * If #dbufFreeList != NULL, use the head of that list; otherwise,
80 * allocate a new buffer.
81 * @return Newly allocated buffer list.
83 static struct DBufBuffer *dbuf_alloc(void)
85 struct DBufBuffer* db = dbufFreeList;
88 dbufFreeList = db->next;
91 else if (DBufAllocCount * DBUF_SIZE < feature_int(FEAT_BUFFERPOOL)) {
92 db = (struct DBufBuffer*) MyMalloc(sizeof(struct DBufBuffer));
100 /** Release a DBufBuffer back to the free list.
101 * @param[in] db Data buffer to release.
103 static void dbuf_free(struct DBufBuffer *db)
107 db->next = dbufFreeList;
111 /** Handle a memory allocation error on a DBuf.
112 * This frees all the buffers owned by the DBuf, since we have to
113 * close the associated connection.
114 * @param[in] dyn DBuf to clean out.
117 static int dbuf_malloc_error(struct DBuf *dyn)
119 struct DBufBuffer *db;
120 struct DBufBuffer *next;
122 for (db = dyn->head; db; db = next)
127 dyn->tail = dyn->head = 0;
132 /** Append bytes to a data buffer.
133 * @param[in] dyn Buffer to append to.
134 * @param[in] buf Data to append.
135 * @param[in] length Number of bytes to append.
136 * @return Non-zero on success, or zero on failure.
138 int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
140 struct DBufBuffer** h;
141 struct DBufBuffer* db;
147 * Locate the last non-empty buffer. If the last buffer is full,
148 * the loop will terminate with 'db==NULL'.
149 * This loop assumes that the 'dyn->length' field is correctly
150 * maintained, as it should--no other check really needed.
157 * Append users data to buffer, allocating buffers as needed
159 dyn->length += length;
161 for (; length > 0; h = &(db->next)) {
162 if (0 == (db = *h)) {
163 if (0 == (db = dbuf_alloc())) {
164 if (feature_bool(FEAT_HAS_FERGUSON_FLUSHER)) {
166 * from "Married With Children" episode were Al bought a REAL toilet
167 * on the black market because he was tired of the wimpy water
168 * conserving toilets they make these days --Bleep
171 * Apparently this doesn't work, the server _has_ to
172 * dump a few clients to handle the load. A fully loaded
173 * server cannot handle a net break without dumping some
174 * clients. If we flush the connections here under a full
175 * load we may end up starving the kernel for mbufs and
179 * attempt to recover from buffer starvation before
180 * bailing this may help servers running out of memory
182 flush_connections(0);
187 return dbuf_malloc_error(dyn);
192 db->start = db->end = db->data;
194 chunk = (db->data + DBUF_SIZE) - db->end;
199 memcpy(db->end, buf, chunk);
209 /** Get the first contiguous block of data from a DBuf.
210 * Generally a call to dbuf_map(dyn, &count) will be followed with a
211 * call to dbuf_delete(dyn, count).
212 * @param[in] dyn DBuf to retrieve data from.
213 * @param[out] length Receives number of bytes in block.
214 * @return Pointer to start of block (or NULL if the first block is empty).
216 const char *dbuf_map(const struct DBuf* dyn, unsigned int* length)
221 if (0 == dyn->length)
226 assert(0 != dyn->head);
228 *length = dyn->head->end - dyn->head->start;
229 return dyn->head->start;
232 /** Discard data from a DBuf.
233 * @param[in,out] dyn DBuf to drop data from.
234 * @param[in] length Number of bytes to discard.
236 void dbuf_delete(struct DBuf *dyn, unsigned int length)
238 struct DBufBuffer *db;
241 if (length > dyn->length)
242 length = dyn->length;
246 if (0 == (db = dyn->head))
248 chunk = db->end - db->start;
253 dyn->length -= chunk;
256 if (db->start == db->end)
258 dyn->head = db->next;
269 /** Copy data from a buffer and remove what was copied.
270 * @param[in,out] dyn Buffer to copy from.
271 * @param[out] buf Buffer to write to.
272 * @param[in] length Maximum number of bytes to copy.
273 * @return Number of bytes actually copied.
275 unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
277 unsigned int moved = 0;
284 while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
289 memcpy(buf, b, chunk);
290 dbuf_delete(dyn, chunk);
299 /** Flush empty lines from a buffer.
300 * @param[in,out] dyn Data buffer to flush.
301 * @return Number of bytes in first available block (or zero if none).
303 static unsigned int dbuf_flush(struct DBuf *dyn)
305 struct DBufBuffer *db = dyn->head;
310 assert(db->start < db->end);
312 * flush extra line terms
314 while (IsEol(*db->start))
316 if (++db->start == db->end)
318 dyn->head = db->next;
320 if (0 == (db = dyn->head))
332 /** Copy a single line from a data buffer.
333 * If the output buffer cannot hold the whole line, or if there is no
334 * EOL in the buffer, return 0.
335 * @param[in,out] dyn Data buffer to copy from.
336 * @param[out] buf Buffer to copy to.
337 * @param[in] length Maximum number of bytes to copy.
338 * @return Number of bytes copied to \a buf.
340 unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
342 struct DBufBuffer *db;
346 unsigned int copied = 0;
351 if (0 == dbuf_flush(dyn))
354 assert(0 != dyn->head);
359 assert(start < db->end);
361 if (length > dyn->length)
362 length = dyn->length;
364 * might as well copy it while we're here
368 end = IRCD_MIN(db->end, (start + length));
369 while (start < end && !IsEol(*start))
372 count = start - db->start;
377 dbuf_delete(dyn, copied);
381 if (0 == (db = db->next))