|
@@ -34,6 +34,7 @@
|
|
#include "utf8.h"
|
|
#include "utf8.h"
|
|
|
|
|
|
static zend_class_entry* util_type;
|
|
static zend_class_entry* util_type;
|
|
|
|
+static const char int64_min_digits[] = "9223372036854775808";
|
|
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1)
|
|
ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1)
|
|
ZEND_ARG_INFO(1, val)
|
|
ZEND_ARG_INFO(1, val)
|
|
@@ -78,8 +79,128 @@ void util_init(TSRMLS_D) {
|
|
// Type checking/conversion.
|
|
// Type checking/conversion.
|
|
// -----------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
+// 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;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
#define CONVERT_TO_INTEGER(type) \
|
|
#define CONVERT_TO_INTEGER(type) \
|
|
- static bool convert_long_to_##type(long val, type##_t* type##_value) { \
|
|
|
|
|
|
+ static bool convert_int64_to_##type(int64_t val, type##_t* type##_value) { \
|
|
*type##_value = (type##_t)val; \
|
|
*type##_value = (type##_t)val; \
|
|
return true; \
|
|
return true; \
|
|
} \
|
|
} \
|
|
@@ -91,15 +212,15 @@ void util_init(TSRMLS_D) {
|
|
\
|
|
\
|
|
static bool convert_string_to_##type(const char* val, int len, \
|
|
static bool convert_string_to_##type(const char* val, int len, \
|
|
type##_t* type##_value) { \
|
|
type##_t* type##_value) { \
|
|
- long lval; \
|
|
|
|
|
|
+ int64_t lval; \
|
|
double dval; \
|
|
double dval; \
|
|
\
|
|
\
|
|
- switch (is_numeric_string(val, len, &lval, &dval, 0)) { \
|
|
|
|
|
|
+ switch (convert_numeric_string(val, len, &lval, &dval)) { \
|
|
case IS_DOUBLE: { \
|
|
case IS_DOUBLE: { \
|
|
return convert_double_to_##type(dval, type##_value); \
|
|
return convert_double_to_##type(dval, type##_value); \
|
|
} \
|
|
} \
|
|
case IS_LONG: { \
|
|
case IS_LONG: { \
|
|
- return convert_long_to_##type(lval, type##_value); \
|
|
|
|
|
|
+ return convert_int64_to_##type(lval, type##_value); \
|
|
} \
|
|
} \
|
|
default: \
|
|
default: \
|
|
zend_error(E_USER_ERROR, \
|
|
zend_error(E_USER_ERROR, \
|
|
@@ -111,7 +232,7 @@ void util_init(TSRMLS_D) {
|
|
bool protobuf_convert_to_##type(zval* from, type##_t* to) { \
|
|
bool protobuf_convert_to_##type(zval* from, type##_t* to) { \
|
|
switch (Z_TYPE_P(from)) { \
|
|
switch (Z_TYPE_P(from)) { \
|
|
case IS_LONG: { \
|
|
case IS_LONG: { \
|
|
- return convert_long_to_##type(Z_LVAL_P(from), to); \
|
|
|
|
|
|
+ return convert_int64_to_##type(Z_LVAL_P(from), to); \
|
|
} \
|
|
} \
|
|
case IS_DOUBLE: { \
|
|
case IS_DOUBLE: { \
|
|
return convert_double_to_##type(Z_DVAL_P(from), to); \
|
|
return convert_double_to_##type(Z_DVAL_P(from), to); \
|
|
@@ -137,7 +258,7 @@ CONVERT_TO_INTEGER(uint64);
|
|
#undef CONVERT_TO_INTEGER
|
|
#undef CONVERT_TO_INTEGER
|
|
|
|
|
|
#define CONVERT_TO_FLOAT(type) \
|
|
#define CONVERT_TO_FLOAT(type) \
|
|
- static bool convert_long_to_##type(long val, type* type##_value) { \
|
|
|
|
|
|
+ static bool convert_int64_to_##type(int64_t val, type* type##_value) { \
|
|
*type##_value = (type)val; \
|
|
*type##_value = (type)val; \
|
|
return true; \
|
|
return true; \
|
|
} \
|
|
} \
|
|
@@ -149,10 +270,10 @@ CONVERT_TO_INTEGER(uint64);
|
|
\
|
|
\
|
|
static bool convert_string_to_##type(const char* val, int len, \
|
|
static bool convert_string_to_##type(const char* val, int len, \
|
|
type* type##_value) { \
|
|
type* type##_value) { \
|
|
- long lval; \
|
|
|
|
|
|
+ int64_t lval; \
|
|
double dval; \
|
|
double dval; \
|
|
\
|
|
\
|
|
- switch (is_numeric_string(val, len, &lval, &dval, 0)) { \
|
|
|
|
|
|
+ switch (convert_numeric_string(val, len, &lval, &dval)) { \
|
|
case IS_DOUBLE: { \
|
|
case IS_DOUBLE: { \
|
|
*type##_value = (type)dval; \
|
|
*type##_value = (type)dval; \
|
|
return true; \
|
|
return true; \
|
|
@@ -171,7 +292,7 @@ CONVERT_TO_INTEGER(uint64);
|
|
bool protobuf_convert_to_##type(zval* from, type* to) { \
|
|
bool protobuf_convert_to_##type(zval* from, type* to) { \
|
|
switch (Z_TYPE_P(from)) { \
|
|
switch (Z_TYPE_P(from)) { \
|
|
case IS_LONG: { \
|
|
case IS_LONG: { \
|
|
- return convert_long_to_##type(Z_LVAL_P(from), to); \
|
|
|
|
|
|
+ return convert_int64_to_##type(Z_LVAL_P(from), to); \
|
|
} \
|
|
} \
|
|
case IS_DOUBLE: { \
|
|
case IS_DOUBLE: { \
|
|
return convert_double_to_##type(Z_DVAL_P(from), to); \
|
|
return convert_double_to_##type(Z_DVAL_P(from), to); \
|