mirror of
https://github.com/php/php-src.git
synced 2025-08-18 15:08:55 +02:00
- 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:
parent
8fee867865
commit
0cb1ff3bda
1 changed files with 57 additions and 170 deletions
225
main/spprintf.c
225
main/spprintf.c
|
@ -89,6 +89,8 @@
|
|||
#define FLOAT_DIGITS 6
|
||||
#define EXPONENT_LENGTH 10
|
||||
|
||||
#include "ext/standard/php_smart_str.h"
|
||||
|
||||
/*
|
||||
* NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
|
||||
*
|
||||
|
@ -96,134 +98,47 @@
|
|||
*/
|
||||
#define NUM_BUF_SIZE 512
|
||||
|
||||
|
||||
/*
|
||||
* Size for realloc operations
|
||||
*/
|
||||
#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.
|
||||
* The INS_CHAR macro inserts a character in the buffer.
|
||||
*
|
||||
* 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) \
|
||||
if (xbuf->nextb < xbuf->buf_end) { \
|
||||
*(xbuf->nextb++) = ch; \
|
||||
cc++; \
|
||||
}
|
||||
#define INS_CHAR_NR(xbuf, ch) do { \
|
||||
smart_str_appendc(xbuf, ch); \
|
||||
} while (0)
|
||||
|
||||
#define INS_STRING(xbuf, s, slen, cc) \
|
||||
xbuf_resize(xbuf, s_len); \
|
||||
if (xbuf->nextb+slen < xbuf->buf_end) { \
|
||||
memcpy(xbuf->nextb, s, slen); \
|
||||
xbuf->nextb += slen; \
|
||||
cc += slen; \
|
||||
s += slen; \
|
||||
} else { \
|
||||
for (i = s_len; i != 0; i--) { \
|
||||
INS_CHAR_NR(xbuf, *s, cc); \
|
||||
s++; \
|
||||
} \
|
||||
}
|
||||
#define INS_STRING(xbuf, s, slen) do { \
|
||||
smart_str_appendl(xbuf, s, slen); \
|
||||
} while (0)
|
||||
|
||||
#define INS_CHAR(xbuf, ch, cc) \
|
||||
xbuf_resize(xbuf, 1); \
|
||||
INS_CHAR_NR(xbuf, ch, cc)
|
||||
#define INS_CHAR(xbuf, ch) \
|
||||
INS_CHAR_NR(xbuf, ch)
|
||||
|
||||
/*
|
||||
* Macro that does padding. The padding is done by printing
|
||||
* the character ch.
|
||||
*/
|
||||
#define PAD(xbuf, width, len, ch, cc) \
|
||||
if (width > len) { \
|
||||
int slen = width-len; \
|
||||
xbuf_resize(xbuf, slen); \
|
||||
if (xbuf->nextb+slen < xbuf->buf_end) { \
|
||||
memset(xbuf->nextb, ch, slen);\
|
||||
xbuf->nextb += slen; \
|
||||
cc += slen; \
|
||||
} else { \
|
||||
do { \
|
||||
INS_CHAR_NR(xbuf, ch, cc); \
|
||||
width--; \
|
||||
} \
|
||||
while (width > len); \
|
||||
} \
|
||||
}
|
||||
#define PAD(xbuf, count, ch) do { \
|
||||
if ((count) > 0) { \
|
||||
size_t newlen; \
|
||||
smart_str_alloc(xbuf, (count), 0); \
|
||||
memset(xbuf->c + xbuf->len, ch, (count)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NUM(c) (c - '0')
|
||||
|
||||
#define STR_TO_DEC(str, num) \
|
||||
num = NUM(*str++); \
|
||||
while (isdigit((int)*str)) { \
|
||||
num *= 10; \
|
||||
num += NUM(*str++); \
|
||||
}
|
||||
#define STR_TO_DEC(str, num) do { \
|
||||
num = NUM(*str++); \
|
||||
while (isdigit((int)*str)) { \
|
||||
num *= 10; \
|
||||
num += NUM(*str++); \
|
||||
if (num >= LONG_MAX / 10) { \
|
||||
while (isdigit((int)*str++)); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* 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
|
||||
* to be printed.
|
||||
*/
|
||||
#define FIX_PRECISION(adjust, precision, s, s_len) \
|
||||
if (adjust) \
|
||||
while (s_len < precision) { \
|
||||
*--s = '0'; \
|
||||
s_len++; \
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
#define FIX_PRECISION(adjust, precision, s, s_len) do { \
|
||||
if (adjust) \
|
||||
while (s_len < precision) { \
|
||||
*--s = '0'; \
|
||||
s_len++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
char *q;
|
||||
int s_len;
|
||||
|
@ -290,7 +193,7 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
|
|||
|
||||
while (*fmt) {
|
||||
if (*fmt != '%') {
|
||||
INS_CHAR(xbuf, *fmt, cc);
|
||||
INS_CHAR(xbuf, *fmt);
|
||||
} else {
|
||||
/*
|
||||
* Default variable settings
|
||||
|
@ -536,7 +439,7 @@ static int xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_lis
|
|||
|
||||
|
||||
case 'n':
|
||||
*(va_arg(ap, int *)) = cc;
|
||||
*(va_arg(ap, int *)) = xbuf->len;
|
||||
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 (pad_char == '0' && prefix_char != NUL) {
|
||||
INS_CHAR(xbuf, *s, cc)
|
||||
s++;
|
||||
INS_CHAR(xbuf, *s);
|
||||
s++;
|
||||
s_len--;
|
||||
min_width--;
|
||||
}
|
||||
PAD(xbuf, min_width, s_len, pad_char, cc);
|
||||
PAD(xbuf, min_width - s_len, pad_char);
|
||||
}
|
||||
/*
|
||||
* 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)
|
||||
PAD(xbuf, min_width, s_len, pad_char, cc);
|
||||
PAD(xbuf, min_width - s_len, pad_char);
|
||||
}
|
||||
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)
|
||||
{
|
||||
xbuffy xbuf;
|
||||
int cc;
|
||||
smart_str xbuf = {0};
|
||||
|
||||
assert(pbuf != NULL);
|
||||
/*
|
||||
* First initialize the descriptor
|
||||
* Notice that if no length is given, we initialize buf_end to the
|
||||
* 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;
|
||||
xbuf_format_converter(&xbuf, format, ap);
|
||||
|
||||
if (max_len && xbuf.len > max_len) {
|
||||
xbuf.len = max_len;
|
||||
}
|
||||
smart_str_0(&xbuf);
|
||||
|
||||
*pbuf = xbuf.c;
|
||||
|
||||
return xbuf.len;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue