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,v 1.12 2004/12/11 05:13:44 klmitch Exp $
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
))