dgaudet 98/03/29 01:35:44
Modified: src/ap ap_snprintf.c src/include ap.h src/main alloc.c buff.c Log: Revamp the apapi_vformatter interface to reduce copies. In this interface the callers hand apapi_vformatter pointers into their own private buffers, and are called back for flushing when appropriate. This is more efficient and seems somewhat more clean. Revision Changes Path 1.16 +23 -44 apache-1.3/src/ap/ap_snprintf.c Index: ap_snprintf.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/ap/ap_snprintf.c,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- ap_snprintf.c 1998/03/28 21:58:38 1.15 +++ ap_snprintf.c 1998/03/29 09:35:40 1.16 @@ -266,10 +266,11 @@ #define INS_CHAR(c, sp, bep, cc) \ { \ if (sp == bep) { \ - if (write_func(write_data, staging_buf, \ - sizeof(staging_buf)) != 0) \ + vbuff->curpos = sp; \ + if (flush_func(vbuff)) \ return -1; \ - sp = staging_buf; \ + sp = vbuff->curpos; \ + bep = vbuff->endpos; \ } \ *sp++ = (c); \ cc++; \ @@ -503,9 +504,8 @@ /* * Do format conversion placing the output in buffer */ -API_EXPORT(int) apapi_vformatter( - int (*write_func)(void *, const char *, size_t), - void *write_data, const char *fmt, va_list ap) +API_EXPORT(int) apapi_vformatter(int (*flush_func)(apapi_vformatter_buff *), + apapi_vformatter_buff *vbuff, const char *fmt, va_list ap) { register char *sp; register char *bep; @@ -531,8 +531,6 @@ char num_buf[NUM_BUF_SIZE]; char char_buf[2]; /* for printing %% and %<unknown> */ - char staging_buf[MAX_STRING_LEN]; - /* * Flag variables */ @@ -544,8 +542,8 @@ boolean_e adjust_width; bool_int is_negative; - sp = staging_buf; - bep = sp + sizeof(staging_buf); + sp = vbuff->curpos; + bep = vbuff->endpos; while (*fmt) { if (*fmt != '%') { @@ -875,33 +873,14 @@ } fmt++; } - if (sp > staging_buf) { - if (write_func(write_data, staging_buf, sp - staging_buf) != 0) { - return -1; - } - } + vbuff->curpos = sp; return cc; } -struct snprintf_write_data { - char *strp; - char *end_buf; -}; - -static int snprintf_write(void *vdata, const char *inp, size_t len) +static int snprintf_flush(apapi_vformatter_buff *vbuff) { - struct snprintf_write_data *wd; - size_t amt; - - wd = vdata; - amt = wd->end_buf - wd->strp; - if (len > amt) { - len = amt; - } - memcpy(wd->strp, inp, len); - wd->strp += len; - return 0; + return -1; } @@ -909,19 +888,19 @@ { int cc; va_list ap; - struct snprintf_write_data wd; + apapi_vformatter_buff vbuff; if (len == 0) return 0; /* save one byte for nul terminator */ - wd.strp = buf; - wd.end_buf = buf + len - 1; + vbuff.curpos = buf; + vbuff.endpos = buf + len - 1; va_start(ap, format); - cc = apapi_vformatter(snprintf_write, &wd, format, ap); - *wd.strp = '\0'; + cc = apapi_vformatter(snprintf_flush, &vbuff, format, ap); va_end(ap); - return (cc); + *vbuff.curpos = '\0'; + return (cc == -1) ? len : cc; } @@ -929,15 +908,15 @@ va_list ap) { int cc; - struct snprintf_write_data wd; + apapi_vformatter_buff vbuff; if (len == 0) return 0; /* save one byte for nul terminator */ - wd.strp = buf; - wd.end_buf = buf + len - 1; - cc = apapi_vformatter(snprintf_write, &wd, format, ap); - *wd.strp = '\0'; - return (cc); + vbuff.curpos = buf; + vbuff.endpos = buf + len - 1; + cc = apapi_vformatter(snprintf_flush, &vbuff, format, ap); + *vbuff.curpos = '\0'; + return (cc == -1) ? len : cc; } 1.9 +41 -11 apache-1.3/src/include/ap.h Index: ap.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/ap.h,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- ap.h 1998/03/28 11:58:17 1.8 +++ ap.h 1998/03/29 09:35:41 1.9 @@ -80,21 +80,51 @@ /* apapi_vformatter() is a generic printf-style formatting routine * with some extensions. * - * The write_func() is called when there is data available to be - * output. write_func() should return 0 when it wishes apapi_vformatter - * to continue, and non-zero otherwise. apapi_vformatter will stop - * immediately and return -1 when a non-zero return from - * write_func(). + * The apapi_vformatter_buff has two elements curpos and endpos. + * curpos is where apapi_vformatter will write the next byte of output. + * It proceeds writing output to curpos, and updating curpos, until + * either the end of output is reached, or curpos == endpos (i.e. the + * buffer is full). * - * If write_func() always returns 0 then apapi_vformatter will return - * the number of characters written. + * If the end of output is reached, apapi_vformatter returns the + * number of bytes written. + * + * When the buffer is full, the flush_func is called. The flush_func + * can return -1 to indicate that no further output should be attempted, + * and apapi_vformatter will return immediately with -1. Otherwise + * the flush_func should flush the buffer in whatever manner is + * appropriate, re-initialize curpos and endpos, and return 0. + * + * Note that flush_func is only invoked as a result of attempting to + * write another byte at curpos when curpos == endpos. So for + * example, it's possible when the output exactly matches the buffer + * space available that curpos == endpos will be true when + * apapi_vformatter returns. */ -API_EXPORT(int) apapi_vformatter( - int (*write_func)(void *write_data, const char *outp, size_t len), - void *write_data, const char *fmt, va_list ap); +typedef struct { + char *curpos; + char *endpos; +} apapi_vformatter_buff; -/* These are snprintf implementations based on apapi_vformatter(). */ +API_EXPORT(int) apapi_vformatter(int (*flush_func)(apapi_vformatter_buff *), + apapi_vformatter_buff *, const char *fmt, va_list ap); + +/* These are snprintf implementations based on apapi_vformatter(). + * + * Note that various standards and implementations disagree on the return + * value of snprintf, and side-effects due to %n in the formatting string. + * ap_snprintf behaves as follows: + * + * Process the format string until the entire string is exhausted, or + * the buffer fills. If the buffer fills then stop processing immediately + * (so no further %n arguments are processed), and return the buffer + * length. In all cases the buffer is NUL terminated. + * + * In no event does ap_snprintf return a negative number. It's not possible + * to distinguish between an output which was truncated, and an output which + * exactly filled the buffer. + */ API_EXPORT(int) ap_snprintf(char *buf, size_t len, const char *format,...) __attribute__((format(printf,3,4))); API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format, 1.82 +31 -40 apache-1.3/src/main/alloc.c Index: alloc.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/alloc.c,v retrieving revision 1.81 retrieving revision 1.82 diff -u -r1.81 -r1.82 --- alloc.c 1998/03/28 21:35:41 1.81 +++ alloc.c 1998/03/29 09:35:42 1.82 @@ -779,68 +779,52 @@ */ struct psprintf_data { - pool *p; + apapi_vformatter_buff vbuff; #ifdef ALLOC_USE_MALLOC char *base; - size_t len; #else union block_hdr *blok; - char *strp; int got_a_new_block; #endif }; -static int psprintf_write(void *vdata, const char *inp, size_t len) +static int psprintf_flush(apapi_vformatter_buff *vbuff) { + struct psprintf_data *ps = (struct psprintf_data *)vbuff; #ifdef ALLOC_USE_MALLOC - struct psprintf_data *ps; int size; char *ptr; - ps = vdata; - - size = ps->len + len + 1; - ptr = realloc(ps->base, size); + size = ps->vbuff.curpos - ps->base; + ptr = realloc(ps->base, 2*size); if (ptr == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); } ps->base = ptr; - memcpy(ptr + ps->len, inp, len); - ps->len += len; + ps->vbuff.curpos = ptr + size; + ps->vbuff.endpos = ptr + 2*size - 1; return 0; #else - struct psprintf_data *ps; union block_hdr *blok; union block_hdr *nblok; size_t cur_len; char *strp; - ps = vdata; - - /* does it fit in the current block? */ blok = ps->blok; - strp = ps->strp; - if (strp + len + 1 < blok->h.endp) { - memcpy(strp, inp, len); - ps->strp = strp + len; - return 0; - } - + strp = ps->vbuff.curpos; cur_len = strp - blok->h.first_avail; /* must try another blok */ block_alarms(); (void) acquire_mutex(alloc_mutex); - nblok = new_block((cur_len + len)*2); + nblok = new_block(2 * cur_len); (void) release_mutex(alloc_mutex); unblock_alarms(); - strp = nblok->h.first_avail; - memcpy(strp, blok->h.first_avail, cur_len); - strp += cur_len; - memcpy(strp, inp, len); - strp += len; - ps->strp = strp; + memcpy(nblok->h.first_avail, strp, cur_len); + strp = nblok->h.first_avail + cur_len; + ps->vbuff.curpos = strp; + ps->vbuff.endpos = nblok->h.endp - 1; /* did we allocate the current blok? if so free it up */ if (ps->got_a_new_block) { @@ -865,16 +849,23 @@ void *ptr; block_alarms(); - ps.p = p; - ps.base = NULL; - ps.len = CLICK_SZ; /* need room at beginning for allocation_list */ - apapi_vformatter(psprintf_write, &ps, fmt, ap); + ps.base = malloc(512); + if (ps.base == NULL) { + fputs("Ouch! Out of memory!\n", stderr); + exit(1); + } + /* need room at beginning for allocation_list */ + ps.vbuff.curpos = ps.base + CLICK_SZ; + ps.vbuff.endpos = ps.base + 511; + apapi_vformatter(psprintf_flush, &ps.vbuff, fmt, ap); + *ps.vbuff.curpos++ = '\0'; ptr = ps.base; + /* shrink */ + ptr = realloc(ptr, ps.vbuff.curpos - ptr); if (ptr == NULL) { - unblock_alarms(); - return pstrdup(p, ""); + fputs("Ouch! Out of memory!\n", stderr); + exit(1); } - *((char *)ptr + ps.len) = '\0'; /* room was saved for this */ *(void **)ptr = p->allocation_list; p->allocation_list = ptr; unblock_alarms(); @@ -884,14 +875,14 @@ char *strp; int size; - ps.p = p; ps.blok = p->last; - ps.strp = ps.blok->h.first_avail; + ps.vbuff.curpos = ps.blok->h.first_avail; + ps.vbuff.endpos = ps.blok->h.endp - 1; ps.got_a_new_block = 0; - apapi_vformatter(psprintf_write, &ps, fmt, ap); + apapi_vformatter(psprintf_flush, &ps.vbuff, fmt, ap); - strp = ps.strp; + strp = ps.vbuff.curpos; *strp++ = '\0'; size = strp - ps.blok->h.first_avail; 1.68 +35 -5 apache-1.3/src/main/buff.c Index: buff.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/buff.c,v retrieving revision 1.67 retrieving revision 1.68 diff -u -r1.67 -r1.68 --- buff.c 1998/03/28 11:58:22 1.67 +++ buff.c 1998/03/29 09:35:43 1.68 @@ -1445,11 +1445,24 @@ fb->error_data = data; } -static int bprintf_write(void *vdata, const char *inp, size_t len) +struct bprintf_data { + apapi_vformatter_buff vbuff; + BUFF *fb; +}; + +static int bprintf_flush(apapi_vformatter_buff *vbuff) { - if (bwrite(vdata, inp, len) != len) { - return -1; + struct bprintf_data *b = (struct bprintf_data *)vbuff; + BUFF *fb = b->fb; + + fb->outcnt += b->vbuff.curpos - (char *)&fb->outbase[fb->outcnt]; + if (fb->outcnt == fb->bufsiz) { + if (bflush(fb)) { + return -1; + } } + vbuff->curpos = &fb->outbase[fb->outcnt]; + vbuff->endpos = &fb->outbase[fb->bufsiz]; return 0; } @@ -1457,14 +1470,31 @@ { va_list ap; int res; + struct bprintf_data b; + b.vbuff.curpos = &fb->outbase[fb->outcnt]; + b.vbuff.endpos = &fb->outbase[fb->bufsiz]; + b.fb = fb; va_start(ap, fmt); - res = apapi_vformatter(bprintf_write, fb, fmt, ap); + res = apapi_vformatter(bprintf_flush, &b.vbuff, fmt, ap); va_end(ap); + if (res != -1) { + fb->outcnt += b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt]; + } return res; } API_EXPORT(int) vbprintf(BUFF *fb, const char *fmt, va_list ap) { - return apapi_vformatter(bprintf_write, fb, fmt, ap); + struct bprintf_data b; + int res; + + b.vbuff.curpos = &fb->outbase[fb->outcnt]; + b.vbuff.endpos = &fb->outbase[fb->bufsiz]; + b.fb = fb; + res = apapi_vformatter(bprintf_flush, &b.vbuff, fmt, ap); + if (res != -1) { + fb->outcnt += b.vbuff.curpos - (char *)&fb->outbase[fb->outcnt]; + } + return res; }