mirror of
https://github.com/postgres/postgres.git
synced 2025-09-08 00:47:37 +03:00
Hand code string to integer conversion for performance.
As benchmarks show, using libc's string-to-integer conversion is pretty slow. At least part of the reason for that is that strtol[l] have to be more generic than what largely is required inside pg. This patch considerably speeds up int2/int4 input (int8 already was already using hand-rolled code). Most of the existing pg_atoi callers have been converted. But as one requires pg_atoi's custom delimiter functionality, and as it seems likely that there's external pg_atoi users, it seems sensible to just keep pg_atoi around. Author: Andres Freund Reviewed-By: Robert Haas Discussion: https://postgr.es/m/20171208214437.qgn6zdltyq5hmjpk@alap3.anarazel.de
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "common/int.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
/*
|
||||
@@ -108,6 +109,154 @@ pg_atoi(const char *s, int size, int c)
|
||||
return (int32) l;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert input string to a signed 16 bit integer.
|
||||
*
|
||||
* Allows any number of leading or trailing whitespace characters. Will throw
|
||||
* ereport() upon bad input format or overflow.
|
||||
*
|
||||
* NB: Accumulate input as a negative number, to deal with two's complement
|
||||
* representation of the most negative number, which can't be represented as a
|
||||
* positive number.
|
||||
*/
|
||||
int16
|
||||
pg_strtoint16(const char *s)
|
||||
{
|
||||
const char *ptr = s;
|
||||
int16 tmp = 0;
|
||||
bool neg = false;
|
||||
|
||||
/* skip leading spaces */
|
||||
while (likely(*ptr) && isspace((unsigned char) *ptr))
|
||||
ptr++;
|
||||
|
||||
/* handle sign */
|
||||
if (*ptr == '-')
|
||||
{
|
||||
ptr++;
|
||||
neg = true;
|
||||
}
|
||||
else if (*ptr == '+')
|
||||
ptr++;
|
||||
|
||||
/* require at least one digit */
|
||||
if (unlikely(!isdigit((unsigned char) *ptr)))
|
||||
goto invalid_syntax;
|
||||
|
||||
/* process digits */
|
||||
while (*ptr && isdigit((unsigned char) *ptr))
|
||||
{
|
||||
int8 digit = (*ptr++ - '0');
|
||||
|
||||
if (unlikely(pg_mul_s16_overflow(tmp, 10, &tmp)) ||
|
||||
unlikely(pg_sub_s16_overflow(tmp, digit, &tmp)))
|
||||
goto out_of_range;
|
||||
}
|
||||
|
||||
/* allow trailing whitespace, but not other trailing chars */
|
||||
while (*ptr != '\0' && isspace((unsigned char) *ptr))
|
||||
ptr++;
|
||||
|
||||
if (unlikely(*ptr != '\0'))
|
||||
goto invalid_syntax;
|
||||
|
||||
if (!neg)
|
||||
{
|
||||
/* could fail if input is most negative number */
|
||||
if (unlikely(tmp == PG_INT16_MIN))
|
||||
goto out_of_range;
|
||||
tmp = -tmp;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
||||
out_of_range:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value \"%s\" is out of range for type %s",
|
||||
s, "smallint")));
|
||||
|
||||
invalid_syntax:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"smallint", s)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert input string to a signed 32 bit integer.
|
||||
*
|
||||
* Allows any number of leading or trailing whitespace characters. Will throw
|
||||
* ereport() upon bad input format or overflow.
|
||||
*
|
||||
* NB: Accumulate input as a negative number, to deal with two's complement
|
||||
* representation of the most negative number, which can't be represented as a
|
||||
* positive number.
|
||||
*/
|
||||
int32
|
||||
pg_strtoint32(const char *s)
|
||||
{
|
||||
const char *ptr = s;
|
||||
int32 tmp = 0;
|
||||
bool neg = false;
|
||||
|
||||
/* skip leading spaces */
|
||||
while (likely(*ptr) && isspace((unsigned char) *ptr))
|
||||
ptr++;
|
||||
|
||||
/* handle sign */
|
||||
if (*ptr == '-')
|
||||
{
|
||||
ptr++;
|
||||
neg = true;
|
||||
}
|
||||
else if (*ptr == '+')
|
||||
ptr++;
|
||||
|
||||
/* require at least one digit */
|
||||
if (unlikely(!isdigit((unsigned char) *ptr)))
|
||||
goto invalid_syntax;
|
||||
|
||||
/* process digits */
|
||||
while (*ptr && isdigit((unsigned char) *ptr))
|
||||
{
|
||||
int8 digit = (*ptr++ - '0');
|
||||
|
||||
if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) ||
|
||||
unlikely(pg_sub_s32_overflow(tmp, digit, &tmp)))
|
||||
goto out_of_range;
|
||||
}
|
||||
|
||||
/* allow trailing whitespace, but not other trailing chars */
|
||||
while (*ptr != '\0' && isspace((unsigned char) *ptr))
|
||||
ptr++;
|
||||
|
||||
if (unlikely(*ptr != '\0'))
|
||||
goto invalid_syntax;
|
||||
|
||||
if (!neg)
|
||||
{
|
||||
/* could fail if input is most negative number */
|
||||
if (unlikely(tmp == PG_INT32_MIN))
|
||||
goto out_of_range;
|
||||
tmp = -tmp;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
|
||||
out_of_range:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("value \"%s\" is out of range for type %s",
|
||||
s, "integer")));
|
||||
|
||||
invalid_syntax:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||
"integer", s)));
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_itoa: converts a signed 16-bit integer to its string representation
|
||||
*
|
||||
|
Reference in New Issue
Block a user