]> granicus.if.org Git - php/commitdiff
Rework some string-to-number and vice-versa functions to work smarter
authorSascha Schumann <sas@php.net>
Thu, 20 Sep 2001 08:22:44 +0000 (08:22 +0000)
committerSascha Schumann <sas@php.net>
Thu, 20 Sep 2001 08:22:44 +0000 (08:22 +0000)
(e.g. without using functions that use slow locales, without allocating
 extra memory and with fewer integer/float operations)

ext/standard/math.c

index 28ca2557212bc6ce6948666746c243a197df34a5..805a4e1e067f01b503843abe7c4431fde861acbe 100644 (file)
@@ -664,7 +664,7 @@ PHP_FUNCTION(rad2deg)
  */
 PHPAPI long
 _php_math_basetolong(zval *arg, int base) {
-       long mult = 1, num = 0, digit;
+       long num = 0, digit, onum;
        int i;
        char c, *s;
 
@@ -674,23 +674,25 @@ _php_math_basetolong(zval *arg, int base) {
 
        s = Z_STRVAL_P(arg);
 
-       for (i = Z_STRLEN_P(arg) - 1; i >= 0; i--, mult *= base) {
-               c = toupper(s[i]);
-               if (c >= '0' && c <= '9') {
-                       digit = (c - '0');
-               } else if (c >= 'A' && c <= 'Z') {
-                       digit = (c - 'A' + 10);
-               } else {
-                       continue;
-               }
+       for (i = Z_STRLEN_P(arg); i > 0; i--) {
+               c = *s++;
+               
+               digit = (c >= '0' && c <= '9') ? c - '0'
+                       : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
+                       : (c >= 'a' && c <= 'z') ? c - 'a' + 10
+                       : base;
+               
                if (digit >= base) {
                        continue;
                }
-               if(!mult || digit > LONG_MAX/mult || num > LONG_MAX-mult*digit) {
-                       php_error(E_WARNING, "base_to_long: number '%s' is too big to fit in long", s);
-                       return LONG_MAX;
-               }
-               num += mult * digit;
+
+               onum = num;
+               num = num * base + digit;
+               if (num > onum)
+                       continue;
+
+               php_error(E_WARNING, "base_to_long: number '%s' is too big to fit in long", s);
+               return LONG_MAX;
        }
 
        return num;
@@ -764,8 +766,8 @@ PHPAPI char *
 _php_math_longtobase(zval *arg, int base)
 {
        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-       char *result, *ptr, *ret;
-       int len, digit;
+       char buf[(sizeof(unsigned long) << 3) + 1];
+       char *ptr, *end;
        unsigned long value;
 
        if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
@@ -774,25 +776,16 @@ _php_math_longtobase(zval *arg, int base)
 
        value = Z_LVAL_P(arg);
 
-       /* allocates space for the longest possible result with the lowest base */
-       len = (sizeof(Z_LVAL_P(arg)) * 8) + 1;
-       result = emalloc((sizeof(Z_LVAL_P(arg)) * 8) + 1);
-
-       ptr = result + len - 1;
-       *ptr-- = '\0';
+       end = ptr = buf + sizeof(buf) - 1;
+       *ptr = '\0';
 
        do {
-               digit = value % base;
-               *ptr = digits[digit];
+               *--ptr = digits[value % base];
                value /= base;
-       }
-       while (ptr-- > result && value);
-       ptr++;
-       ret = estrdup(ptr);
-       efree(result);
+       } while (ptr > buf && value);
 
-       return ret;
-}      
+       return estrndup(ptr, end - ptr);
+}
 
 /* }}} */
 /* {{{ _php_math_zvaltobase */
@@ -805,49 +798,28 @@ PHPAPI char *
 _php_math_zvaltobase(zval *arg, int base)
 {
        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-       char *result, *ptr, *ret;
-       int len, digit;
-       unsigned long value;
-       double fvalue;
-       int f_mode;
 
        if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
                return empty_string;
        }
 
-       f_mode = (Z_TYPE_P(arg) == IS_DOUBLE);
-
-       if(f_mode) {
-               fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
-       } else {
-               value = Z_LVAL_P(arg);
-       }
+       if (Z_TYPE_P(arg) == IS_DOUBLE) {
+               double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
+               char *ptr, *end;
+               char buf[(sizeof(double) << 3) + 1];
 
-       /* allocates space for the longest possible result with the lowest base */
-       len = (sizeof(Z_DVAL_P(arg)) * 8) + 1;
-       result = emalloc((sizeof(Z_DVAL_P(arg)) * 8) + 1);
+               end = ptr = buf + sizeof(buf) - 1;
+               *ptr = '\0';
 
-       ptr = result + len - 1;
-       *ptr-- = '\0';
+               do {
+                       *--ptr = digits[(int) fmod(fvalue, base)];
+                       fvalue /= base;
+               } while (ptr > buf && fabs(fvalue) >= 1);
 
-       do {
-               if(f_mode) {
-                       double d = floor(fvalue/base);
-                       digit = (int)ceil(fvalue - d*base);
-                       *ptr = digits[digit];
-                       fvalue = d;
-               } else {
-                       digit = value % base;
-                       *ptr = digits[digit];
-                       value /= base;
-               }
+               return estrndup(ptr, end - ptr);
        }
-       while (ptr-- > result && (f_mode?(fabs(fvalue)>=1):value));
-       ptr++;
-       ret = estrdup(ptr);
-       efree(result);
-
-       return ret;
+       
+       return _php_math_longtobase(arg, base);
 }      
 
 /* }}} */