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.
30 * dbuf is a collection of functions which can be used to
31 * maintain a dynamic buffering of a byte stream.
32 * Functions allocate and release memory dynamically as
33 * required [Actually, there is nothing that prevents
34 * this package maintaining the buffer on disk, either]
37 int DBufAllocCount = 0;
38 int DBufUsedCount = 0;
40 static struct DBufBuffer *dbufFreeList = 0;
42 #define DBUF_SIZE 2048
45 struct DBufBuffer *next; /* Next data buffer, NULL if last */
46 char *start; /* data starts here */
47 char *end; /* data ends here */
48 char data[DBUF_SIZE]; /* Actual data stored here */
51 void dbuf_count_memory(size_t *allocated, size_t *used)
53 assert(0 != allocated);
55 *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
56 *used = DBufUsedCount * sizeof(struct DBufBuffer);
60 * dbuf_alloc - allocates a DBufBuffer structure from the free list or
63 static struct DBufBuffer *dbuf_alloc(void)
65 struct DBufBuffer *db = dbufFreeList;
70 dbufFreeList = db->next;
72 else if (DBufAllocCount * DBUF_SIZE < BUFFERPOOL)
74 if ((db = (struct DBufBuffer *)RunMalloc(sizeof(struct DBufBuffer))))
84 * dbuf_free - return a struct DBufBuffer structure to the freelist
86 static void dbuf_free(struct DBufBuffer *db)
90 db->next = dbufFreeList;
95 * This is called when malloc fails. Scrap the whole content
96 * of dynamic buffer. (malloc errors are FATAL, there is no
97 * reason to continue this buffer...).
98 * After this the "dbuf" has consistent EMPTY status.
100 static int dbuf_malloc_error(struct DBuf *dyn)
102 struct DBufBuffer *db;
103 struct DBufBuffer *next;
105 for (db = dyn->head; db; db = next)
110 dyn->tail = dyn->head = 0;
116 * dbuf_put - Append the number of bytes to the buffer, allocating memory
117 * as needed. Bytes are copied into internal buffers from users buffer.
119 * Returns > 0, if operation successful
120 * < 0, if failed (due memory allocation problem)
122 * dyn: Dynamic buffer header
123 * buf: Pointer to data to be stored
124 * length: Number of bytes to store
126 int dbuf_put(struct DBuf *dyn, const char *buf, size_t length)
128 struct DBufBuffer **h;
129 struct DBufBuffer *db;
135 * Locate the last non-empty buffer. If the last buffer is full,
136 * the loop will terminate with 'db==NULL'.
137 * This loop assumes that the 'dyn->length' field is correctly
138 * maintained, as it should--no other check really needed.
145 * Append users data to buffer, allocating buffers as needed
147 dyn->length += length;
149 for (; length > 0; h = &(db->next))
153 if (0 == (db = dbuf_alloc()))
154 return dbuf_malloc_error(dyn);
159 db->start = db->end = db->data;
161 chunk = (db->data + DBUF_SIZE) - db->end;
167 memcpy(db->end, buf, chunk);
178 * dbuf_map, dbuf_delete
180 * These functions are meant to be used in pairs and offer a more efficient
181 * way of emptying the buffer than the normal 'dbuf_get' would allow--less
184 * map returns a pointer to a largest contiguous section
185 * of bytes in front of the buffer, the length of the
186 * section is placed into the indicated "long int"
187 * variable. Returns NULL *and* zero length, if the
190 * delete removes the specified number of bytes from the
191 * front of the buffer releasing any memory used for them.
193 * Example use (ignoring empty condition here ;)
195 * buf = dbuf_map(&dyn, &count);
196 * <process N bytes (N <= count) of data pointed by 'buf'>
197 * dbuf_delete(&dyn, N);
199 * Note: delete can be used alone, there is no real binding
200 * between map and delete functions...
202 * dyn: Dynamic buffer header
203 * length: Return number of bytes accessible
205 const char *dbuf_map(const struct DBuf *dyn, size_t *length)
210 if (0 == dyn->length)
215 assert(0 != dyn->head);
217 *length = dyn->head->end - dyn->head->start;
218 return dyn->head->start;
222 * dbuf_delete - delete length bytes from DBuf
224 * dyn: Dynamic buffer header
225 * length: Number of bytes to delete
227 void dbuf_delete(struct DBuf *dyn, size_t length)
229 struct DBufBuffer *db;
232 if (length > dyn->length)
233 length = dyn->length;
237 if (0 == (db = dyn->head))
239 chunk = db->end - db->start;
244 dyn->length -= chunk;
247 if (db->start == db->end)
249 dyn->head = db->next;
263 * Remove number of bytes from the buffer, releasing dynamic memory,
264 * if applicaple. Bytes are copied from internal buffers to users buffer.
266 * Returns the number of bytes actually copied to users buffer,
267 * if >= 0, any value less than the size of the users
268 * buffer indicates the dbuf became empty by this operation.
270 * Return 0 indicates that buffer was already empty.
272 * dyn: Dynamic buffer header
273 * buf: Pointer to buffer to receive the data
274 * length: Max amount of bytes that can be received
276 size_t dbuf_get(struct DBuf *dyn, char *buf, size_t length)
285 while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
290 memcpy(buf, b, chunk);
291 dbuf_delete(dyn, chunk);
300 static size_t dbuf_flush(struct DBuf *dyn)
302 struct DBufBuffer *db = dyn->head;
307 assert(db->start < db->end);
309 * flush extra line terms
311 while (isEol(*db->start))
313 if (++db->start == db->end)
315 dyn->head = db->next;
317 if (0 == (db = dyn->head))
331 * dbuf_getmsg - Check the buffers to see if there is a string which is
332 * terminated with either a \r or \n present. If so, copy as much as
333 * possible (determined by length) into buf and return the amount copied
336 size_t dbuf_getmsg(struct DBuf *dyn, char *buf, size_t length)
338 struct DBufBuffer *db;
347 if (0 == dbuf_flush(dyn))
350 assert(0 != dyn->head);
355 assert(start < db->end);
357 if (length > dyn->length)
358 length = dyn->length;
360 * might as well copy it while we're here
364 end = MIN(db->end, (start + length));
365 while (start < end && !isEol(*start))
368 count = start - db->start;
373 dbuf_delete(dyn, copied);
377 if (0 == (db = db->next))