// This is modified from is_numeric_string in zend_operators.h. The behavior of // this function is the same as is_numeric_string, except that this takes // int64_t as input instead of long. static zend_uchar convert_numeric_string( const char *str, int length, int64_t *lval, double *dval) { const char *ptr; int base = 10, digits = 0, dp_or_e = 0; double local_dval = 0.0; zend_uchar type; if (length == 0) { return IS_NULL; } while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') { str++; length--; } ptr = str; if (*ptr == '-' || *ptr == '+') { ptr++; } if (ZEND_IS_DIGIT(*ptr)) { // Handle hex numbers // str is used instead of ptr to disallow signs and keep old behavior. if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) { base = 16; ptr += 2; } // Skip any leading 0s. while (*ptr == '0') { ptr++; } // Count the number of digits. If a decimal point/exponent is found, // it's a double. Otherwise, if there's a dval or no need to check for // a full match, stop when there are too many digits for a int64 */ for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_INT64 && dval); digits++, ptr++) { check_digits: if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) { continue; } else if (base == 10) { if (*ptr == '.' && dp_or_e < 1) { goto process_double; } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) { const char *e = ptr + 1; if (*e == '-' || *e == '+') { ptr = e++; } if (ZEND_IS_DIGIT(*e)) { goto process_double; } } } break; } if (base == 10) { if (digits >= MAX_LENGTH_OF_INT64) { dp_or_e = -1; goto process_double; } } else if (!(digits < SIZEOF_INT64 * 2 || (digits == SIZEOF_INT64 * 2 && ptr[-digits] <= '7'))) { if (dval) { local_dval = zend_hex_strtod(str, &ptr); } type = IS_DOUBLE; } } else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) { process_double: type = IS_DOUBLE; // If there's a dval, do the conversion; else continue checking // the digits if we need to check for a full match. if (dval) { local_dval = zend_strtod(str, &ptr); } else if (dp_or_e != -1) { dp_or_e = (*ptr++ == '.') ? 1 : 2; goto check_digits; } } else { return IS_NULL; } if (ptr != str + length) { zend_error(E_NOTICE, "A non well formed numeric value encountered"); return 0; } if (type == IS_LONG) { if (digits == MAX_LENGTH_OF_INT64 - 1) { int cmp = strcmp(&ptr[-digits], int64_min_digits); if (!(cmp < 0 || (cmp == 0 && *str == '-'))) { if (dval) { *dval = zend_strtod(str, NULL); } return IS_DOUBLE; } } if (lval) { *lval = strtoll(str, NULL, base); } return IS_LONG; } else { if (dval) { *dval = local_dval; } return IS_DOUBLE; } }
DataType is_numeric_string(const char *str, int length, int64_t *lval, double *dval, int allow_errors /* = 0 */) { DataType type; const char *ptr; int base = 10, digits = 0, dp_or_e = 0; double local_dval = 0.0; if (!length || ((unsigned char)(*str)) > '9') { return KindOfNull; } /* Skip any whitespace * This is much faster than the isspace() function */ while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') { str++; length--; } ptr = str; if (*ptr == '-' || *ptr == '+') { ptr++; } if (IS_DIGIT(*ptr)) { /* Handle hex numbers * str is used instead of ptr to disallow signs and keep old behavior */ if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) { base = 16; ptr += 2; } /* Skip any leading 0s */ while (*ptr == '0') { ptr++; } /* Count the number of digits. If a decimal point/exponent is found, * it's a double. Otherwise, if there's a dval or no need to check for * a full match, stop when there are too many digits for a int64 */ for (type = KindOfInt64; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) { check_digits: if (IS_DIGIT(*ptr) || (base == 16 && IS_XDIGIT(*ptr))) { continue; } else if (base == 10) { if (*ptr == '.' && dp_or_e < 1) { goto process_double; } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) { const char *e = ptr + 1; if (*e == '-' || *e == '+') { ptr = e++; } if (IS_DIGIT(*e)) { goto process_double; } } } break; } if (base == 10) { if (digits >= MAX_LENGTH_OF_LONG) { dp_or_e = -1; goto process_double; } } else if (!(digits < SIZEOF_LONG * 2 || (digits == SIZEOF_LONG * 2 && ptr[-digits] <= '7'))) { if (dval) { local_dval = zend_hex_strtod(str, (const char **)&ptr); } type = KindOfDouble; } } else if (*ptr == '.' && IS_DIGIT(ptr[1])) { process_double: type = KindOfDouble; /* If there's a dval, do the conversion; else continue checking * the digits if we need to check for a full match */ if (dval) { /* This section can be called while static strings are initialized. * Creating the bigint pool is usually done on thread initialization, * but this happens even before then. */ zend_get_bigint_data(); local_dval = zend_strtod(str, (const char **)&ptr); } else if (allow_errors != 1 && dp_or_e != -1) { dp_or_e = (*ptr++ == '.') ? 1 : 2; goto check_digits; } } else { return KindOfNull; } if (ptr != str + length) { if (!allow_errors) { return KindOfNull; } // if (allow_errors == -1) { // zend_error(E_NOTICE, "A non well formed numeric value encountered"); // } } if (type == KindOfInt64) { if (digits == MAX_LENGTH_OF_LONG - 1) { int cmp = strcmp(&ptr[-digits], long_min_digits); if (!(cmp < 0 || (cmp == 0 && *str == '-'))) { if (dval) { *dval = strtod(str, nullptr); } return KindOfDouble; } } if (lval) { *lval = strtol(str, nullptr, base); } return KindOfInt64; } if (dval) { *dval = local_dval; } return KindOfDouble; }