[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