pg_strtoint_fastpath4.patch
application/octet-stream
Filename: pg_strtoint_fastpath4.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/utils/adt/numutils.c | 246 | 21 |
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index 471fbb7ee6..7067bc5d48 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -122,9 +122,84 @@ pg_strtoint16_safe(const char *s, Node *escontext)
const char *firstdigit;
uint16 tmp = 0;
bool neg = false;
+ unsigned char digit;
+
+ /*
+ * The majority of cases are likely to be base-10 digits without any
+ * underscore separator characters. We'll first try to parse the string with
+ * the assumption that's the case and only fallback on a slower
+ * implementation which handles hex, octal and binary strings and
+ * underscores if the fastpath version cannot parse the string.
+ */
+
+ /* leave it up to the slow path to look for leading spaces */
+
+ if (*ptr == '-')
+ {
+ ptr++;
+ neg = true;
+ }
+
+ /* a leading '+' is uncommon so leave that for the slow path */
+
+ /* process the first digit */
+ digit = (*ptr - '0');
+
+ /*
+ * Exploit unsigned arithmetic to save having to check both the upper and
+ * lower bounds of the digit.
+ */
+ if (likely(digit < 10))
+ {
+ ptr++;
+ tmp = digit;
+ }
+ else
+ {
+ /* we need at least one digit */
+ goto slow;
+ }
+
+ /* process remaining digits */
+ for (;;)
+ {
+ digit = (*ptr - '0');
+
+ if (digit >= 10)
+ break;
+
+ ptr++;
+
+ if (unlikely(tmp > -(PG_INT16_MIN / 10)))
+ goto out_of_range;
+
+ tmp = tmp * 10 + digit;
+ }
+
+ /* when the string does not end in a digit, let the slow path handle it */
+ if (unlikely(*ptr != '\0'))
+ goto slow;
+
+ if (neg)
+ {
+ /* check the negative equivalent will fit without overflowing */
+ if (unlikely(tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1))
+ goto out_of_range;
+ return -((int16) tmp);
+ }
+
+ if (unlikely(tmp > PG_INT16_MAX))
+ goto out_of_range;
+
+ return (int16) tmp;
+
+slow:
+ tmp = 0;
+ ptr = s;
+ /* no need to reset neg */
/* skip leading spaces */
- while (likely(*ptr) && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
/* handle sign */
@@ -141,7 +216,7 @@ pg_strtoint16_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (isxdigit((unsigned char) *ptr))
{
@@ -165,7 +240,7 @@ pg_strtoint16_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '7')
{
@@ -189,7 +264,7 @@ pg_strtoint16_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '1')
{
@@ -213,9 +288,9 @@ pg_strtoint16_safe(const char *s, Node *escontext)
{
firstdigit = ptr;
- while (*ptr)
+ for (;;)
{
- if (isdigit((unsigned char) *ptr))
+ if (*ptr >= '0' && *ptr <= '9')
{
if (unlikely(tmp > -(PG_INT16_MIN / 10)))
goto out_of_range;
@@ -242,7 +317,7 @@ pg_strtoint16_safe(const char *s, Node *escontext)
goto invalid_syntax;
/* allow trailing whitespace, but not other trailing chars */
- while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
if (unlikely(*ptr != '\0'))
@@ -300,9 +375,84 @@ pg_strtoint32_safe(const char *s, Node *escontext)
const char *firstdigit;
uint32 tmp = 0;
bool neg = false;
+ unsigned char digit;
+
+ /*
+ * The majority of cases are likely to be base-10 digits without any
+ * underscore separator characters. We'll first try to parse the string with
+ * the assumption that's the case and only fallback on a slower
+ * implementation which handles hex, octal and binary strings and
+ * underscores if the fastpath version cannot parse the string.
+ */
+
+ /* leave it up to the slow path to look for leading spaces */
+
+ if (*ptr == '-')
+ {
+ ptr++;
+ neg = true;
+ }
+
+ /* a leading '+' is uncommon so leave that for the slow path */
+
+ /* process the first digit */
+ digit = (*ptr - '0');
+
+ /*
+ * Exploit unsigned arithmetic to save having to check both the upper and
+ * lower bounds of the digit.
+ */
+ if (likely(digit < 10))
+ {
+ ptr++;
+ tmp = digit;
+ }
+ else
+ {
+ /* we need at least one digit */
+ goto slow;
+ }
+
+ /* process remaining digits */
+ for (;;)
+ {
+ digit = (*ptr - '0');
+
+ if (digit >= 10)
+ break;
+
+ ptr++;
+
+ if (unlikely(tmp > -(PG_INT32_MIN / 10)))
+ goto out_of_range;
+
+ tmp = tmp * 10 + digit;
+ }
+
+ /* when the string does not end in a digit, let the slow path handle it */
+ if (unlikely(*ptr != '\0'))
+ goto slow;
+
+ if (neg)
+ {
+ /* check the negative equivalent will fit without overflowing */
+ if (unlikely(tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1))
+ goto out_of_range;
+ return -((int32) tmp);
+ }
+
+ if (unlikely(tmp > PG_INT32_MAX))
+ goto out_of_range;
+
+ return (int32) tmp;
+
+slow:
+ tmp = 0;
+ ptr = s;
+ /* no need to reset neg */
/* skip leading spaces */
- while (likely(*ptr) && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
/* handle sign */
@@ -319,7 +469,7 @@ pg_strtoint32_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (isxdigit((unsigned char) *ptr))
{
@@ -343,7 +493,7 @@ pg_strtoint32_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '7')
{
@@ -367,7 +517,7 @@ pg_strtoint32_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '1')
{
@@ -391,9 +541,9 @@ pg_strtoint32_safe(const char *s, Node *escontext)
{
firstdigit = ptr;
- while (*ptr)
+ for (;;)
{
- if (isdigit((unsigned char) *ptr))
+ if (*ptr >= '0' && *ptr <= '9')
{
if (unlikely(tmp > -(PG_INT32_MIN / 10)))
goto out_of_range;
@@ -420,7 +570,7 @@ pg_strtoint32_safe(const char *s, Node *escontext)
goto invalid_syntax;
/* allow trailing whitespace, but not other trailing chars */
- while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
if (unlikely(*ptr != '\0'))
@@ -478,9 +628,84 @@ pg_strtoint64_safe(const char *s, Node *escontext)
const char *firstdigit;
uint64 tmp = 0;
bool neg = false;
+ unsigned char digit;
+
+ /*
+ * The majority of cases are likely to be base-10 digits without any
+ * underscore separator characters. We'll first try to parse the string with
+ * the assumption that's the case and only fallback on a slower
+ * implementation which handles hex, octal and binary strings and
+ * underscores if the fastpath version cannot parse the string.
+ */
+
+ /* leave it up to the slow path to look for leading spaces */
+
+ if (*ptr == '-')
+ {
+ ptr++;
+ neg = true;
+ }
+
+ /* a leading '+' is uncommon so leave that for the slow path */
+
+ /* process the first digit */
+ digit = (*ptr - '0');
+
+ /*
+ * Exploit unsigned arithmetic to save having to check both the upper and
+ * lower bounds of the digit.
+ */
+ if (likely(digit < 10))
+ {
+ ptr++;
+ tmp = digit;
+ }
+ else
+ {
+ /* we need at least one digit */
+ goto slow;
+ }
+
+ /* process remaining digits */
+ for (;;)
+ {
+ digit = (*ptr - '0');
+
+ if (digit >= 10)
+ break;
+
+ ptr++;
+
+ if (unlikely(tmp > -(PG_INT64_MIN / 10)))
+ goto out_of_range;
+
+ tmp = tmp * 10 + digit;
+ }
+
+ /* when the string does not end in a digit, let the slow path handle it */
+ if (unlikely(*ptr != '\0'))
+ goto slow;
+
+ if (neg)
+ {
+ /* check the negative equivalent will fit without overflowing */
+ if (unlikely(tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1))
+ goto out_of_range;
+ return -((int64) tmp);
+ }
+
+ if (unlikely(tmp > PG_INT64_MAX))
+ goto out_of_range;
+
+ return (int64) tmp;
+
+slow:
+ tmp = 0;
+ ptr = s;
+ /* no need to reset neg */
/* skip leading spaces */
- while (*ptr && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
/* handle sign */
@@ -497,7 +722,7 @@ pg_strtoint64_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (isxdigit((unsigned char) *ptr))
{
@@ -521,7 +746,7 @@ pg_strtoint64_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '7')
{
@@ -545,7 +770,7 @@ pg_strtoint64_safe(const char *s, Node *escontext)
{
firstdigit = ptr += 2;
- while (*ptr)
+ for (;;)
{
if (*ptr >= '0' && *ptr <= '1')
{
@@ -569,9 +794,9 @@ pg_strtoint64_safe(const char *s, Node *escontext)
{
firstdigit = ptr;
- while (*ptr)
+ for (;;)
{
- if (isdigit((unsigned char) *ptr))
+ if (*ptr >= '0' && *ptr <= '9')
{
if (unlikely(tmp > -(PG_INT64_MIN / 10)))
goto out_of_range;
@@ -598,7 +823,7 @@ pg_strtoint64_safe(const char *s, Node *escontext)
goto invalid_syntax;
/* allow trailing whitespace, but not other trailing chars */
- while (*ptr != '\0' && isspace((unsigned char) *ptr))
+ while (isspace((unsigned char) *ptr))
ptr++;
if (unlikely(*ptr != '\0'))