dbuffer.c

Go to the documentation of this file.
00001 
00008 /*
00009  *   Copyright (C) Andrew Suffield <asuffield@debian.org>
00010  *
00011  *   This program is free software; you can redistribute it and/or modify
00012  *   it under the terms of the GNU General Public License as published by
00013  *   the Free Software Foundation; either version 2 of the License, or
00014  *   (at your option) any later version.
00015  *
00016  *   This program is distributed in the hope that it will be useful,
00017  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *   GNU General Public License for more details.
00020  *
00021  *   You should have received a copy of the GNU General Public License
00022  *   along with this program; if not, write to the Free Software
00023  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00024  */
00025 
00026 #include "common.h"
00027 #include <SDL_thread.h>
00028 
00029 /* This should fit neatly in one page */
00030 #define DBUFFER_ELEMENT_SIZE 4000
00031 /* Free some buffers when we have this many free */
00032 #ifdef DEBUG
00033 #define DBUFFER_ELEMENTS_FREE_THRESHOLD 0
00034 #define DBUFFER_FREE_THRESHOLD 0
00035 #else
00036 #define DBUFFER_ELEMENTS_FREE_THRESHOLD 1000
00037 #define DBUFFER_FREE_THRESHOLD 1000
00038 #endif
00039 
00040 struct dbuffer_element {
00041     struct dbuffer_element *next;
00042     char data[DBUFFER_ELEMENT_SIZE];
00043     size_t space, len;
00044 };
00045 
00046 static int free_elements = 0, allocated_elements = 0;
00047 static struct dbuffer_element *free_element_list = NULL;
00048 
00049 static int free_dbuffers = 0, allocated_dbuffers = 0;
00050 static struct dbuffer *free_dbuffer_list = NULL;
00051 
00052 static SDL_mutex *dbuf_lock;
00053 
00057 static struct dbuffer_element * allocate_element (void)
00058 {
00059     struct dbuffer_element *e;
00060 
00061     SDL_mutexP(dbuf_lock);
00062 
00063     if (free_elements == 0) {
00064         struct dbuffer_element *newBuf = Mem_PoolAlloc(sizeof(struct dbuffer_element), com_genericPool, 0);
00065         newBuf->next = free_element_list;
00066         free_element_list = newBuf;
00067         free_elements++;
00068         allocated_elements++;
00069     }
00070     assert(free_element_list);
00071 
00072     e = free_element_list;
00073     free_element_list = free_element_list->next;
00074     assert(free_elements > 0);
00075     free_elements--;
00076     e->space = DBUFFER_ELEMENT_SIZE;
00077     e->len = 0;
00078     e->next = NULL;
00079 
00080     SDL_mutexV(dbuf_lock);
00081 
00082     return e;
00083 }
00084 
00088 static void free_element (struct dbuffer_element *e)
00089 {
00090     SDL_mutexP(dbuf_lock);
00091 
00092     e->next = free_element_list;
00093     free_element_list = e;
00094     free_elements++;
00095     while (free_elements > DBUFFER_ELEMENTS_FREE_THRESHOLD) {
00096         e = free_element_list;
00097         free_element_list = e->next;
00098         Mem_Free(e);
00099         free_elements--;
00100         allocated_elements--;
00101     }
00102 
00103     SDL_mutexV(dbuf_lock);
00104 }
00105 
00111 struct dbuffer * new_dbuffer (void)
00112 {
00113     struct dbuffer *buf;
00114 
00115     SDL_mutexP(dbuf_lock);
00116 
00117     if (free_dbuffers == 0) {
00118         struct dbuffer *newBuf = Mem_PoolAlloc(sizeof(struct dbuffer), com_genericPool, 0);
00119         newBuf->next_free = free_dbuffer_list;
00120         free_dbuffer_list = newBuf;
00121         free_dbuffers++;
00122         allocated_dbuffers++;
00123     }
00124     assert(free_dbuffer_list);
00125 
00126     buf = free_dbuffer_list;
00127     free_dbuffer_list = free_dbuffer_list->next_free;
00128     assert(free_dbuffers > 0);
00129     free_dbuffers--;
00130 
00131     SDL_mutexV(dbuf_lock);
00132 
00133     buf->len = 0;
00134     buf->space = DBUFFER_ELEMENT_SIZE;
00135     buf->head = buf->tail = allocate_element();
00136     buf->start = buf->end = &buf->head->data[0];
00137 
00138     return buf;
00139 }
00140 
00146 void free_dbuffer (struct dbuffer *buf)
00147 {
00148     struct dbuffer_element *e;
00149     if (!buf)
00150         return;
00151     while ((e = buf->head)) {
00152         buf->head = e->next;
00153         free_element(e);
00154     }
00155 
00156     SDL_mutexP(dbuf_lock);
00157 
00158     buf->next_free = free_dbuffer_list;
00159     free_dbuffer_list = buf;
00160     free_dbuffers++;
00161 
00162     /* now we should free them, as we are still having a lot of them allocated */
00163     while (free_dbuffers > DBUFFER_FREE_THRESHOLD) {
00164         buf = free_dbuffer_list;
00165         free_dbuffer_list = buf->next_free;
00166         Mem_Free(buf);
00167         free_dbuffers--;
00168         allocated_dbuffers--;
00169     }
00170 
00171     SDL_mutexV(dbuf_lock);
00172 }
00173 
00181 static inline void dbuffer_grow (struct dbuffer *buf, size_t len)
00182 {
00183     struct dbuffer_element *e;
00184     while (buf->space <= len) {
00185         e = allocate_element();
00186         e->next = NULL;
00187         buf->tail->next = e;
00188         buf->tail = e;
00189         buf->space += DBUFFER_ELEMENT_SIZE;
00190     }
00191 }
00192 
00201 struct dbuffer *dbuffer_merge (struct dbuffer *old, struct dbuffer *old2)
00202 {
00203     /* element we're currently reading from */
00204     const struct dbuffer_element *e;
00205     struct dbuffer *buf = new_dbuffer();
00206     const char *p;
00207 
00208     e = old->head;
00209     p = old->start;
00210     while (e && (e->len > 0)) {
00211         dbuffer_add(buf, p, e->len);
00212         e = e->next;
00213         p = &e->data[0];
00214     }
00215 
00216     e = old2->head;
00217     p = old2->start;
00218     while (e && (e->len > 0)) {
00219         dbuffer_add(buf, p, e->len);
00220         e = e->next;
00221         p = &e->data[0];
00222     }
00223 
00224     return buf;
00225 }
00226 
00236 struct dbuffer *dbuffer_prepend (struct dbuffer *old, const char *data, size_t len)
00237 {
00238     /* element we're currently reading from */
00239     const struct dbuffer_element *e;
00240     struct dbuffer *buf = new_dbuffer();
00241     const char *p;
00242 
00243     dbuffer_add(buf, data, len);
00244 
00245     e = old->head;
00246     p = old->start;
00247     while (e && (e->len > 0)) {
00248         dbuffer_add(buf, p, e->len);
00249         e = e->next;
00250         p = &e->data[0];
00251     }
00252 
00253     return buf;
00254 }
00255 
00263 void dbuffer_add (struct dbuffer *buf, const char *data, size_t len)
00264 {
00265     struct dbuffer_element *e;
00266 
00267     if (!buf || !data || !len)
00268         return;
00269 
00270     e = buf->tail;
00271     /* We grow the buffer enough to ensure that we always have one extra
00272      * byte, so that buf->end has somewhere to point. The minor loss in
00273      * memory efficiency is offset by the code complexity this avoids */
00274     if (len >= buf->space)
00275         dbuffer_grow(buf, len);
00276 
00277     assert(len < buf->space);
00278     while (len > 0) {
00279         size_t l = (len < e->space) ? len : e->space;
00280         memcpy(buf->end, data, l);
00281         data += l;
00282         len -= l;
00283         e->space -= l;
00284         e->len += l;
00285         buf->end += l;
00286         buf->len += l;
00287         buf->space -=l;
00288         if (e->space == 0) {
00289             e = e->next;
00290             assert(e != NULL);
00291             buf->end = &e->data[0];
00292         }
00293     }
00294 }
00295 
00310 size_t dbuffer_get (const struct dbuffer *buf, char *data, size_t len)
00311 {
00312     size_t read = 0;
00313     /* element we're currently reading from */
00314     const struct dbuffer_element *e;
00315     /* position we're currently reading from */
00316     const void *p;
00317 
00318     if (!buf || !data || !len)
00319         return 0;
00320 
00321     e = buf->head;
00322     p = buf->start;
00323     while (len > 0 && e && e->len > 0) {
00324         const size_t l = (len < e->len) ? len : e->len;
00325         memcpy(data, p, l);
00326         data += l;
00327         len -= l;
00328         e = e->next;
00329         p = &e->data[0];
00330         read += l;
00331     }
00332     return read;
00333 }
00334 
00350 size_t dbuffer_get_at (const struct dbuffer *buf, size_t offset, char *data, size_t len)
00351 {
00352     size_t read = 0;
00353     /* element we're currently reading from */
00354     const struct dbuffer_element *e;
00355     /* position we're currently reading from */
00356     const char *p;
00357 
00358     if (!buf || !data || !len)
00359         return 0;
00360 
00361     e = buf->head;
00362     p = buf->start;
00363 
00364     while (offset > 0 && e && offset >= e->len) {
00365         offset -= e->len;
00366         e = e->next;
00367         p = &e->data[0];
00368     }
00369 
00370     if (e == NULL || e->len <= 0)
00371         return 0;
00372 
00373     while (len > 0 && e && e->len > 0) {
00374         size_t l = e->len - offset;
00375         if (len < l)
00376             l = len;
00377         p += offset;
00378         memcpy(data, p, l);
00379         data += l;
00380         len -= l;
00381         offset = 0;
00382         e = e->next;
00383         p = &e->data[0];
00384         read += l;
00385     }
00386 
00387     return read;
00388 }
00389 
00397 struct dbuffer *dbuffer_dup (struct dbuffer *old)
00398 {
00399     /* element we're currently reading from */
00400     const struct dbuffer_element *e;
00401     struct dbuffer *buf = new_dbuffer();
00402     const char *p;
00403 
00404     e = old->head;
00405     p = old->start;
00406     while (e && (e->len > 0)) {
00407         dbuffer_add(buf, p, e->len);
00408         e = e->next;
00409         p = &e->data[0];
00410     }
00411 
00412     return buf;
00413 }
00414 
00421 size_t dbuffer_remove (struct dbuffer *buf, size_t len)
00422 {
00423     size_t removed = 0;
00424 
00425     if (!buf  || !len)
00426         return 0;
00427 
00428     while (len > 0 && buf->len > 0) {
00429         const size_t l = (len < buf->head->len) ? len : buf->head->len;
00430         struct dbuffer_element *e = buf->head;
00431         buf->len -= l;
00432         buf->space += l;
00433         len -= l;
00434         e->len -= l;
00435         e->space += l;
00436         removed += l;
00437         buf->start += l;
00438         if (e->len == 0) {
00439             if (e->next) {
00440                 buf->head = e->next;
00441                 free_element(e);
00442                 buf->space -= DBUFFER_ELEMENT_SIZE;
00443             } else {
00444                 assert(buf->len == 0);
00445                 buf->end = &buf->head->data[0];
00446             }
00447             buf->start = &buf->head->data[0];
00448         }
00449     }
00450     return removed;
00451 }
00452 
00471 size_t dbuffer_extract (struct dbuffer *buf, char *data, size_t len)
00472 {
00473     size_t extracted = 0;
00474     if (!buf || !data || !len)
00475         return 0;
00476     while (len > 0 && buf->len > 0) {
00477         const size_t l = (len < buf->head->len) ? len : buf->head->len;
00478         struct dbuffer_element *e = buf->head;
00479         memcpy(data, buf->start, l);
00480         data += l;
00481         len -= l;
00482         buf->len -= l;
00483         buf->space += l;
00484         e->len -= l;
00485         e->space += l;
00486         extracted += l;
00487         buf->start += l;
00488         if (e->len == 0) {
00489             if (e->next) {
00490                 buf->head = e->next;
00491                 free_element(e);
00492                 buf->space -= DBUFFER_ELEMENT_SIZE;
00493             } else {
00494                 assert(buf->len == 0);
00495                 buf->end = &buf->head->data[0];
00496             }
00497             buf->start = &buf->head->data[0];
00498         }
00499     }
00500     return extracted;
00501 }
00502 
00503 void dbuffer_init (void)
00504 {
00505     dbuf_lock = SDL_CreateMutex();
00506 }
00507 
00508 void dbuffer_shutdown (void)
00509 {
00510     SDL_DestroyMutex(dbuf_lock);
00511 }

Generated by  doxygen 1.6.2