- trims +100 lines of code from spprintf.c

- introduces an overflow detection in STR_TO_DEC
    - eliminates dead code (e.g. assert(foo); if (foo) {..})
    - removes unused macros from the original code
    - simplifies code (e.g. cc was completely dropped)
    - improves run-time performance

      The max_len feature is never used in our code base.
      Nevertheless, cpu cycles were spent on each string
      operation to check the current length against max_len which
      is quite inefficient.  Thus, I've moved the check to
      vspprintf where it is applied only once per call.
This commit is contained in:
Sascha Schumann 2003-02-12 19:38:10 +00:00
parent 8fee867865
commit 0cb1ff3bda

View file

@ -89,6 +89,8 @@
#define FLOAT_DIGITS 6 #define FLOAT_DIGITS 6
#define EXPONENT_LENGTH 10 #define EXPONENT_LENGTH 10
#include "ext/standard/php_smart_str.h"
/* /*
* NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
* *
@ -96,134 +98,47 @@
*/ */
#define NUM_BUF_SIZE 512 #define NUM_BUF_SIZE 512
/* /*
* Size for realloc operations * The INS_CHAR macro inserts a character in the buffer.
*/
#define SPPRINTF_BLOCK_SIZE 128
/*
* Descriptor for buffer area
*/
struct xbuf_area {
char *buf; /* pointer to buffer */
size_t size;
size_t max_len;
char *buf_end; /* pointer to buffer end or ~0 */
char *nextb; /* pointer to next byte to read/write */
};
typedef struct xbuf_area xbuffy;
/* Resize xbuf so that add bytes can be added. Reallocation is done
* in defined block size to minimize calls to realloc.
*/
static void xbuf_resize(xbuffy *xbuf, size_t add)
{
char *buf;
size_t size, offset;
if (xbuf->buf) {
offset = xbuf->nextb - xbuf->buf;
if (offset+add < xbuf->size) {
return; /* do not change size if not necessary */
}
} else {
offset = 0;
}
if (add<SPPRINTF_BLOCK_SIZE) {
size = xbuf->size + SPPRINTF_BLOCK_SIZE;
} else {
size = xbuf->size + add;
}
if (xbuf->max_len && size > xbuf->max_len) {
size = xbuf->max_len;
}
buf = erealloc(xbuf->buf, size+1); /* alloc space for NUL */
if (buf) {
xbuf->buf = buf;
xbuf->buf_end = xbuf->max_len ? &buf[size] : (char *) ~0;
xbuf->nextb = buf+offset;
xbuf->size = size;
}
}
/* Initialise xbuffy with size spprintf_BLOCK_SIZE
*/
static char * xbuf_init(xbuffy *xbuf, size_t max_len)
{
xbuf->buf = NULL;
xbuf->size = 0;
xbuf->max_len = max_len;
xbuf_resize(xbuf, 0); /* NOT max_len */
return xbuf->buf;
}
/*
* The INS_CHAR macro inserts a character in the buffer and writes
* the buffer back to disk if necessary
* It uses the char pointers sp and bep:
* sp points to the next available character in the buffer
* bep points to the end-of-buffer+1
* While using this macro, note that the nextb pointer is NOT updated.
* *
* NOTE: Evaluation of the c argument should not have any side-effects * NOTE: Evaluation of the ch argument should not have any side-effects
*/ */
#define INS_CHAR_NR(xbuf, ch, cc) \ #define INS_CHAR_NR(xbuf, ch) do { \
if (xbuf->nextb < xbuf->buf_end) { \ smart_str_appendc(xbuf, ch); \
*(xbuf->nextb++) = ch; \ } while (0)
cc++; \
}
#define INS_STRING(xbuf, s, slen, cc) \ #define INS_STRING(xbuf, s, slen) do { \
xbuf_resize(xbuf, s_len); \ smart_str_appendl(xbuf, s, slen); \
if (xbuf->nextb+slen < xbuf->buf_end) { \ } while (0)
memcpy(xbuf->nextb, s, slen); \
xbuf->nextb += slen; \ #define INS_CHAR(xbuf, ch) \
cc += slen; \ INS_CHAR_NR(xbuf, ch)
s += slen; \
} else { \
for (i = s_len; i != 0; i--) { \
INS_CHAR_NR(xbuf, *s, cc); \
s++; \
} \
}
#define INS_CHAR(xbuf, ch, cc) \
xbuf_resize(xbuf, 1); \
INS_CHAR_NR(xbuf, ch, cc)
/* /*
* Macro that does padding. The padding is done by printing * Macro that does padding. The padding is done by printing
* the character ch. * the character ch.
*/ */
#define PAD(xbuf, width, len, ch, cc) \ #define PAD(xbuf, count, ch) do { \
if (width > len) { \ if ((count) > 0) { \
int slen = width-len; \ size_t newlen; \
xbuf_resize(xbuf, slen); \ smart_str_alloc(xbuf, (count), 0); \
if (xbuf->nextb+slen < xbuf->buf_end) { \ memset(xbuf->c + xbuf->len, ch, (count)); \
memset(xbuf->nextb, ch, slen);\ } \
xbuf->nextb += slen; \ } while (0)
cc += slen; \
} else { \
do { \
INS_CHAR_NR(xbuf, ch, cc); \
width--; \
} \
while (width > len); \
} \
}
#define NUM(c) (c - '0') #define NUM(c) (c - '0')
#define STR_TO_DEC(str, num) \ #define STR_TO_DEC(str, num) do { \
num = NUM(*str++); \ num = NUM(*str++); \
while (isdigit((int)*str)) { \ while (isdigit((int)*str)) { \
num *= 10; \ num *= 10; \
num += NUM(*str++); \ num += NUM(*str++); \
} if (num >= LONG_MAX / 10) { \
while (isdigit((int)*str++)); \
break; \
} \
} \
} while (0)
/* /*
* This macro does zero padding so that the precision * This macro does zero padding so that the precision
@ -231,33 +146,21 @@ static char * xbuf_init(xbuffy *xbuf, size_t max_len)
* adding '0's to the left of the string that is going * adding '0's to the left of the string that is going
* to be printed. * to be printed.
*/ */
#define FIX_PRECISION(adjust, precision, s, s_len) \ #define FIX_PRECISION(adjust, precision, s, s_len) do { \
if (adjust) \ if (adjust) \
while (s_len < precision) { \ while (s_len < precision) { \
*--s = '0'; \ *--s = '0'; \
s_len++; \ s_len++; \
} } \
} while (0)
/*
* Prefix the character ch to the string str
* Increase length
* Set the has_prefix flag
*/
#define PREFIX(str, length, ch) \
*--str = ch; \
length++; \
has_prefix = YES
/* /*
* Do format conversion placing the output in buffer * Do format conversion placing the output in buffer
*/ */
static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_list ap) static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
{ {
register int cc = 0;
register int i;
register char *s = NULL; register char *s = NULL;
char *q; char *q;
int s_len; int s_len;
@ -290,7 +193,7 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
while (*fmt) { while (*fmt) {
if (*fmt != '%') { if (*fmt != '%') {
INS_CHAR(xbuf, *fmt, cc); INS_CHAR(xbuf, *fmt);
} else { } else {
/* /*
* Default variable settings * Default variable settings
@ -536,7 +439,7 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
case 'n': case 'n':
*(va_arg(ap, int *)) = cc; *(va_arg(ap, int *)) = xbuf->len;
break; break;
/* /*
@ -593,24 +496,24 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
} }
if (adjust_width && adjust == RIGHT && min_width > s_len) { if (adjust_width && adjust == RIGHT && min_width > s_len) {
if (pad_char == '0' && prefix_char != NUL) { if (pad_char == '0' && prefix_char != NUL) {
INS_CHAR(xbuf, *s, cc) INS_CHAR(xbuf, *s);
s++; s++;
s_len--; s_len--;
min_width--; min_width--;
} }
PAD(xbuf, min_width, s_len, pad_char, cc); PAD(xbuf, min_width - s_len, pad_char);
} }
/* /*
* Print the string s. * Print the string s.
*/ */
INS_STRING(xbuf, s, s_len, cc); INS_STRING(xbuf, s, s_len);
if (adjust_width && adjust == LEFT && min_width > s_len) if (adjust_width && adjust == LEFT && min_width > s_len)
PAD(xbuf, min_width, s_len, pad_char, cc); PAD(xbuf, min_width - s_len, pad_char);
} }
fmt++; fmt++;
} }
return (cc); return;
} }
@ -619,34 +522,18 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
*/ */
PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap)
{ {
xbuffy xbuf; smart_str xbuf = {0};
int cc;
assert(pbuf != NULL); xbuf_format_converter(&xbuf, format, ap);
/*
* First initialize the descriptor if (max_len && xbuf.len > max_len) {
* Notice that if no length is given, we initialize buf_end to the xbuf.len = max_len;
* highest possible address.
*/
if (!xbuf_init(&xbuf, max_len)) {
if (pbuf)
*pbuf = NULL;
return 0;
} else {
/*
* Do the conversion
*/
cc = xbuf_format_converter(&xbuf, format, ap);
if (xbuf.nextb <= xbuf.buf_end)
*(xbuf.nextb) = '\0';
else if (xbuf.size)
xbuf.buf[xbuf.size-1] = '\0';
if (pbuf)
*pbuf = xbuf.buf;
else
efree(pbuf);
return cc;
} }
smart_str_0(&xbuf);
*pbuf = xbuf.c;
return xbuf.len;
} }