head 1.41; access; symbols; locks; strict; comment @ * @; 1.41 date 2002.12.17.13.43.19; author mlelstv; state dead; branches; next 1.40; 1.40 date 2002.11.29.14.06.52; author mlelstv; state Exp; branches; next 1.39; 1.39 date 2002.11.28.17.21.55; author mlelstv; state Exp; branches; next 1.38; 1.38 date 2002.11.28.16.11.49; author mlelstv; state Exp; branches; next 1.37; 1.37 date 2002.11.28.15.12.40; author mlelstv; state Exp; branches; next 1.36; 1.36 date 2002.11.19.17.02.17; author mlelstv; state Exp; branches; next 1.35; 1.35 date 2002.11.14.09.22.42; author rse; state Exp; branches; next 1.34; 1.34 date 2002.11.05.14.51.37; author mlelstv; state Exp; branches; next 1.33; 1.33 date 2002.11.04.13.14.50; author mlelstv; state Exp; branches; next 1.32; 1.32 date 2002.10.25.13.29.02; author mlelstv; state Exp; branches; next 1.31; 1.31 date 2002.10.23.16.49.29; author mlelstv; state Exp; branches; next 1.30; 1.30 date 2002.10.23.13.59.30; author mlelstv; state Exp; branches; next 1.29; 1.29 date 2002.10.22.15.33.16; author mlelstv; state Exp; branches; next 1.28; 1.28 date 2002.10.22.15.09.44; author mlelstv; state Exp; branches; next 1.27; 1.27 date 2002.10.18.14.12.11; author mlelstv; state Exp; branches; next 1.26; 1.26 date 2002.10.18.12.41.20; author mlelstv; state Exp; branches; next 1.25; 1.25 date 2002.10.18.12.31.50; author mlelstv; state Exp; branches; next 1.24; 1.24 date 2002.10.18.12.24.53; author mlelstv; state Exp; branches; next 1.23; 1.23 date 2002.10.18.12.08.52; author rse; state Exp; branches; next 1.22; 1.22 date 2002.10.18.11.01.59; author rse; state Exp; branches; next 1.21; 1.21 date 2002.10.18.10.20.26; author rse; state Exp; branches; next 1.20; 1.20 date 2002.10.18.09.09.18; author mlelstv; state Exp; branches; next 1.19; 1.19 date 2002.10.18.08.58.19; author mlelstv; state Exp; branches; next 1.18; 1.18 date 2002.10.17.15.02.49; author rse; state Exp; branches; next 1.17; 1.17 date 2002.10.17.14.16.51; author thl; state Exp; branches; next 1.16; 1.16 date 2002.10.17.11.42.43; author thl; state Exp; branches; next 1.15; 1.15 date 2002.10.17.10.17.54; author mlelstv; state Exp; branches; next 1.14; 1.14 date 2002.10.16.12.37.08; author rse; state Exp; branches; next 1.13; 1.13 date 2002.10.16.11.45.30; author mlelstv; state Exp; branches; next 1.12; 1.12 date 2002.10.16.09.31.55; author mlelstv; state Exp; branches; next 1.11; 1.11 date 2002.10.16.09.28.17; author mlelstv; state Exp; branches; next 1.10; 1.10 date 2002.10.16.07.59.00; author mlelstv; state Exp; branches; next 1.9; 1.9 date 2002.10.15.13.13.06; author mlelstv; state Exp; branches; next 1.8; 1.8 date 2002.10.14.15.44.03; author mlelstv; state Exp; branches; next 1.7; 1.7 date 2002.10.14.15.41.37; author mlelstv; state Exp; branches; next 1.6; 1.6 date 2002.10.14.15.26.04; author mlelstv; state Exp; branches; next 1.5; 1.5 date 2002.10.14.13.45.47; author mlelstv; state Exp; branches; next 1.4; 1.4 date 2002.10.14.13.34.25; author mlelstv; state Exp; branches; next 1.3; 1.3 date 2002.10.14.12.32.16; author mlelstv; state Exp; branches; next 1.2; 1.2 date 2002.10.14.09.17.21; author mlelstv; state Exp; branches; next 1.1; 1.1 date 2002.10.14.08.04.46; author mlelstv; state Exp; branches; next ; desc @@ 1.41 log @moved into its own al project PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @/* ** OSSP al -- Assembly Line ** Copyright (c) 2002 The OSSP Project ** Copyright (c) 2002 Cable & Wireless Deutschland ** Copyright (c) 2002 Ralf S. Engelschall ** Copyright (c) 2002 Michael van Elst ** ** This file is part of OSSP al, an abstract datatype of a data buffer ** that can assemble, move and truncate data but avoids actual copying. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** al.c: assembly line library implementation */ #include #include #include #ifdef TEST #include #endif #include "list.h" #include "al.h" /* unique library identifier */ const char al_id[] = "OSSP al"; /* support for OSSP ex based exception throwing */ #ifdef WITH_EX #include "ex.h" #define AL_RC(rv) \ ( (rv) != AL_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(al_id, NULL, (rv)), (rv)) : (rv) ) #else #define AL_RC(rv) (rv) #endif /* WITH_EX */ struct al_buffer_st; typedef struct al_buffer_st al_buffer_t; typedef struct { void *(*malloc)(size_t); /* malloc(3) style function (for al_chunk_t) */ void (*free)(void *); /* free(3) style function (for al_chunk_t) */ void *(*balloc)(size_t); /* malloc(3) style function (for al_buffer_t) */ void (*bfree)(void *); /* free(3) style function (for al_buffer_t) */ size_t new_buffersize; /* default size for memory underlying al_buffer_t */ int max_freechunks; /* maximum number of cached al_chunk_t objects */ } al_memops_t; struct al_st { LIST(al_chunk_t) chunks; /* list header for al_chunk_t objects */ size_t bytes; /* total cached number of bytes in chunk */ al_memops_t m; /* assembly line memory operations (see above) */ }; struct al_chunk_st { NODE(al_chunk_t) chunks; /* list node for al_chunk_t object chaining */ al_buffer_t *buf; /* (non-exlusively) referenced buffer object */ size_t begin; /* offset into buf->mem where data starts */ size_t end; /* offset into buf->mem where data ends */ al_label_t label; /* tag data with a label, chunks with distinct labels are not coalesced */ }; struct al_buffer_st { char *mem; /* reference to underlying chunk of data */ size_t size; /* size of underlying chunk of data */ int usecount; /* reference count (from al_chunk_t) */ void (*freemem)(char *p, size_t n, void *u); /* callback function to reclaim memory when it is no longer referenced */ void *userdata; /* arbitrary pointer to pass context data to the callback function */ }; struct al_tx_st { al_td_t dir; /* traversal direction */ al_chunk_t *cur; /* current chunk during traveral steps */ size_t skip; /* number of bytes to skip for traversal */ size_t togo; /* number of bytes left for traversal */ al_label_t label; /* label filter or NULL */ al_chunk_t view; /* synthetic chunk for returning during traversal steps */ }; /* number of bytes described by a chunk */ #define AL_CHUNK_LEN(alc) \ ((alc)->end - (alc)->begin) /* memory pointer into a chunk, offset must be less than length */ #define AL_CHUNK_PTR(alc, off) \ ((alc)->buf->mem + (alc)->begin + (off)) /* number of bytes of a span that are stored in a chunk */ #define AL_CHUNK_SPAN(alc, off, n) \ ((n) > (AL_CHUNK_LEN(alc) - (off)) ? \ (AL_CHUNK_LEN(alc) - (off)) : (n)) /* return chunk label */ #define AL_CHUNK_LABEL(alc) \ ((alc)->label) /* check wether labels match */ #define AL_SAME_LABEL(alc,label) \ ((label) == NULL || AL_CHUNK_LABEL(alc) == (label)) /* * number of bytes a chunk can be grown to the end * we must not grow a chunk in a shared buffer since * we do not track other chunks sharing the buffer */ #define AL_CHUNK_RESERVE(alc,label) \ ( (alc) != NULL \ ? ( (alc)->buf->usecount > 1 || !AL_SAME_LABEL(alc,label) \ ? 0 \ : (alc)->buf->size - (alc)->end) \ : 0) /* grow chunk to the end */ #define AL_RESIZE(al, alc, n) \ do { (alc)->end += n; (al)->bytes += (n); } while (0) /* * number of bytes a chunk can be grown to the beginning * we must not grow a chunk in a shared buffer since * we do not track other chunks sharing the buffer * * empty chunks can be aligned with end */ #define AL_CHUNK_PRESERVE(alc,label) \ ( (alc) != NULL \ ? ( (alc)->buf->usecount > 1 || !AL_SAME_LABEL(alc,label) \ ? 0 \ : (alc)->end <= (alc)->begin ? \ (alc)->buf->size \ : (alc)->begin) \ : 0) /* * grow chunk to the beginning * * empty chunks can be aligned with end */ #define AL_PRESIZE(al, alc, n) \ do { \ if ((alc)->end <= (alc)->begin) \ (alc)->end = (alc)->begin = (alc)->buf->size; \ (alc)->begin -= n; (al)->bytes += (n); \ } while (0) /* * callback to release buffer memory allocated by the library */ static void freemem(char *p, size_t n, void *u) { void (*f)(void *) = (void (*)(void *))u; f(p); } /* * allocate backing store and its control structure from heap * * can be freed when usecount drops to zero */ static al_rc_t new_buffer(al_t *al, al_buffer_t **bufp) { size_t n = al->m.new_buffersize; al_buffer_t *buf; if ((buf = (al_buffer_t *)(al->m.malloc)(sizeof(al_buffer_t))) == NULL) return AL_ERR_MEM; if ((buf->mem = (al->m.balloc)(n)) == NULL) { (al->m.free)(buf); return AL_ERR_MEM; } buf->freemem = freemem; buf->userdata = (void *)al->m.bfree; buf->size = n; buf->usecount = 0; *bufp = buf; return AL_OK; } static al_rc_t dispose_buffer(al_t *al, al_buffer_t *buf) { /* must not happen */ if (buf->usecount != 0) return AL_ERR_INT; if (buf->freemem) (buf->freemem)(buf->mem, buf->size, buf->userdata); (al->m.free)(buf); return AL_OK; } /* * allocate only control structure for backing store from heap * and attach to existing memory * only the control structure will be freed later */ static al_rc_t make_buffer(al_t *al, char *p, size_t n, void (*freemem_cb)(char *, size_t, void *), void *u, al_buffer_t **bufp) { al_buffer_t *buf; if ((buf = (al_buffer_t *)(al->m.malloc)(sizeof(al_buffer_t))) == NULL) return AL_ERR_MEM; buf->mem = p; buf->freemem = freemem_cb; buf->userdata = u; buf->size = n; buf->usecount = 0; *bufp = buf; return AL_OK; } /* * allocate chunks from heap and attach to backing store * keep some freed chunks in a freelist to avoid expensive malloc() * and excessive fragmentation * maintain usage count of backing store * free backing store when no longer in use, this also includes * the case where new_chunk fails and the buffer has no other * users */ static struct { LIST(al_chunk_t) chunks; } alc_freelist; static int alc_freecount = 0; static al_rc_t new_chunk(al_t *al, al_buffer_t *buf, al_label_t label, al_chunk_t **alcp) { al_chunk_t *alc; if (ISEMPTY(&alc_freelist, chunks)) { if ((alc = (al_chunk_t *)(al->m.malloc)(sizeof(al_chunk_t))) == NULL) { if (buf->usecount == 0) dispose_buffer(al,buf); return AL_ERR_MEM; } } else { /* thl: FIXME i would suggest using REMTAIL() here because dispose_chunk * puts the latest disposed chunks at the end of the list and * those latest data should have a closer approximation to the * CPU in terms of internal cache, external cache, RAM and VM */ REMHEAD(&alc_freelist, chunks, alc); --alc_freecount; } NODEINIT(alc, chunks); alc->buf = buf; alc->begin = 0; alc->end = 0; alc->label = label; buf->usecount++; *alcp = alc; return AL_OK; } static al_rc_t split_chunk(al_t *al, al_chunk_t *orig, size_t off, al_chunk_t **alcp) { al_rc_t rc; al_chunk_t *alc; size_t len = AL_CHUNK_LEN(orig); if (off > len) return AL_ERR_ARG; rc = new_chunk(al, orig->buf, orig->label, &alc); if (rc != AL_OK) return rc; alc->begin = orig->begin; alc->end = orig->begin+off; orig->begin = alc->end; *alcp = alc; return AL_OK; } static void dispose_chunk(al_t *al, al_chunk_t *alc) { alc->buf->usecount--; if (alc->buf->usecount == 0) dispose_buffer(al,alc->buf); alc->buf = NULL; alc->label = NULL; /* stop freelist from growing infinitely */ if (alc_freecount >= al->m.max_freechunks) (al->m.free)(alc); else { ADDTAIL(&alc_freelist, chunks, alc); alc_freecount++; } } /* * find chunk that represents a particular offset * a reference to the chunk is stored in *alcp * the relative offset into the chunk is stored in *skipp * return AL_OK and *alcp == NULL if positioned exactly to end of list * return AL_ERR_EOF when no such chunk can be found */ static al_rc_t al_seek(al_t *al, size_t off, al_chunk_t **alcp, size_t *skipp) { al_chunk_t *cur; size_t pos, end; size_t chunksize; if ((al->bytes / 2) >= off) { /* FIXME poor man's heuristic */ /* forward search */ pos = 0; FOREACH(al,chunks,cur) { chunksize = AL_CHUNK_LEN(cur); end = pos+chunksize; if (pos <= off && off < end) { *alcp = cur; *skipp = off - pos; return AL_OK; } if (end > off) break; pos = end; } /* seek to EOF position is ok */ if (pos == off) { *alcp = NULL; *skipp = 0; return AL_OK; } } else { /* reverse search */ pos = al->bytes; /* seek to EOF position is ok */ if (pos == off) { *alcp = NULL; *skipp = 0; return AL_OK; } FOREACHR(al,chunks,cur) { chunksize = AL_CHUNK_LEN(cur); end = pos; pos -= chunksize; if (pos <= off && off < end) { *alcp = cur; *skipp = off - pos; return AL_OK; } if (pos < off) break; } } return AL_ERR_EOF; } #ifdef TEST static void dump(al_t *al) { al_chunk_t *cur; size_t total; printf("AL: %p\n", al); total = 0; FOREACH(al,chunks,cur) { printf(" C: %p [%p] (%d @@ %p + %d < %d (use=%d))\n", cur, cur->label, cur->buf->size,cur->buf->mem, cur->begin,cur->end, cur->buf->usecount); total += AL_CHUNK_LEN(cur); } printf("size = %d == %d\n",al->bytes,total); printf("--\n\n"); } #endif /****************************************************************************/ /* * allocate an empty assembly line * dispose all chunks and all allocated backing store */ al_rc_t al_create(al_t **alp) { al_t *al; /* argument sanity check(s) */ if (alp == NULL) return AL_RC(AL_ERR_ARG); /* allocate and initialize new assembly line object */ /* XXX - what malloc ? */ if ((al = (al_t *)malloc(sizeof(al_t))) == NULL) return AL_RC(AL_ERR_MEM); LISTINIT(al,chunks); al->bytes = 0; /* memory policy */ al->m.malloc = malloc; /* structure allocation */ al->m.free = free; al->m.balloc = malloc; /* buffer allocation */ al->m.bfree = free; #ifdef TEST al->m.new_buffersize = 42; #else al->m.new_buffersize = 4096; #endif al->m.max_freechunks = 500; /* pass object to caller */ *alp = al; return AL_OK; } al_rc_t al_destroy(al_t *al) { al_chunk_t *cur, *pred; /* argument sanity check(s) */ if (al == NULL) return AL_RC(AL_ERR_ARG); /* free chunks and backing store */ FOREACHD(al,chunks,cur,pred) { REMOVE(al,chunks,cur); dispose_chunk(al,cur); } /* free object itself */ /* XXX - which free() ? */ free(al); return AL_OK; } /* * copy bytes into buffer, FIFO * * stops copy operation when a new buffer cannot be created * but leaves data structure consistent */ al_rc_t al_append_bytes(al_t *al, const char *src, size_t n, al_label_t label) { al_rc_t rc; al_chunk_t *cur; al_buffer_t *buf; size_t res, step; char *dst; /* argument sanity check(s) */ if (al == NULL || src == NULL) return AL_RC(AL_ERR_ARG); cur = TAIL(al,chunks); res = AL_CHUNK_RESERVE(cur,label); while (n > 0) { if (res == 0) { rc = new_buffer(al, &buf); if (rc != AL_OK) return AL_RC(rc); rc = new_chunk(al,buf,label,&cur); if (rc != AL_OK) return AL_RC(rc); res = AL_CHUNK_RESERVE(cur,label); ADDTAIL(al, chunks, cur); } step = n; if (step > res) step = res; dst = AL_CHUNK_PTR(cur, AL_CHUNK_LEN(cur)); memcpy(dst, src, step); src += step; AL_RESIZE(al, cur, step); n -= step; res = AL_CHUNK_RESERVE(cur,label); } return AL_OK; } /* * copy bytes into buffer, LIFO * * stops copy operation when a new buffer cannot be created * but leaves data structure consistent */ al_rc_t al_prepend_bytes(al_t *al, const char *src, size_t n, al_label_t label) { al_rc_t rc; al_chunk_t *cur; al_buffer_t *buf; size_t res, step; char *dst; /* argument sanity check(s) */ if (al == NULL || src == NULL) return AL_RC(AL_ERR_ARG); cur = HEAD(al,chunks); res = AL_CHUNK_PRESERVE(cur,label); src += n; while (n > 0) { if (res == 0) { rc = new_buffer(al, &buf); if (rc != AL_OK) return AL_RC(rc); rc = new_chunk(al,buf,label,&cur); if (rc != AL_OK) return AL_RC(rc); res = AL_CHUNK_PRESERVE(cur,label); ADDHEAD(al, chunks, cur); } step = n; if (step > res) step = res; src -= step; AL_PRESIZE(al, cur, step); n -= step; res = AL_CHUNK_PRESERVE(cur,label); dst = AL_CHUNK_PTR(cur, 0); memcpy(dst, src, step); } return AL_OK; } /* * append a caller supplied buffer * * buffer must exist until list is destroyed * XXX - buffer can have multiple refs caused by splice operations * XXX - some list operations modify the buffer * */ al_rc_t al_attach_buffer(al_t *al, char *p, size_t n, al_label_t label, void (*freemem_cb)(char *, size_t, void *), void *u) { al_rc_t rc; al_buffer_t *buf; al_chunk_t *cur; /* argument sanity check(s) */ if (al == NULL || p == NULL || n <= 0) return AL_RC(AL_ERR_ARG); rc = make_buffer(al, p, n, freemem_cb, u, &buf); if (rc != AL_OK) return AL_RC(rc); rc = new_chunk(al,buf,label,&cur); if (rc != AL_OK) return AL_RC(rc); ADDTAIL(al, chunks, cur); /* validate data in buffer */ AL_RESIZE(al, cur, n); return AL_OK; } /* * this is the central function to manipulate assembly line * * cut arbitrary spans from list into a destination buffer (or drop it) * insert (destructive move) content from another list into the cut * * this is analog to perls splice function, except that * -> the output is not stored in the target buffer but is appended * -> the replacement data is moved and not copied. * */ al_rc_t al_splice(al_t *al, size_t off, size_t n, al_t *nal, al_t *tal) { al_rc_t rc; al_chunk_t *cur, *start, *end, *next, *ins, *splitbuf; size_t pos, skip, len, step; int doinsert; /* argument sanity check(s) */ if (al == NULL) return AL_RC(AL_ERR_ARG); /* optimization: treat an empty list as no insertion at all */ doinsert = (nal != NULL) && !ISEMPTY(nal,chunks); /* * seek to beginning, return EOF when seek position does not exist * EOD must be a valid seek position so that we can append data */ rc = al_seek(al, off, &cur, &skip); if (rc != AL_OK) return AL_RC(rc); /* * remember insertion point * * caveat: if the first chunk is removed from input the insertion * point shifts to the next chunk (or EOD -> NULL pointer) */ ins = cur; /* * the inseration point is either behind the list or * within the current chunk * * if it is behind the list: * -> insert operation switches to append mode * * if the insertion point is at the beginning of the current chunk: * -> insertion point moves later if the chunk is removed * * if the inseration point is in the middle of the current chunk: * -> chunk is split into two so that the insertion * point is at the beginning of the second part * * insertion point cannot be at EOD of the chunk * * splitting at this point preserves all data in case the * allocation of the split buffer fails */ if (doinsert) { if (ins != NULL && skip > 0) { rc = split_chunk(al, ins, skip, &splitbuf); if (rc != AL_OK) return AL_RC(rc); INSERT(al,chunks,ins,splitbuf); skip = 0; } } /* * as long as there is data to move */ pos = off; while (n > 0 && cur != NULL) { next = NEXT(cur, chunks); len = AL_CHUNK_LEN(cur); /* * check if partial chunk was selected * if skip > 0 we have to preserve bytes at the beginning * if skip == 0 && n < len-skip we have to preserve bytes at the end */ if (skip > 0 || n < len - skip) { /* compute number of bytes to process */ step = AL_CHUNK_SPAN(cur, skip, n); /* copy data to target */ if (tal != NULL) { /* * XXX - this operation can fail */ size_t before = tal->bytes; rc = al_append_bytes(tal, AL_CHUNK_PTR(cur, skip), step, AL_CHUNK_LABEL(cur)); if (rc != AL_OK) /* correct step size to actual size */ step = tal->bytes - before; } else rc = AL_OK; /* * cut span from src chunk * if skip == 0, simply shrink the chunk from the beginning * if skip > 0, compute number of bytes to preserve, * align buffer and shrink chunk from the end */ if (skip == 0) AL_PRESIZE(al, cur, -step); else { /* align trailing bytes */ if (len > (skip+step)) { memmove( AL_CHUNK_PTR(cur, skip), AL_CHUNK_PTR(cur, skip+step), len - (skip+step) ); } AL_RESIZE(al, cur, -step); } /* * bail out from failed al_append_bytes operation above * */ if (rc != AL_OK) return AL_RC(rc); } else { /* * the whole chunk has to be moved, * * scan ahead for more such chunks to unlink * and relink a whole chain in a single operation * * move the chunks to the target chain * manual accounting for total size */ /* * when the insertion chunk is removed, we have to adjust * the insertion point * * if the insertion chunk was the last chunk we get a NULL * next pointer and the insertion method switches to append * mode */ step = len; start = cur; end = cur; if (cur == ins) ins = next; while (next && step + (len = AL_CHUNK_LEN(next)) <= n) { step += len; end = next; next = NEXT(next, chunks); if (end == ins) ins = next; } REMOVE2(al, chunks, start, end); al->bytes -= step; if (tal == NULL) { do { cur = start; start = NEXT(cur, chunks); dispose_chunk(al, cur); } while (cur != end); } else { ADDTAIL2(tal, chunks, start, end); tal->bytes += step; } } n -= step; pos += step; cur = next; skip = 0; } /* * now splice in replacement chunks */ if (doinsert) { if (ins != NULL) { /* * complex case where list is inserted * * the original list has already been modified so * that we can simply insert between two chunks */ INSERTLIST(al,chunks,ins,nal); } else { /* * simple case where list end is 'replaced' */ APPENDLIST(al,chunks,nal); } al->bytes += nal->bytes; nal->bytes = 0; } return AL_OK; } /* * */ al_rc_t al_setlabel(al_t *al, size_t off, size_t n, al_label_t oldlabel, al_label_t newlabel) { al_rc_t rc; al_chunk_t *cur, *splitbuf; size_t skip, len; /* argument sanity check(s) */ if (al == NULL) return AL_RC(AL_ERR_ARG); /* * seek to beginning, return EOF when seek position does not exist * EOD must be a valid seek position so that we can append data */ rc = al_seek(al, off, &cur, &skip); if (rc != AL_OK) return AL_RC(rc); /* * seek to EOD, nothing to label */ if (cur == NULL) return AL_OK; /* * if first chunk doesn't need relabeling * then skip it, adjust seek size * * else if offset is not at chunk start * then split chunk at offset, continue * with second half */ if (!AL_SAME_LABEL(cur, oldlabel) || AL_SAME_LABEL(cur, newlabel)) { len = AL_CHUNK_LEN(cur) - skip; n = n < len ? 0 : n - len; cur = NEXT(cur, chunks); } else if (skip > 0) { rc = split_chunk(al, cur, skip, &splitbuf); if (rc != AL_OK) return AL_RC(rc); INSERT(al,chunks,cur,splitbuf); } /* * for all remaining chunks and bytes * * if chunk doesn't need relabeling * then skip it, adjust size * * else if chunk isn't covered in total * then split chunk at end offset and * process first half */ while (n > 0 && cur != NULL) { len = AL_CHUNK_LEN(cur); if (!AL_SAME_LABEL(cur, oldlabel) || AL_SAME_LABEL(cur, newlabel)) { n = n < len ? 0 : n - len; } else { if (n < len) { /* * split last chunk at end offset */ rc = split_chunk(al, cur, n, &splitbuf); if (rc != AL_OK) return AL_RC(rc); INSERT(al,chunks,cur,splitbuf); cur = splitbuf; len = AL_CHUNK_LEN(cur); } AL_CHUNK_LABEL(cur) = newlabel; n -= len; } cur = NEXT(cur, chunks); } return AL_OK; } /* * assembly line traversal requires a context. It needs to be * malloced to keep its inner structure private */ al_rc_t al_txalloc(al_t *al, al_tx_t **txp) { al_tx_t *tx; tx = (al_tx_t*)(al->m.malloc)(sizeof(al_tx_t)); if (tx == NULL) return AL_RC(AL_ERR_MEM); *txp = tx; return AL_OK; } /* * free traversal context using proper policy function */ al_rc_t al_txfree(al_t *al, al_tx_t *tx) { (al->m.free)(tx); return AL_OK; } /* * initiate assembly line traversal * * - do initial seek, fail if position does not exist * - save traversal parameters */ al_rc_t al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_label_t label, al_tx_t *tx) { al_rc_t rc; tx->cur = NULL; rc = al_seek(al, off, &tx->cur, &tx->skip); if (rc != AL_OK) return AL_RC(rc); tx->dir = dir; tx->togo = n; tx->label = label; return AL_OK; } /* * assembly line traversal step * * return EOF if at end of assembly line * clip view chunk to traversal bounds * advance chunk cursor according to traversal direction */ al_rc_t al_traverse_next(al_t *al, al_tx_t *tx, al_chunk_t **alcp) { size_t step; do { if (tx->togo <= 0) /* XXX - togo can be negative from bad input */ return AL_ERR_EOF; if (tx->cur == NULL) /* premature EOF */ return AL_ERR_EOF; /* compute number of bytes to process */ step = AL_CHUNK_SPAN(tx->cur, tx->skip, tx->togo); /* * synthetic chunk which is NOT maintained in usecount * MUST NOT BE USED for modifications to chunk list * MUST NOT BE USED for modifications to chunk size * ALLOWED is read access to chunk content * ALLOWED is modification in place of chunk content */ tx->view = *(tx->cur); tx->view.begin += tx->skip; tx->view.end = tx->view.begin + step; switch (tx->dir) { case AL_FORWARD: case AL_BACKWARD: break; case AL_FORWARD_SPAN: case AL_BACKWARD_SPAN: if (!AL_SAME_LABEL(&tx->view, tx->label)) { tx->togo = 0; return AL_ERR_EOF; } break; } switch (tx->dir) { case AL_FORWARD: case AL_FORWARD_SPAN: tx->cur = NEXT(tx->cur,chunks); tx->togo -= step; tx->skip = 0; break; case AL_BACKWARD: case AL_BACKWARD_SPAN: tx->cur = PREV(tx->cur,chunks); tx->togo -= step; tx->skip = 0; break; } } while (!AL_SAME_LABEL(&tx->view, tx->label)); *alcp = &tx->view; return AL_OK; } /* * assembly line traversal end * * to free resources allocated/locked during traversal * currently a NOOP */ al_rc_t al_traverse_end(al_t *al, al_tx_t *tx, int final) { return AL_OK; } /* * full assembly line traversal with callback * * traversal is aborted when callback return != AL_OK * reaching EOF (and also aborting with AL_ERR_EOF) is not an error * * traversal context is kept on stack (XXX ?) */ al_rc_t al_traverse_cb(al_t *al, size_t off, size_t n, al_td_t dir, al_label_t label, al_rc_t (*cb)(al_chunk_t *, void *), void *u) { al_rc_t rc; al_tx_t tx; /* XXX - private tx structure on stack */ al_chunk_t *view; rc = al_traverse(al, off, n, dir, label, &tx); if (rc != AL_OK) return AL_RC(rc); while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) { rc = cb(view, u); if (rc != AL_OK) break; } al_traverse_end(al, &tx, 1); if (rc != AL_ERR_EOF) return AL_RC(rc); return AL_OK; } /* * full assembly line traversal with data copy to linear buffer * * returns actual number of bytes copied * * Do not copy if destination pointer is NULL, but still count. * This can be used to precalculate the size of the needed linear * buffer with n set to some arbitrary huge value. * * traversal context is kept on stack (XXX ?) */ al_rc_t al_flatten(al_t *al, size_t off, size_t n, al_td_t dir, al_label_t label, char *dst, size_t *lenp) { al_rc_t rc; al_tx_t tx; /* XXX - private tx structure on stack */ al_chunk_t *view; size_t step, total; *lenp = 0; /* keep caller on safe side */ rc = al_traverse(al, off, n, dir, label, &tx); if (rc != AL_OK) return AL_RC(rc); switch (dir) { case AL_FORWARD: case AL_FORWARD_SPAN: break; case AL_BACKWARD: case AL_BACKWARD_SPAN: dst += n; break; } total = 0; if (dst == NULL) { while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) total += AL_CHUNK_LEN(view); } else { while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) { step = AL_CHUNK_LEN(view); switch (dir) { case AL_FORWARD: case AL_FORWARD_SPAN: memmove(dst, AL_CHUNK_PTR(view, 0), step); dst += step; break; case AL_BACKWARD: case AL_BACKWARD_SPAN: dst -= step; memmove(dst, AL_CHUNK_PTR(view, 0), step); break; } total += step; } } *lenp = total; al_traverse_end(al, &tx, 1); if (rc != AL_ERR_EOF) return AL_RC(rc); return AL_OK; } /* * full assembly line traversal with data copy to target assembly line * * traversal context is kept on stack (XXX ?) */ al_rc_t al_copy(al_t *al, size_t off, size_t n, al_label_t label, al_t *tal) { al_rc_t rc; al_tx_t tx; /* XXX - private tx structure on stack */ al_chunk_t *view; size_t step; rc = al_traverse(al, off, n, AL_FORWARD, label, &tx); if (rc != AL_OK) return AL_RC(rc); while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) { step = AL_CHUNK_LEN(view); al_append_bytes(tal, AL_CHUNK_PTR(view, 0), step, AL_CHUNK_LABEL(view)); } al_traverse_end(al, &tx, 1); if (rc != AL_ERR_EOF) return AL_RC(rc); return AL_OK; } /* * traverse assembly line and retrieve first chunk * * traversal context is kept on stack (XXX ?) */ al_rc_t al_firstlabel(al_t *al, size_t off, size_t n, al_td_t dir, al_label_t label, al_label_t *labelp) { al_rc_t rc; al_tx_t tx; /* XXX - private tx structure on stack */ al_chunk_t *view; rc = al_traverse(al, off, n, dir, label, &tx); if (rc != AL_OK) return AL_RC(rc); if ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) *labelp = AL_CHUNK_LABEL(view); al_traverse_end(al, &tx, 1); return rc; } /* * traverse assembly line forward and search first chunk * that matches label and continue search until end of * span of same label * * return offset to first byte in *offp * return size of span to last byte in *spanp * * traversal context is kept on stack (XXX ?) */ al_rc_t al_spanlabel(al_t *al, size_t off, size_t n, al_label_t label, size_t *offp, size_t *spanp) { al_rc_t rc; al_tx_t tx; /* XXX - private tx structure on stack */ al_chunk_t *view; size_t len, total, start; int have_first; /* * we need to track absolute traversal position, * so we have to see all chunks.. no filtering * allowed */ rc = al_traverse(al, off, n, AL_FORWARD, NULL, &tx); if (rc != AL_OK) return AL_RC(rc); have_first = 0; start = 0; total = 0; while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) { len = AL_CHUNK_LEN(view); if (AL_SAME_LABEL(view, label)) { if (!have_first) { start = total; have_first = 1; } } else if (have_first) break; total += len; } al_traverse_end(al, &tx, 1); if (rc != AL_OK && rc != AL_ERR_EOF) return AL_RC(rc); if (!have_first) return AL_RC(AL_ERR_EOF); *offp = off + start; *spanp = total - start; return AL_OK; } /* * relay macros to caller * * al_bytes - total number of bytes in assembly line * al_chunk_len - number of bytes in chunk * al_chunk_span - clip interval (off,off+n( against chunk length * al_chunk_label- return chunk label * al_same_label - check if label matches with chunk label * al_chunk_ptr - return memory pointer to byte within chunk * * off must be a valid offset within (0,len( */ size_t al_bytes(const al_t *al) { return al->bytes; } size_t al_chunk_len(al_chunk_t *alc) { return AL_CHUNK_LEN(alc); } size_t al_chunk_span(al_chunk_t *alc, size_t off, size_t n) { return AL_CHUNK_SPAN(alc, off, n); } al_label_t al_chunk_label(al_chunk_t *alc) { return AL_CHUNK_LABEL(alc); } int al_same_label(al_chunk_t *alc, al_label_t label) { return AL_SAME_LABEL(alc, label); } char * al_chunk_ptr(al_chunk_t *alc, size_t off) { return AL_CHUNK_PTR(alc, off); } /* * convert error code into human readable form */ const char * al_error(al_rc_t rc) { const char *mess; switch (rc) { case AL_OK: mess = "Everything Ok"; break; case AL_ERR_ARG: mess = "Invalid Argument"; break; case AL_ERR_MEM: mess = "Not Enough Memory"; break; case AL_ERR_EOF: mess = "End Of Data"; break; case AL_ERR_INT: mess = "Internal Error"; break; default: mess = "Invalid Result Code"; break; } return mess; } @ 1.40 log @get rid of superflous unsigned<0 tests PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @@ 1.39 log @make al_setlabel conditional (use oldlabel == NULL for unconditional behavíour) PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d490 1 a490 1 if (al == NULL || src == NULL || n < 0) d539 1 a539 1 if (al == NULL || src == NULL || n < 0) d627 1 a627 1 if (al == NULL || n < 0) d832 1 a832 1 if (al == NULL || n < 0) @ 1.38 log @new al_spanlabel method PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d824 2 a825 1 al_setlabel(al_t *al, size_t off, size_t n, al_label_t label) d857 2 a858 1 if (AL_SAME_LABEL(cur, label)) { d881 2 a882 1 if (AL_SAME_LABEL(cur, label)) { d896 1 a896 1 AL_CHUNK_LABEL(cur) = label; @ 1.37 log @new al_setlabel method to modify the data label within an assembly line. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d1189 58 @ 1.36 log @buffers can move to different assembly lines so that the freemem function can reference a dangling al pointer to reach bfree(). We now reference the bfree() function directly as this is supposed to remain valid even after an assembly line has been freed. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d821 82 @ 1.35 log @remove remaining GCC warnings @ text @d170 2 a171 2 al_t *al = (al_t *)u; (al->m.bfree)(p); d194 1 a194 1 buf->userdata = (void *)al; @ 1.34 log @size_t is unsigned -> get rid of possible negative offsets that weren't used anywhere. remove sanity checks for 'unsigned < 0'. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d222 1 a222 1 al_rc_t make_buffer(al_t *al, char *p, size_t n, void (*freemem)(char *, size_t, void *), void *u, al_buffer_t **bufp) d231 1 a231 1 buf->freemem = freemem; d583 1 a583 1 void (*freemem)(char *, size_t, void *), void *u) d593 1 a593 1 rc = make_buffer(al, p, n, freemem, u, &buf); @ 1.33 log @prepending to a zero length chunk is now possible, the chunk is aligned to the end of the buffer PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d294 1 a294 1 if (off < 0 || off > len) a328 1 * if off is negative, treat it as relative offset from the end a339 3 if (off < 0) /* off is negative */ off += al->bytes; @ 1.32 log @optimize al_splice to unlink/relink several chunks in a single step added necessary list macros to list.h PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d140 2 d147 3 a149 1 : (alc)->begin) \ d152 5 a156 1 /* grow chunk to the beginning */ d158 5 a162 1 do { (alc)->begin -= n; (al)->bytes += (n); } while (0) @ 1.31 log @new span traversal mode add direction to al_flatten to support span traversal utility method al_firstlabel PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d614 1 a614 1 al_chunk_t *cur, *next, *ins, *splitbuf; d733 6 a738 2 * the whole chunk has to be moved, simply move it * to the target chain a740 2 step = len; REMOVE(al, chunks, cur); d750 5 d758 9 d768 8 a775 4 if (tal == NULL) dispose_chunk(al, cur); else { ADDTAIL(tal, chunks, cur); @ 1.30 log @seeking backwards to EOF position is obviously ok as well PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d582 1 a582 1 if (al == NULL || p == NULL || n < 0) d877 13 d891 1 d897 1 d966 1 a966 1 al_flatten(al_t *al, size_t off, size_t n, al_label_t label, d976 1 a976 1 rc = al_traverse(al, off, n, AL_FORWARD, label, &tx); d980 10 a990 2 while ((rc = al_traverse_next(al, &tx, &view)) == AL_OK) { step = AL_CHUNK_LEN(view); d992 19 a1010 3 if (dst != NULL) { memmove(dst, AL_CHUNK_PTR(view, 0), step); dst += step; a1011 2 total += step; d1051 25 @ 1.29 log @significant API change. now support data labels. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d357 6 @ 1.28 log @prepend used wrong macros (cut&paste error) traverse_next didn't compute step correctly and didn't clear skip from initial seek when moving to next/previous chunk PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d79 1 d95 1 d112 8 d125 1 a125 1 #define AL_CHUNK_RESERVE(alc) \ d127 1 a127 1 ? ( (alc)->buf->usecount > 1 \ d141 1 a141 1 #define AL_CHUNK_PRESERVE(alc) \ d143 1 a143 1 ? ( (alc)->buf->usecount > 1 \ d243 1 a243 1 new_chunk(al_t *al, al_buffer_t *buf, al_chunk_t **alcp) d267 1 d285 1 a285 1 rc = new_chunk(al, orig->buf, &alc); d304 1 d384 1 a384 1 printf(" C: %p (%d @@ %p + %d < %d (use=%d))\n", d386 1 d467 1 a467 1 al_append_bytes(al_t *al, const char *src, size_t n) d480 1 a480 1 res = AL_CHUNK_RESERVE(cur); d487 1 a487 1 rc = new_chunk(al,buf,&cur); d490 1 a490 1 res = AL_CHUNK_RESERVE(cur); d503 1 a503 1 res = AL_CHUNK_RESERVE(cur); d516 1 a516 1 al_prepend_bytes(al_t *al, const char *src, size_t n) d529 1 a529 1 res = AL_CHUNK_PRESERVE(cur); d538 1 a538 1 rc = new_chunk(al,buf,&cur); d541 1 a541 1 res = AL_CHUNK_PRESERVE(cur); d551 1 a551 1 res = AL_CHUNK_PRESERVE(cur); d568 2 a569 1 al_rc_t al_attach_buffer(al_t *al, char *p, size_t n, void (*freemem)(char *, size_t, void *), void *u) d582 1 a582 1 rc = new_chunk(al,buf, &cur); d689 2 a690 1 rc = al_append_bytes(tal, AL_CHUNK_PTR(cur, skip), step); d820 1 a820 1 al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_tx_t *tx) d830 3 a832 2 tx->dir = dir; tx->togo = n; d849 3 a851 2 if (tx->togo <= 0) /* XXX - togo can be negative from bad input */ return AL_ERR_EOF; d853 2 a854 2 if (tx->cur == NULL) /* premature EOF */ return AL_ERR_EOF; d856 2 a857 2 /* compute number of bytes to process */ step = AL_CHUNK_SPAN(tx->cur, tx->skip, tx->togo); d859 24 a882 23 /* * synthetic chunk which is NOT maintained in usecount * MUST NOT BE USED for modifications to chunk list * MUST NOT BE USED for modifications to chunk size * ALLOWED is read access to chunk content * ALLOWED is modification in place of chunk content */ tx->view = *(tx->cur); tx->view.begin += tx->skip; tx->view.end = tx->view.begin + step; switch (tx->dir) { case AL_FORWARD: tx->cur = NEXT(tx->cur,chunks); tx->togo -= step; tx->skip = 0; break; case AL_BACKWARD: tx->cur = PREV(tx->cur,chunks); tx->togo -= step; tx->skip = 0; break; } d908 1 a908 1 al_traverse_cb(al_t *al, size_t off, size_t n, al_td_t dir, d915 1 a915 1 rc = al_traverse(al, off, n, dir, &tx); d938 4 d945 2 a946 1 al_flatten(al_t *al, size_t off, size_t n, char *dst, size_t *lenp) d955 1 a955 1 rc = al_traverse(al, off, n, AL_FORWARD, &tx); d962 6 a967 2 memmove(dst, AL_CHUNK_PTR(view, 0), step); dst += step; d986 1 a986 1 al_copy(al_t *al, size_t off, size_t n, al_t *tal) d993 1 a993 1 rc = al_traverse(al, off, n, AL_FORWARD, &tx); d999 1 a999 1 al_append_bytes(tal, AL_CHUNK_PTR(view, 0), step); d1016 2 d1039 12 @ 1.27 log @beautification PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d536 1 a536 1 AL_RESIZE(al, cur, step); d538 1 a538 1 res = AL_CHUNK_RESERVE(cur); d839 2 a840 3 step = AL_CHUNK_LEN(tx->cur); if (step > tx->togo) step = tx->togo; d857 1 d862 1 @ 1.26 log @correctly initialize callback userdata get rid of debug printf PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d85 2 a86 2 void (*freemem)(char *p, size_t n, void *u); /* callback function to reclaim memory when it is no longer referenced */ void *userdata; /* arbitrary pointer to pass context data to the callback function */ @ 1.25 log @after splitting initial chunk, the split offset ("skip") needs to be reset to zero. PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @a147 1 printf("*** FREEMEM(LIBRARY) buffer %p size %d\n",p,n); d172 1 a172 1 buf->userdata = NULL; @ 1.24 log @change in al_attach_buffer API back out rse's change to al_traverse parameters. parameter order is: object ptr(if any), input parameters, output parameters PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d647 1 @ 1.23 log @Undocumented internal code is usually always ok (at least as long as it is self-describing enough ;-), but undocumented internal data is most of the time nasty. It especially makes it hard to review anything in-depth because only the author has all of the gory details in mind all the time. Hence internal data should be documented at least a little bit to make clear for what each structure member is for. So, here it is. Feel free to fix my quick descriptions. @ text @d85 2 a86 1 int freemem; /* boolean flag whether chunk of data has to be free(3)ed */ d143 11 d172 2 a173 1 buf->freemem = 1; d189 1 a189 1 (al->m.bfree)(buf->mem); d200 2 a201 2 static al_rc_t make_buffer(al_t *al, char *p, size_t n, al_buffer_t **bufp) d210 2 a211 1 buf->freemem = 0; d556 1 a556 2 al_rc_t al_attach_buffer(al_t *al, char *p, size_t n) d566 1 a566 1 rc = make_buffer(al, p, n, &buf); d805 1 a805 1 al_traverse(al_t *al, al_tx_t *tx, size_t off, size_t n, al_td_t dir) d896 1 a896 1 rc = al_traverse(al, &tx, off, n, dir); d931 1 a931 1 rc = al_traverse(al, &tx, off, n, AL_FORWARD); d965 1 a965 1 rc = al_traverse(al, &tx, off, n, AL_FORWARD); @ 1.22 log @Be pedantic about consistent argument orders and move al_tx_t argument to second position where it is for all other al_tx_t based functions: -al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_tx_t *tx) +al_traverse(al_t *al, al_tx_t *tx, size_t off, size_t n, al_td_t dir) @ text @d60 6 a65 6 void *(*malloc)(size_t); void (*free)(void *); void *(*balloc)(size_t); void (*bfree)(void *); size_t new_buffersize; int max_freechunks; d69 3 a71 3 LIST(al_chunk_t) chunks; size_t bytes; al_memops_t m; d75 4 a78 3 NODE(al_chunk_t) chunks; al_buffer_t *buf; size_t begin, end; d82 4 a85 4 char *mem; size_t size; int usecount; int freemem; d89 5 a93 5 al_td_t dir; al_chunk_t *cur; size_t skip; size_t togo; al_chunk_t view; @ 1.21 log @Before diving into the semantics, bring syntax into standard OSSP shape by applying the following cosmetics: - add newlines between "if" and "return" at some statements - make sure indentation is exactly 4 spaces (were 8 at dispose_chunk, etc) - consistently use no braces for "if" or "else" clauses if only single statement exists in body. - we do not indicate the not-used-return-code via (void) on statements, because both every compiler and human is smart enough to see the intention. THERE ARE NO FUNCTIONAL CHANGES IN THIS COMMIT AT ALL. @ text @d791 1 a791 1 al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_tx_t *tx) d882 1 a882 1 rc = al_traverse(al, off, n, dir, &tx); d917 1 a917 1 rc = al_traverse(al, off, n, AL_FORWARD, &tx); d951 1 a951 1 rc = al_traverse(al, off, n, AL_FORWARD, &tx); @ 1.20 log @added al_traverse_end to API, to allow future cleanup/locking PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d105 2 a106 2 ((n) > AL_CHUNK_LEN(alc) - (off) ? \ AL_CHUNK_LEN(alc) - (off) : (n)) d114 5 a118 3 ((alc) \ ? (alc)->buf->usecount > 1 ? 0 : (alc)->buf->size - (alc)->end \ : 0) d130 5 a134 3 ((alc) \ ? (alc)->buf->usecount > 1 ? 0 : (alc)->begin \ : 0) d145 2 a146 2 static al_rc_t new_buffer(al_t *al, al_buffer_t **bufp) d167 2 a168 2 static al_rc_t dispose_buffer(al_t *al, al_buffer_t *buf) d186 2 a187 2 static al_rc_t make_buffer(al_t *al, char *p, size_t n, al_buffer_t **bufp) d213 1 a213 2 static struct { d216 1 a216 2 static int alc_freecount = 0; d218 2 a219 2 static al_rc_t new_chunk(al_t *al, al_buffer_t *buf, al_chunk_t **alcp) d244 1 a244 1 ++buf->usecount; d250 2 a251 2 static al_rc_t split_chunk(al_t *al, al_chunk_t *orig, size_t off, al_chunk_t **alcp) d261 2 a262 1 if (rc != AL_OK) return rc; d272 2 a273 2 static void dispose_chunk(al_t *al, al_chunk_t *alc) d275 12 a286 12 --alc->buf->usecount; if (alc->buf->usecount == 0) dispose_buffer(al,alc->buf); alc->buf = NULL; /* stop freelist from growing infinitely */ if (alc_freecount >= al->m.max_freechunks) { (al->m.free)(alc); } else { ADDTAIL(&alc_freelist, chunks, alc); ++alc_freecount; } d297 2 a298 2 static al_rc_t al_seek(al_t *al, size_t off, al_chunk_t **alcp, size_t *skipp) d349 2 a350 2 static void dump(al_t *al) d376 2 a377 1 al_rc_t al_create(al_t **alp) d383 1 a383 1 return AL_RC(AL_ERR_ARG); d388 1 a388 1 return AL_RC(AL_ERR_MEM); d411 2 a412 1 al_rc_t al_destroy(al_t *al) d418 1 a418 1 return AL_RC(AL_ERR_ARG); d439 2 a440 1 al_rc_t al_append_bytes(al_t *al, const char *src, size_t n) d450 1 a450 1 return AL_RC(AL_ERR_ARG); d458 2 a459 1 if (rc != AL_OK) return AL_RC(rc); d461 2 a462 1 if (rc != AL_OK) return AL_RC(rc); d488 2 a489 1 al_rc_t al_prepend_bytes(al_t *al, const char *src, size_t n) d499 1 a499 1 return AL_RC(AL_ERR_ARG); d509 2 a510 1 if (rc != AL_OK) return AL_RC(rc); d512 2 a513 1 if (rc != AL_OK) return AL_RC(rc); d541 2 a542 1 al_rc_t al_attach_buffer(al_t *al, char *p, size_t n) d550 1 a550 1 return AL_RC(AL_ERR_ARG); d553 2 a554 1 if (rc != AL_OK) return AL_RC(rc); d556 2 a557 1 if (rc != AL_OK) return AL_RC(rc); d577 2 a578 1 al_rc_t al_splice(al_t *al, size_t off, size_t n, al_t *nal, al_t *tal) d587 1 a587 1 return AL_RC(AL_ERR_ARG); d597 3 a599 2 if (rc != AL_OK) return AL_RC(rc); d630 2 a631 1 if (rc != AL_OK) return AL_RC(rc); d662 1 a662 1 if (rc != AL_OK) { a664 1 } d666 1 a666 1 } else { a667 1 } d675 1 a675 1 if (skip == 0) { d677 1 a677 1 } else { d693 4 a696 4 if (rc != AL_OK) return AL_RC(rc); } else { d717 1 a717 1 if (tal == NULL) { d719 1 a719 1 } else { a722 1 a733 1 d761 2 a762 1 al_rc_t al_txalloc(al_t *al, al_tx_t **txp) d777 2 a778 1 al_rc_t al_txfree(al_t *al, al_tx_t *tx) d790 2 a791 1 al_rc_t al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_tx_t *tx) d798 2 a799 1 if (rc != AL_OK) return AL_RC(rc); d814 2 a815 1 al_rc_t al_traverse_next(al_t *al, al_tx_t *tx, al_chunk_t **alcp) d841 8 a848 8 case AL_FORWARD: tx->cur = NEXT(tx->cur,chunks); tx->togo -= step; break; case AL_BACKWARD: tx->cur = PREV(tx->cur,chunks); tx->togo -= step; break; d874 3 a876 2 al_rc_t al_traverse_cb(al_t *al, size_t off, size_t n, al_td_t dir, al_rc_t (*cb)(al_chunk_t *, void *), void *u) d883 2 a884 1 if (rc != AL_OK) return AL_RC(rc); d892 1 a892 1 (void) al_traverse_end(al, &tx, 1); d907 2 a908 1 al_rc_t al_flatten(al_t *al, size_t off, size_t n, char *dst, size_t *lenp) d918 2 a919 1 if (rc != AL_OK) return AL_RC(rc); d924 1 a924 1 memmove(dst, AL_CHUNK_PTR(view,0), step); d930 1 a930 1 (void) al_traverse_end(al, &tx, 1); d943 2 a944 1 al_rc_t al_copy(al_t *al, size_t off, size_t n, al_t *tal) d952 2 a953 1 if (rc != AL_OK) return AL_RC(rc); d957 1 a957 1 al_append_bytes(tal, AL_CHUNK_PTR(view,0), step); d960 1 a960 1 (void) al_traverse_end(al, &tx, 1); d978 3 a980 1 size_t al_bytes(const al_t *al) d984 3 a986 1 size_t al_chunk_len(al_chunk_t *alc) d990 3 a992 1 size_t al_chunk_span(al_chunk_t *alc, size_t off, size_t n) d996 3 a998 1 char *al_chunk_ptr(al_chunk_t *alc, size_t off) d1006 2 a1007 1 const char *al_error(al_rc_t rc) d1012 6 a1017 6 case AL_OK: mess = "Everything Ok"; break; case AL_ERR_ARG: mess = "Invalid Argument"; break; case AL_ERR_MEM: mess = "Not Enough Memory"; break; case AL_ERR_EOF: mess = "End Of Data"; break; case AL_ERR_INT: mess = "Internal Error"; break; default: mess = "Invalid Result Code"; break; @ 1.19 log @moved generic list macros to separate header PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d838 11 d872 2 d908 2 d935 2 @ 1.18 log @Ok, as arranged with Michael: use 'assembly line' as the official long name @ text @d39 1 a39 132 /****************************************************************************/ #define LIST(elem) \ struct { elem *head, *tail; } #define NODE(elem) \ struct { elem *next, *prev; } #define HEAD(q,l) ((q)->l.head) #define TAIL(q,l) ((q)->l.tail) #define NEXT(n,l) ((n)->l.next) #define PREV(n,l) ((n)->l.prev) #define ISEMPTY(q,l) (HEAD(q,l) == NULL) #define LISTINIT(q,l) \ do { \ HEAD(q,l) = NULL; \ TAIL(q,l) = NULL; \ } while(0) #define NODEINIT(n,l) \ do { \ NEXT(n,l) = NULL; \ PREV(n,l) = NULL; \ } while(0) #define ADDTAIL(q,l,n) \ do { \ if (TAIL(q,l)) { \ NEXT(TAIL(q,l),l) = (n); \ PREV(n,l) = TAIL(q,l); \ } else { \ PREV(n,l) = NULL; \ HEAD(q,l) = (n); \ } \ TAIL(q,l) = (n); \ NEXT(n,l) = NULL; \ } while (0) #define ADDHEAD(q,l,n) \ do { \ if (HEAD(q,l)) { \ PREV(HEAD(q,l),l) = (n); \ NEXT(n,l) = HEAD(q,l); \ } else { \ NEXT(n,l) = NULL; \ TAIL(q,l) = (n); \ } \ HEAD(q,l) = (n); \ PREV(n,l) = NULL; \ } while (0) #define REMHEAD(q,l,n) \ do { \ (n) = HEAD(q,l); \ if (n) { \ HEAD(q,l) = NEXT(n,l); \ if (HEAD(q,l)) \ PREV(HEAD(q,l),l) = NULL; \ else \ TAIL(q,l) = NULL; \ } \ } while(0) #define REMTAIL(q,l,n) \ do { \ (n) = TAIL(q,l); \ if (n) { \ TAIL(q,l) = PREV(n,l); \ if (TAIL(q,l)) \ NEXT(TAIL(q,l),l) = NULL; \ else \ HEAD(q,l) = NULL; \ } \ } while(0) #define REMOVE(q,l,n) \ do { \ if (PREV(n,l)) \ NEXT(PREV(n,l),l) = NEXT(n,l); \ else \ HEAD(q,l) = NEXT(n,l); \ if (NEXT(n,l)) \ PREV(NEXT(n,l),l) = PREV(n,l); \ else \ TAIL(q,l) = PREV(n,l); \ NEXT(n,l) = NULL; \ PREV(n,l) = NULL; \ } while (0) #define INSERT(q,l,i,n) \ do { \ if (PREV(i,l)) { \ NEXT(PREV(i,l),l) = (n); \ } else { \ HEAD(q,l) = (n); \ } \ PREV(n,l) = PREV(i,l); \ PREV(i,l) = (n); \ NEXT(n,l) = (i); \ } while (0) #define APPENDLIST(q1,l,q2) \ do { \ if (TAIL(q1,l)) { \ NEXT(TAIL(q1,l),l) = HEAD(q2,l); \ PREV(HEAD(q2,l),l) = TAIL(q1,l); \ } else { \ HEAD(q1,l) = HEAD(q2,l); \ } \ TAIL(q1,l) = TAIL(q2,l); \ LISTINIT(q2,l); \ } while(0) #define INSERTLIST(q1,l,i,q2) \ do { \ if (PREV(i,l)) { \ NEXT(PREV(i,l),l) = HEAD(q2,l); \ } else { \ HEAD(q1,l) = HEAD(q2,l); \ } \ PREV(HEAD(q2,l),l) = PREV(i,l); \ NEXT(TAIL(q2,l),l) = (i); \ PREV(i,l) = TAIL(q2,l); \ LISTINIT(q2,l); \ } while(0) #define FOREACH(q,l,n) for (n = HEAD(q,l); n; n = NEXT(n,l)) #define FOREACHR(q,l,n) for (n = TAIL(q,l); n; n = PREV(n,l)) #define FOREACHD(q,l,n,r) for (n = TAIL(q,l); n && (r = PREV(n,l), 1); n = r) /****************************************************************************/ @ 1.17 log @decouple pos/neg. offset handling from search direction decision @ text @d2 1 a2 1 ** OSSP al -- Assembly Lists d29 1 a29 1 ** al.c: assembly lists library implementation d501 1 a501 1 * allocate an empty assembly list d512 1 a512 1 /* allocate and initialize new assembly list object */ d684 1 a684 1 * this is the central function to manipulate assembly lists d876 1 a876 1 * list traversal requires a context. It needs to be d901 1 a901 1 * initiate list traversal d922 1 a922 1 * list traversal step d924 1 a924 1 * return EOF if at end of list d969 1 a969 1 * full list traversal with callback d999 1 a999 1 * full list traversal with data copy to linear buffer d1033 1 a1033 1 * full list traversal with data copy to target assembly list d1061 1 a1061 1 * al_bytes - total number of bytes in list @ 1.16 log @using REMTAIL ... closer approximity to the CPU @ text @d432 5 a436 1 if (off >= 0) { d457 1 a457 1 off += al->bytes; @ 1.15 log @make comment clearer about not resizing chunks in a shared buffer PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d359 5 @ 1.14 log @add the usual amount of license fun @ text @d239 5 a243 1 /* number of bytes a chunk can be grown to the end */ d253 5 a257 1 /* number of bytes a chunk can be grown to the beginning */ @ 1.13 log @use default signedness in API and internally PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d1 31 @ 1.12 log @code cleanup PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d181 1 a181 1 unsigned char *mem; @ 1.11 log @optimization for short sizes was buggy. clarify comments PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d831 1 a831 1 al_rc_t al_txalloc(al_t *al, al_tx_t **txpp) d833 1 a833 1 al_tx_t *txp; d835 2 a836 2 txp = (al_tx_t*)(al->m.malloc)(sizeof(al_tx_t)); if (txp == NULL) d839 1 a839 1 *txpp = txp; d846 1 a846 1 al_rc_t al_txfree(al_t *al, al_tx_t *txp) d848 1 a848 1 (al->m.free)(txp); d858 1 a858 1 al_rc_t al_traverse(al_t *al, size_t off, size_t n, al_td_t dir, al_tx_t *txp) d862 1 a862 1 txp->cur = NULL; d864 1 a864 1 rc = al_seek(al, off, &txp->cur, &txp->skip); d867 2 a868 2 txp->dir = dir; txp->togo = n; d880 1 a880 1 al_rc_t al_traverse_next(al_t *al, al_tx_t *txp, al_chunk_t **alcp) d884 1 a884 1 if (txp->togo <= 0) /* XXX - togo can be negative from bad input */ d887 1 a887 1 if (txp->cur == NULL) /* premature EOF */ d890 3 a892 3 step = AL_CHUNK_LEN(txp->cur); if (step > txp->togo) step = txp->togo; d901 3 a903 3 txp->view = *(txp->cur); txp->view.begin += txp->skip; txp->view.end = txp->view.begin + step; d905 1 a905 1 switch (txp->dir) { d907 2 a908 2 txp->cur = NEXT(txp->cur,chunks); txp->togo -= step; d911 2 a912 2 txp->cur = PREV(txp->cur,chunks); txp->togo -= step; d916 1 a916 1 *alcp = &txp->view; @ 1.10 log @move private al_buffer_t declaration to .c file PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d676 14 a689 3 * if the first chunk is not removed completely it needs to be * split into two halves to allow insertion of new chunks later * from nal d695 1 a695 1 if (ins != NULL && skip > 0 && skip+n < AL_CHUNK_LEN(ins)) { d774 1 a774 1 * when the insertion point is removed, we have to adjust d777 2 a778 2 * if the insertion point was the last chunk we get a NULL * next pointer and the insertion method switches to APPEND @ 1.9 log @EX support more comments insert failure path moved to top of al_splice chunk copy failure path leaves data structures intact ?! PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d156 3 @ 1.8 log @remove debugging assert() PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d510 3 d522 4 d532 1 a532 1 /* XXX handle error */ d534 1 a534 1 /* XXX handle error */ d556 3 d568 4 d580 1 a580 1 if (rc != AL_OK) return rc; d582 1 a582 1 if (rc != AL_OK) return rc; d616 4 d621 1 a621 1 if (rc != AL_OK) return rc; d623 1 a623 1 if (rc != AL_OK) return rc; d646 1 a646 1 al_chunk_t *cur, *next; d648 8 d662 9 a670 1 if (rc != AL_OK) return rc; d672 19 a690 1 /* as long as there is data to move */ d706 16 a721 2 if (tal != NULL) al_append_bytes(tal, AL_CHUNK_PTR(cur, skip), step); d743 6 d758 12 d788 1 a788 8 if (nal && !ISEMPTY(nal,chunks)) { al_chunk_t *ins; /* rewind */ rc = al_seek(al, off, &ins, &skip); if (rc != AL_OK) return rc; if (ins == NULL) { d790 1 d792 4 a795 1 * simple case where list end is 'replaced' d797 1 a797 2 APPENDLIST(al,chunks,nal); a798 1 d800 1 a800 2 * if beginning and end were in same chunk we have to split this * chunk in two for a copyless insert operation d802 1 a802 9 if (skip > 0) { al_chunk_t *twin; rc = split_chunk(al, ins, skip, &twin); if (rc != AL_OK) return rc; INSERT(al,chunks,ins,twin); } INSERTLIST(al,chunks,ins,nal); d807 1 d823 1 a823 1 return AL_ERR_MEM; d851 1 a851 1 if (rc != AL_OK) return rc; d922 1 a922 1 if (rc != AL_OK) return rc; d931 1 a931 1 return rc; d953 1 a953 1 if (rc != AL_OK) return rc; d965 1 a965 1 return rc; d983 1 a983 1 if (rc != AL_OK) return rc; d991 1 a991 1 return rc; @ 1.7 log @al_copy function, like al_flatten but into a target assembly list PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @a0 1 #include a355 2 assert(NEXT(alc,chunks) == NULL && PREV(alc,chunks) == NULL); @ 1.6 log @INSERT/REMOVE didn't maintain list header test code PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d618 1 d620 2 d623 3 d895 26 @ 1.5 log @stop traversal at span boundary code cleanup PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d1 1 d5 3 d89 2 d93 4 a101 1 PREV(n,l) = PREV(i,l); \ d103 2 d106 1 d357 2 d428 1 a428 2 #if 0 #include d433 1 d435 2 a436 1 printf("AL: %x\n", al); d438 1 a438 1 printf(" C: %x (%d @@ %x + %d < %d)\n", d441 3 a443 1 cur->begin,cur->end); d445 1 d477 3 d481 1 d500 1 d684 3 a686 1 if (tal != NULL) { a688 2 } else { dispose_chunk(al, cur); @ 1.4 log @code cleanup get rid of AL_ALL_BYTES, not very useful PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d773 4 a776 1 if (txp->cur == NULL) d852 2 @ 1.3 log @splice operation traversal needs alloc/free function for context lost of comments PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d5 2 d128 2 d191 1 a191 1 ((n) == AL_BYTES_ALL || (n) > AL_CHUNK_LEN(alc) - (off) ? \ d364 1 d371 1 a371 1 size_t pos; d378 2 a379 1 if (pos <= off && off < pos+chunksize) { d384 9 a392 3 if (pos+chunksize >= off) return AL_ERR_EOF; pos += chunksize; d399 1 d401 1 a401 1 if (pos <= off && off < pos+chunksize) { d406 2 a407 2 if (pos+chunksize < off) return AL_ERR_EOF; d432 1 a432 1 /******************************************************************/ a604 5 /* simplify API */ if (n == AL_BYTES_ALL) n = al->bytes - off; pos = off; d609 1 a609 1 rc = al_seek(al, pos, &cur, &skip); d613 1 a672 3 if (cur == NULL) break; a712 1 a750 2 if (n == AL_BYTES_ALL) n = al->bytes - off; d893 19 @ 1.2 log @added memory allocation policy needs to pass through context to lower functions PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d15 2 d87 35 a121 1 #define ISEMPTY(q,l) (HEAD(q,l) == NULL) d212 1 d318 21 d422 1 a422 1 * allocate an empty assembly line d433 1 a433 1 /* allocate and initialize new assembly line object */ d475 3 d514 3 d555 8 d581 6 a586 1 al_rc_t al_splice(al_t *al, size_t off, size_t n, al_t *tal) d590 1 a590 1 size_t skip, len, step; d592 1 d595 7 a601 1 rc = al_seek(al, off, &cur, &skip); d603 3 a605 3 printf("initial seek %d -> %p + %d\n",off,cur,skip); while (n > 0) { /* check if a partial chunk was selected */ d608 6 d615 4 a618 5 /* copy span from src chunk to target */ step = len - skip; if (n < step) step = n; printf("partial copy (%p + %d) + %d bytes\n",cur,skip,step); d621 7 a627 1 /* cut span from src chunk */ a628 1 printf("shrink at begin\n"); a632 1 printf("align chunk buffer (%d + %d) <- %d\n",skip,step,len-(skip+step)); a638 1 printf("shrink at end\n"); d641 1 d643 6 a649 1 printf("hand over chunk %d\n",step); d656 1 a656 1 dispose_chunk(al,cur); d658 1 d661 1 a661 1 off += step; d664 3 d669 56 a724 1 return AL_ERR_INT; d727 4 a730 1 al_rc_t al_truncate(al_t *al, size_t off, size_t n) d732 2 a733 1 return al_splice(al, off, n, NULL); d736 6 d751 1 a751 1 if (rc != AL_OK) return AL_OK; d759 7 d803 8 d815 1 a815 1 al_tx_t tx; d833 8 a840 1 al_rc_t al_flatten(al_t *al, size_t off, size_t n, char *dst) d843 1 a843 1 al_tx_t tx; d845 1 a845 1 size_t step; d850 1 d854 2 a855 1 dst += step; d857 1 d865 10 a878 1 a882 1 a886 1 @ 1.1 log @first iteration through buffer data type PR: Submitted by: Reviewed by: Approved by: Obtained from: @ text @d105 9 d117 1 a173 3 #define AL_NEW_BUFFERSIZE 100 #define AL_MAX_FREECHUNKS 500 d179 1 a179 1 al_rc_t new_buffer(al_buffer_t **bufp) d181 1 a181 1 size_t n = AL_NEW_BUFFERSIZE; d184 1 a184 1 if ((buf = (al_buffer_t *)malloc(sizeof(al_buffer_t))) == NULL) d187 2 a188 2 if ((buf->mem = malloc(n)) == NULL) { free(buf); d201 1 a201 1 al_rc_t dispose_buffer(al_buffer_t *buf) d208 1 a208 1 free(buf->mem); d210 1 a210 1 free(buf); d220 1 a220 1 al_rc_t make_buffer(char *p, size_t n, al_buffer_t **bufp) d224 1 a224 1 if ((buf = (al_buffer_t *)malloc(sizeof(al_buffer_t))) == NULL) d254 1 a254 1 al_rc_t new_chunk(al_buffer_t *buf, al_chunk_t **alcp) d259 1 a259 1 if ((alc = (al_chunk_t *)malloc(sizeof(al_chunk_t))) == NULL) { d261 1 a261 1 dispose_buffer(buf); d281 1 a281 1 void dispose_chunk(al_chunk_t *alc) d285 1 a285 1 dispose_buffer(alc->buf); d289 2 a290 2 if (alc_freecount >= AL_MAX_FREECHUNKS) { free(alc); d376 1 d383 8 d407 1 a407 1 dispose_chunk(cur); d411 1 d430 1 a430 1 rc = new_buffer(&buf); d432 1 a432 1 rc = new_chunk(buf,&cur); d468 1 a468 1 rc = new_buffer(&buf); d470 1 a470 1 rc = new_chunk(buf,&cur); d497 1 a497 1 rc = make_buffer(p, n, &buf); d499 1 a499 1 rc = new_chunk(buf, &cur); d558 1 a558 1 dispose_chunk(cur); @