Example #1
0
// 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;
  }
}
Example #2
0
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;
}