ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/branches/newio/src/dbuf.c
Revision: 2388
Committed: Tue Jul 9 11:22:52 2013 UTC (10 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 9694 byte(s)
Log Message:
- Working towards implementing new ioengine

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 1990 Markku Savela
5 * Copyright (C) 2013 by the Hybrid Development Team.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 */
22
23 /*! \file dbuf.c
24 * \brief Interfaces and declarations for dealing with data buffers.
25 * \version $Id$
26 */
27
28 #include "stdinc.h"
29 #include "dbuf.h"
30 #include "memory.h"
31 #include "log.h"
32 #include "send.h"
33 #include "irc_string.h"
34
35 #define FEAT_BUFFERPOOL 27000000 /* XXX */
36
37 /*
38 * dbuf is a collection of functions which can be used to
39 * maintain a dynamic buffering of a byte stream.
40 * Functions allocate and release memory dynamically as
41 * required [Actually, there is nothing that prevents
42 * this package maintaining the buffer on disk, either]
43 */
44
45 /** Number of dbufs allocated.
46 * This should only be modified by dbuf.c.
47 */
48 int DBufAllocCount = 0;
49 /** Number of dbufs in use.
50 * This should only be modified by dbuf.c.
51 */
52 int DBufUsedCount = 0;
53
54 /** List of allocated but unused DBuf structures. */
55 static struct DBufBuffer *dbufFreeList;
56
57 /** Size of data for a single DBufBuffer. */
58 #define DBUF_SIZE 2048
59
60 /** Single data buffer in a DBuf. */
61 struct DBufBuffer
62 {
63 struct DBufBuffer *next; /**< Next data buffer, NULL if last */
64 char *start; /**< Data starts here */
65 char *end; /**< Data ends here */
66 char data[DBUF_SIZE]; /**< Actual data stored here */
67 };
68
69 /** Return memory used by allocated data buffers.
70 * @param[out] allocated Receives number of bytes allocated to DBufs.
71 * @param[out] used Receives number of bytes for currently used DBufs.
72 */
73 void
74 dbuf_count_memory(size_t *allocated, size_t *used)
75 {
76 assert(allocated);
77 assert(used);
78
79 *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
80 *used = DBufUsedCount * sizeof(struct DBufBuffer);
81 }
82
83 /** Allocate a new DBufBuffer.
84 * If #dbufFreeList != NULL, use the head of that list; otherwise,
85 * allocate a new buffer.
86 * @return Newly allocated buffer list.
87 */
88 static struct DBufBuffer *
89 dbuf_alloc(void)
90 {
91 struct DBufBuffer *db = dbufFreeList;
92
93 if (db)
94 {
95 dbufFreeList = db->next;
96 ++DBufUsedCount;
97 }
98 else if ((DBufAllocCount * DBUF_SIZE) < FEAT_BUFFERPOOL)
99 {
100 db = (struct DBufBuffer *)MyMalloc(sizeof(struct DBufBuffer));
101 assert(0 != db);
102 ++DBufAllocCount;
103 ++DBufUsedCount;
104 }
105
106 return db;
107 }
108
109 /** Release a DBufBuffer back to the free list.
110 * @param[in] db Data buffer to release.
111 */
112 static void
113 dbuf_free(struct DBufBuffer *db)
114 {
115 assert(db);
116
117 --DBufUsedCount;
118 db->next = dbufFreeList;
119 dbufFreeList = db;
120 }
121
122 /** Handle a memory allocation error on a DBuf.
123 * This frees all the buffers owned by the DBuf, since we have to
124 * close the associated connection.
125 * @param[in] dyn DBuf to clean out.
126 * @return Zero.
127 */
128 static int
129 dbuf_malloc_error(struct DBuf *dyn)
130 {
131 struct DBufBuffer *db = NULL, *next = NULL;
132
133 for (db = dyn->head; db; db = next)
134 {
135 next = db->next;
136 dbuf_free(db);
137 }
138
139 dyn->tail = dyn->head = 0;
140 dyn->length = 0;
141 return 0;
142 }
143
144 /** Append bytes to a data buffer.
145 * @param[in] dyn Buffer to append to.
146 * @param[in] buf Data to append.
147 * @param[in] length Number of bytes to append.
148 * @return Non-zero on success, or zero on failure.
149 */
150 int
151 dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length)
152 {
153 struct DBufBuffer **h;
154 struct DBufBuffer *db;
155 unsigned int chunk;
156
157 assert(dyn);
158 assert(buf);
159
160 /*
161 * Locate the last non-empty buffer. If the last buffer is full,
162 * the loop will terminate with 'db==NULL'.
163 * This loop assumes that the 'dyn->length' field is correctly
164 * maintained, as it should--no other check really needed.
165 */
166 if (!dyn->length)
167 h = &(dyn->head);
168 else
169 h = &(dyn->tail);
170
171 /*
172 * Append users data to buffer, allocating buffers as needed
173 */
174 dyn->length += length;
175
176 for (; length > 0; h = &(db->next))
177 {
178 if (!(db = *h))
179 {
180 if (!(db = dbuf_alloc()))
181 {
182 /*
183 * From "Married With Children" episode were Al bought a REAL toilet
184 * on the black market because he was tired of the wimpy water
185 * conserving toilets they make these days --Bleep
186 */
187
188 /*
189 * Apparently this doesn't work, the server _has_ to
190 * dump a few clients to handle the load. A fully loaded
191 * server cannot handle a net break without dumping some
192 * clients. If we flush the connections here under a full
193 * load we may end up starving the kernel for mbufs and
194 * crash the machine
195 */
196
197 /*
198 * Attempt to recover from buffer starvation before
199 * bailing this may help servers running out of memory
200 */
201 flush_connections(0);
202 db = dbuf_alloc();
203
204 if (!db)
205 return dbuf_malloc_error(dyn);
206 }
207
208 dyn->tail = db;
209 *h = db;
210 db->next = 0;
211 db->start = db->end = db->data;
212 }
213
214 chunk = (db->data + DBUF_SIZE) - db->end;
215
216 if (chunk)
217 {
218 if (chunk > length)
219 chunk = length;
220
221 memcpy(db->end, buf, chunk);
222
223 length -= chunk;
224 buf += chunk;
225 db->end += chunk;
226 }
227 }
228
229 return 1;
230 }
231
232 /** Get the first contiguous block of data from a DBuf.
233 * Generally a call to dbuf_map(dyn, &count) will be followed with a
234 * call to dbuf_delete(dyn, count).
235 * @param[in] dyn DBuf to retrieve data from.
236 * @param[out] length Receives number of bytes in block.
237 * @return Pointer to start of block (or NULL if the first block is empty).
238 */
239 const char *
240 dbuf_map(const struct DBuf *dyn, unsigned int *length)
241 {
242 assert(dyn);
243 assert(length);
244
245 if (!dyn->length)
246 {
247 *length = 0;
248 return 0;
249 }
250
251 assert(dyn->head);
252
253 *length = dyn->head->end - dyn->head->start;
254 return dyn->head->start;
255 }
256
257 /** Discard data from a DBuf.
258 * @param[in,out] dyn DBuf to drop data from.
259 * @param[in] length Number of bytes to discard.
260 */
261 void
262 dbuf_delete(struct DBuf *dyn, unsigned int length)
263 {
264 struct DBufBuffer *db = NULL;
265 unsigned int chunk = 0;
266
267 if (length > dyn->length)
268 length = dyn->length;
269
270 while (length > 0)
271 {
272 if (!(db = dyn->head))
273 break;
274
275 chunk = db->end - db->start;
276 if (chunk > length)
277 chunk = length;
278
279 length -= chunk;
280 dyn->length -= chunk;
281 db->start += chunk;
282
283 if (db->start == db->end)
284 {
285 dyn->head = db->next;
286 dbuf_free(db);
287 }
288 }
289
290 if (!dyn->head)
291 {
292 dyn->length = 0;
293 dyn->tail = 0;
294 }
295 }
296
297 /** Copy data from a buffer and remove what was copied.
298 * @param[in,out] dyn Buffer to copy from.
299 * @param[out] buf Buffer to write to.
300 * @param[in] length Maximum number of bytes to copy.
301 * @return Number of bytes actually copied.
302 */
303 unsigned int
304 dbuf_get(struct DBuf *dyn, char *buf, unsigned int length)
305 {
306 unsigned int moved = 0;
307 unsigned int chunk;
308 const char *b;
309
310 assert(dyn);
311 assert(buf);
312
313 while (length > 0 && (b = dbuf_map(dyn, &chunk)))
314 {
315 if (chunk > length)
316 chunk = length;
317
318 memcpy(buf, b, chunk);
319 dbuf_delete(dyn, chunk);
320
321 buf += chunk;
322 length -= chunk;
323 moved += chunk;
324 }
325
326 return moved;
327 }
328
329 /** Flush empty lines from a buffer.
330 * @param[in,out] dyn Data buffer to flush.
331 * @return Number of bytes in first available block (or zero if none).
332 */
333 static unsigned int
334 dbuf_flush(struct DBuf *dyn)
335 {
336 struct DBufBuffer *db = dyn->head;
337
338 if (!db)
339 return 0;
340
341 assert(db->start < db->end);
342
343 /*
344 * Flush extra line terms
345 */
346 while (IsEol(*db->start))
347 {
348 if (++db->start == db->end)
349 {
350 dyn->head = db->next;
351 dbuf_free(db);
352
353 if (!(db = dyn->head))
354 {
355 dyn->tail = 0;
356 dyn->length = 0;
357 break;
358 }
359 }
360
361 --dyn->length;
362 }
363
364 return dyn->length;
365 }
366
367 /** Copy a single line from a data buffer.
368 * If the output buffer cannot hold the whole line, or if there is no
369 * EOL in the buffer, return 0.
370 * @param[in,out] dyn Data buffer to copy from.
371 * @param[out] buf Buffer to copy to.
372 * @param[in] length Maximum number of bytes to copy.
373 * @return Number of bytes copied to \a buf.
374 */
375 unsigned int
376 dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length)
377 {
378 struct DBufBuffer *db = NULL;
379 char *start = NULL;
380 char *end = NULL;
381 unsigned int count = 0;
382 unsigned int copied = 0;
383
384 assert(dyn);
385 assert(buf);
386
387 if (!dbuf_flush(dyn))
388 return 0;
389
390 assert(dyn->head);
391
392 db = dyn->head;
393 start = db->start;
394
395 assert(start < db->end);
396
397 if (length > dyn->length)
398 length = dyn->length;
399
400 /*
401 * Might as well copy it while we're here
402 */
403 while (length > 0)
404 {
405 end = IRCD_MIN(db->end, (start + length));
406
407 while (start < end && !IsEol(*start))
408 *buf++ = *start++;
409
410 count = start - db->start;
411
412 if (start < end)
413 {
414 *buf = '\0';
415 copied += count;
416 dbuf_delete(dyn, copied);
417 dbuf_flush(dyn);
418 return copied;
419 }
420
421 if (!(db = db->next))
422 break;
423 copied += count;
424 length -= count;
425 start = db->start;
426 }
427
428 return 0;
429 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision