Index: ossp-pkg/sio/al.c RCS File: /v/ossp/cvs/ossp-pkg/sio/Attic/al.c,v co -q -kk -p'1.40' '/v/ossp/cvs/ossp-pkg/sio/Attic/al.c,v' | diff -u - /dev/null -L'ossp-pkg/sio/al.c' 2>/dev/null --- ossp-pkg/sio/al.c +++ /dev/null 2025-04-09 16:01:24.000000000 +0200 @@ -1,1317 +0,0 @@ -/* -** 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; -} - Index: ossp-pkg/sio/al.h RCS File: /v/ossp/cvs/ossp-pkg/sio/Attic/al.h,v co -q -kk -p'1.17' '/v/ossp/cvs/ossp-pkg/sio/Attic/al.h,v' | diff -u - /dev/null -L'ossp-pkg/sio/al.h' 2>/dev/null --- ossp-pkg/sio/al.h +++ /dev/null 2025-04-09 16:01:24.000000000 +0200 @@ -1,89 +0,0 @@ -/* -** 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.h: assembly line public API definition -*/ - -#ifndef __AL_H__ -#define __AL_H__ - -typedef enum { - AL_OK, - AL_ERR_ARG, - AL_ERR_MEM, - AL_ERR_EOF, - AL_ERR_INT -} al_rc_t; - -struct al_st; -typedef struct al_st al_t; - -struct al_chunk_st; -typedef struct al_chunk_st al_chunk_t; - -typedef void *al_label_t; - -typedef enum { - AL_FORWARD, - AL_BACKWARD, - AL_FORWARD_SPAN, - AL_BACKWARD_SPAN -} al_td_t; - -struct al_tx_st; -typedef struct al_tx_st al_tx_t; - -al_rc_t al_create (al_t **alp); -al_rc_t al_destroy (al_t *al); -al_rc_t al_append_bytes (al_t *al, const char *src, size_t n, al_label_t label); -al_rc_t al_prepend_bytes(al_t *al, const char *src, size_t n, al_label_t label); -al_rc_t al_attach_buffer(al_t *al, char *p, size_t n, al_label_t label, void (*freemem)(char *, size_t, void *), void *u); -al_rc_t al_txalloc (al_t *al, al_tx_t **txp); -al_rc_t al_txfree (al_t *al, al_tx_t *tx); -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 al_traverse_next(al_t *al, al_tx_t *tx, al_chunk_t **alcp); -al_rc_t al_traverse_end (al_t *al, al_tx_t *tx, int final); -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 al_copy (al_t *al, size_t off, size_t n, al_label_t label, al_t *tal); -al_rc_t al_splice (al_t *al, size_t off, size_t n, al_t *nal, al_t *tal); -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 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 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 al_spanlabel (al_t *al, size_t off, size_t n, al_label_t label, size_t *offp, size_t *spanp); - -size_t al_bytes (const al_t *al); -size_t al_chunk_len (al_chunk_t *alc); -al_label_t al_chunk_label(al_chunk_t *alc); -int al_same_label (al_chunk_t *alc, al_label_t label); -size_t al_chunk_span (al_chunk_t *alc, size_t off, size_t n); -char *al_chunk_ptr (al_chunk_t *alc, size_t off); - -const char *al_error (al_rc_t rc); - -#endif /* __AL_H__ */ - Index: ossp-pkg/sio/al.pod RCS File: /v/ossp/cvs/ossp-pkg/sio/Attic/al.pod,v co -q -kk -p'1.17' '/v/ossp/cvs/ossp-pkg/sio/Attic/al.pod,v' | diff -u - /dev/null -L'ossp-pkg/sio/al.pod' 2>/dev/null --- ossp-pkg/sio/al.pod +++ /dev/null 2025-04-09 16:01:24.000000000 +0200 @@ -1,474 +0,0 @@ -## -## 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.pod: assembly line library manual page -## - -=pod - -=head1 NAME - -B - Assembly Line - -=head1 VERSION - -B - -=head1 SYNOPSIS - -=over 4 - -=item B: - -al_rc_t, -al_t, -al_tx_t, -al_td_t, -al_chunk_t. - -=item B: - -al_create, -al_destroy, -al_append_bytes, -al_prepend_bytes, -al_attach_buffer, -al_splice, -al_setlabel, -al_bytes. - -=item B: - -al_txalloc, -al_txfree, -al_traverse, -al_traverse_next, -al_traverse_end, -al_traverse_cb. - -=item B: - -al_flatten, -al_copy, -al_firstlabel, -al_spanlabel. - -=item B: - -al_chunk_len, -al_chunk_span, -al_chunk_label, -al_same_label, -al_chunk_ptr. - -=item B: - -al_error. - -=back - -=head1 DESCRIPTION - -B defines an abstract type of a data buffer that can -assemble, move and truncate data but avoids actual copying. - -It provides the following key features: - -=over 4 - -=back - -=head1 DATA TYPES - -B uses six data types in its API: - -=over 4 - -=item B (Return Code Type) - -This is an exported enumerated integer type with the following possible -values: - - AL_OK Everything Ok - AL_ERR_ARG Invalid Argument - AL_ERR_MEM Not Enough Memory - AL_ERR_EOF End Of Communication - AL_ERR_INT Internal Error - -=item B (Assembly Line Type) - -This is an opaque data type representing a data buffer. -Only pointers to this abstract data type are used in the API. - -=item B (Label Type) - -This is an opaque pointer type representing a specific data flavour. -You can restrict traversal operations to data that was marked with -the specific flavour. Usually you would cast a pointer to the -object that maintains the data to B. You may use -NULL as a label but on traversal NULL matches any label. - -=item B (Traversal Context Type) - -This is an opaque data type representing the state of a buffer -traversal operation. Only pointers to this abstract data type are -used in the API. - -=item B (Traversal Direction Type) - -This is an exported enumerated integer type with the following possible -values: - - AL_FORWARD traverse assembly line from beginning to end - AL_BACKWARD traverse assembly line from end to beginning - AL_FORWARD_SPAN like AL_FORWARD, but stop when label does not match - AL_BACKWARD_SPAN like AL_BACKWARD, but stop when label does not match - -=item B (Chunk Type) - -This is an opaque data type representing a chunk of a buffer during -a traversal operation. Only pointers to this abstract data type are -used in the API. The B type is used to generate a pointer -and byte count to access the data in the buffer. - -=back - -=head1 FUNCTIONS - -B provides a bunch of API functions, all modelled after the -same prototype: "C CIC<(al_>[C]C<_t *,> -...C<)>". This means every function returns C to indicate its -success (C) or failure (C) by returning a return code -(the corresponding describing text can be determined by passing this -return code to C). Each function name starts with the common -prefix C and receives a C (or C) object on which -it operates as its first argument. - -=head2 Assembly Line Operations - -=over 4 - -=item al_rc_t B(al_t **I); - -Create an assembly line abstraction object. -The object is stored in I on success. - -Example: C - -=item al_rc_t B(al_t *I); - -Destroy an assembly line abstraction object. -The object I is invalid after this call succeeded. - -Example: C - -=item al_rc_t B(al_t *I, const char *I, size_t I, al_label_t I