mirror of
https://github.com/php/php-src.git
synced 2025-08-18 23:18:56 +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
205
main/spprintf.c
205
main/spprintf.c
|
@ -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; \
|
|
||||||
cc += slen; \
|
|
||||||
s += slen; \
|
|
||||||
} else { \
|
|
||||||
for (i = s_len; i != 0; i--) { \
|
|
||||||
INS_CHAR_NR(xbuf, *s, cc); \
|
|
||||||
s++; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define INS_CHAR(xbuf, ch, cc) \
|
#define INS_CHAR(xbuf, ch) \
|
||||||
xbuf_resize(xbuf, 1); \
|
INS_CHAR_NR(xbuf, ch)
|
||||||
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; \
|
|
||||||
cc += slen; \
|
|
||||||
} else { \
|
|
||||||
do { \
|
|
||||||
INS_CHAR_NR(xbuf, ch, cc); \
|
|
||||||
width--; \
|
|
||||||
} \
|
} \
|
||||||
while (width > len); \
|
} while (0)
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue