[FFmpeg-devel] [PATCH] eval: implement av_strtod internally.

Nicolas George nicolas.george at normalesup.org
Sun Feb 19 15:02:42 CET 2012


Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 libavutil/eval.c |  155 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 127 insertions(+), 28 deletions(-)


I do not know if people will like it, and I will not insist if not.

This patch implements our own version of strtod, with pros and cons;
hopefully more pros but YMMV. The cons are:

- More code to maintain.
- Does not set errno (we do not use it).
- Does not support "NAN(blah)".
- Probably less optimized than the libc's.

The pros, on the other hand:

- Does not break if LC_NUMERIC is set (LC_NUMERIC is synonym with
  PLEASE_BREAK_HALF_MY_CODE, but still, some program init it).

- Supports hexadecimal non-integers (the current implementation implements
  hexadecimal integers by wrapping strtod, therefore making it impossible to
  use them if they are supported; this would be easily fixed though).

- Can use a string terminated by a pointer rather than an extra NUL. This is
  useful when parsing a substring, to avoid copying it.

Regards,

-- 
  Nicolas George


diff --git a/libavutil/eval.c b/libavutil/eval.c
index 2ee3965..efc2105 100644
--- a/libavutil/eval.c
+++ b/libavutil/eval.c
@@ -81,39 +81,138 @@ static const struct {
     { "PHI", M_PHI },
 };
 
-double av_strtod(const char *numstr, char **tail)
+#define LOW(c) ((c) | 0x20)
+
+static int casematch(const char *pat, unsigned len,
+                     const char *s, const char *end)
+                     
 {
-    double d;
-    char *next;
-    if(numstr[0]=='0' && (numstr[1]|0x20)=='x') {
-        d = strtoul(numstr, &next, 16);
-    } else
-        d = strtod(numstr, &next);
-    /* if parsing succeeded, check for and interpret postfixes */
-    if (next!=numstr) {
-        if (*next >= 'E' && *next <= 'z') {
-            int e= si_prefixes[*next - 'E'];
-            if (e) {
-                if (next[1] == 'i') {
-                    d*= pow( 2, e/0.3);
-                    next+=2;
-                } else {
-                    d*= pow(10, e);
-                    next++;
-                }
-            }
+    if (end - s < len)
+        return 0;
+    while (len-- > 0)
+        if (LOW(*(s++)) != *(pat++))
+            return 0;
+    return 1;
+}
+
+static double av_strtod_ended(const char *numstr, const char *numstr_end,
+                              char **rtail)
+{
+    const char *cur = numstr, *tail = numstr;
+    double ret = 0;
+    unsigned neg = 0, hexa = 0, part, exp_neg = 0, exp = 0;
+    unsigned nb_digits[2] = { 0, 0 };
+    double mantissa = 0, base = 10;
+    char exp_mark = 'e';
+
+    /* skip leading spaces */
+    while (cur < numstr_end && (*cur == ' '  || *cur == '\t' || *cur == '\n' ||
+                                *cur == '\r' || *cur == '\f' || *cur == '\v'))
+        cur++;
+    if (cur == numstr_end) goto finish;
+
+    /* sign, named constants and base prefix */
+    if (*cur == '-' || *cur == '+')
+        neg = *(cur++) == '-';
+    if (cur == numstr_end) goto finish;
+    if (casematch("inf", 3, cur, numstr_end)) {
+        ret = neg ? -INFINITY : INFINITY;
+        tail = cur + (casematch("inity", 5, cur + 3, numstr_end) ? 8 : 3);
+        goto finish;
+    }
+    if (casematch("nan", 3, cur, numstr_end)) {
+        ret = NAN;
+        tail = cur + 3;
+        goto finish;
+    }
+    if (*cur == '0' && cur + 1 < numstr_end && LOW(cur[1]) == 'x') {
+        tail = cur + 1; /* if all fails, we still got a 0 */
+        hexa = 1;
+        base = 16;
+        exp_mark = 'p';
+        cur += 2;
+    }
+
+    /* integer and decimal part */
+    if (cur == numstr_end) goto finish;
+    for (part = 0; part < 2; part++) { /* 0 = integer part, 1 = decimal part */
+        while (cur < numstr_end) {
+            if (!(*cur - '0' <= 9U || (hexa && LOW(*cur) - 'a' <= 5U)))
+                break;
+            mantissa = mantissa * base +
+                       (*cur - '0' <= 9U ? *cur - '0' : LOW(*cur) - 'a' + 10);
+            nb_digits[part]++;
+            cur++;
+        }
+        if (part || !(cur < numstr_end && *cur == '.'))
+            break;
+        cur++;
+    }
+    if (!nb_digits[0] && !nb_digits[1])
+        goto finish;
+    tail = cur;
+
+    /* exponent */
+    if (cur < numstr_end && LOW(*cur) == exp_mark) {
+        cur++;
+        if (cur < numstr_end && (*cur == '-' || *cur == '+'))
+            exp_neg = *(cur++) == '-';
+        while (cur < numstr_end && *cur - '0' <= 9U) {
+            exp = exp * 10 + *(cur++) - '0';
+            tail = cur;
         }
+    }
+
+    /* include the position of the . in the exponent, keeping it unsigned */
+    if (exp_neg) {
+        exp += nb_digits[1];
+    } else if (exp >= nb_digits[1]) {
+        exp -= nb_digits[1];
+    } else {
+        exp_neg = 1;
+        exp = nb_digits[1] - exp;
+    }
 
-        if (*next=='B') {
-            d*=8;
-            next++;
+    /* apply the exponent and sign */
+    if (exp_neg) {
+        for (; exp; exp >>= 1, base *= base)
+            if (exp & 1)
+                mantissa /= base;
+    } else {
+        for (; exp; exp >>= 1, base *= base)
+            if (exp & 1)
+                mantissa *= base;
+    }
+    ret = neg ? -mantissa : mantissa;
+
+    /* SI suffixes */
+    if (cur < numstr_end && *cur - 'E' <= (unsigned)('z' - 'E')) {
+        exp = si_prefixes[*cur - 'E'];
+        if (exp) {
+            if (++cur < numstr_end && *cur == 'i') {
+                ret *= pow(1024, exp / 3);
+                cur++;
+            } else {
+                ret *= pow(10, exp);
+            }
+            if (cur < numstr_end && *cur == 'B') {
+                ret *= 8;
+                cur++;
+            }
+            tail = cur;
         }
     }
-    /* if requested, fill in tail with the position after the last parsed
-       character */
-    if (tail)
-        *tail = next;
-    return d;
+
+finish:
+    if (rtail)
+        *rtail = (char *)(intptr_t)tail; /* discard const */
+    return ret;
+
+}
+
+double av_strtod(const char *numstr, char **tail)
+{
+    return av_strtod_ended(numstr, numstr + strlen(numstr), tail);
 }
 
 #define IS_IDENTIFIER_CHAR(c) ((c) - '0' <= 9U || (c) - 'a' <= 25U || (c) - 'A' <= 25U || (c) == '_')
-- 
1.7.9



More information about the ffmpeg-devel mailing list