eval.c 16.5 KB
Newer Older
1
/*
2
 * Copyright (c) 2002-2006 Michael Niedermayer <michaelni@gmx.at>
3
 * Copyright (c) 2006 Oded Shimon <ods15@ods15.dyndns.org>
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 21
 */

Michael Niedermayer's avatar
Michael Niedermayer committed
22
/**
23
 * @file
Michael Niedermayer's avatar
Michael Niedermayer committed
24 25
 * simple arithmetic expression evaluator.
 *
26 27 28
 * see http://joe.hotchkiss.com/programming/eval/eval.html
 */

29
#include "libavutil/avutil.h"
30 31
#include "eval.h"

32
typedef struct Parser {
33
    const AVClass *class;
34 35
    int stack_index;
    char *s;
36 37 38 39 40 41
    const double *const_values;
    const char * const *const_names;          // NULL terminated
    double (* const *funcs1)(void *, double a);           // NULL terminated
    const char * const *func1_names;          // NULL terminated
    double (* const *funcs2)(void *, double a, double b); // NULL terminated
    const char * const *func2_names;          // NULL terminated
42
    void *opaque;
43 44
    int log_offset;
    void *log_ctx;
45 46
#define VARS 10
    double var[VARS];
47 48
} Parser;

49 50
static const AVClass class = { "Eval", av_default_item_name, NULL, LIBAVUTIL_VERSION_INT, offsetof(Parser,log_offset), offsetof(Parser,log_ctx) };

51
static const int8_t si_prefixes['z' - 'E' + 1] = {
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    ['y'-'E']= -24,
    ['z'-'E']= -21,
    ['a'-'E']= -18,
    ['f'-'E']= -15,
    ['p'-'E']= -12,
    ['n'-'E']= - 9,
    ['u'-'E']= - 6,
    ['m'-'E']= - 3,
    ['c'-'E']= - 2,
    ['d'-'E']= - 1,
    ['h'-'E']=   2,
    ['k'-'E']=   3,
    ['K'-'E']=   3,
    ['M'-'E']=   6,
    ['G'-'E']=   9,
    ['T'-'E']=  12,
    ['P'-'E']=  15,
    ['E'-'E']=  18,
    ['Z'-'E']=  21,
    ['Y'-'E']=  24,
};

74 75
double av_strtod(const char *numstr, char **tail)
{
76 77
    double d;
    char *next;
78
    d = strtod(numstr, &next);
79
    /* if parsing succeeded, check for and interpret postfixes */
80
    if (next!=numstr) {
81
        if (*next >= 'E' && *next <= 'z') {
82
            int e= si_prefixes[*next - 'E'];
83 84
            if (e) {
                if (next[1] == 'i') {
85 86
                    d*= pow( 2, e/0.3);
                    next+=2;
87
                } else {
88 89 90 91 92 93
                    d*= pow(10, e);
                    next++;
                }
            }
        }

94
        if (*next=='B') {
95
            d*=8;
96
            next++;
97 98 99 100 101 102 103 104 105
        }
    }
    /* if requested, fill in tail with the position after the last parsed
       character */
    if (tail)
        *tail = next;
    return d;
}

106 107
static int strmatch(const char *s, const char *prefix)
{
108
    int i;
109 110
    for (i=0; prefix[i]; i++) {
        if (prefix[i] != s[i]) return 0;
111 112 113 114
    }
    return 1;
}

115
struct AVExpr {
116 117
    enum {
        e_value, e_const, e_func0, e_func1, e_func2,
118
        e_squish, e_gauss, e_ld,
119 120
        e_mod, e_max, e_min, e_eq, e_gt, e_gte,
        e_pow, e_mul, e_div, e_add,
121
        e_last, e_st, e_while,
122 123 124 125 126 127 128 129
    } type;
    double value; // is sign in other types
    union {
        int const_index;
        double (*func0)(double);
        double (*func1)(void *, double);
        double (*func2)(void *, double, double);
    } a;
130
    struct AVExpr *param[2];
131 132
};

133 134
static double eval_expr(Parser *p, AVExpr *e)
{
135 136
    switch (e->type) {
        case e_value:  return e->value;
137
        case e_const:  return e->value * p->const_values[e->a.const_index];
138 139 140 141 142
        case e_func0:  return e->value * e->a.func0(eval_expr(p, e->param[0]));
        case e_func1:  return e->value * e->a.func1(p->opaque, eval_expr(p, e->param[0]));
        case e_func2:  return e->value * e->a.func2(p->opaque, eval_expr(p, e->param[0]), eval_expr(p, e->param[1]));
        case e_squish: return 1/(1+exp(4*eval_expr(p, e->param[0])));
        case e_gauss: { double d = eval_expr(p, e->param[0]); return exp(-d*d/2)/sqrt(2*M_PI); }
143
        case e_ld:     return e->value * p->var[av_clip(eval_expr(p, e->param[0]), 0, VARS-1)];
144
        case e_while: {
145
            double d = NAN;
146
            while (eval_expr(p, e->param[0]))
147 148 149
                d=eval_expr(p, e->param[1]);
            return d;
        }
150 151 152 153 154 155 156 157 158 159 160 161 162 163
        default: {
            double d = eval_expr(p, e->param[0]);
            double d2 = eval_expr(p, e->param[1]);
            switch (e->type) {
                case e_mod: return e->value * (d - floor(d/d2)*d2);
                case e_max: return e->value * (d >  d2 ?   d : d2);
                case e_min: return e->value * (d <  d2 ?   d : d2);
                case e_eq:  return e->value * (d == d2 ? 1.0 : 0.0);
                case e_gt:  return e->value * (d >  d2 ? 1.0 : 0.0);
                case e_gte: return e->value * (d >= d2 ? 1.0 : 0.0);
                case e_pow: return e->value * pow(d, d2);
                case e_mul: return e->value * (d * d2);
                case e_div: return e->value * (d / d2);
                case e_add: return e->value * (d + d2);
Oded Shimon's avatar
Oded Shimon committed
164
                case e_last:return e->value * d2;
165
                case e_st : return e->value * (p->var[av_clip(d, 0, VARS-1)]= d2);
166 167 168 169 170 171
            }
        }
    }
    return NAN;
}

172
static int parse_expr(AVExpr **e, Parser *p);
173

174
void av_free_expr(AVExpr *e)
175
{
176
    if (!e) return;
177 178
    av_free_expr(e->param[0]);
    av_free_expr(e->param[1]);
179 180 181
    av_freep(&e);
}

182 183
static int parse_primary(AVExpr **e, Parser *p)
{
184
    AVExpr *d = av_mallocz(sizeof(AVExpr));
185
    char *next = p->s, *s0 = p->s;
186
    int ret, i;
187

188
    if (!d)
189
        return AVERROR(ENOMEM);
190

191
    /* number */
192
    d->value = av_strtod(p->s, &next);
193
    if (next != p->s) {
194
        d->type = e_value;
195
        p->s= next;
196 197
        *e = d;
        return 0;
198
    }
199
    d->value = 1;
200

201
    /* named constants */
202 203
    for (i=0; p->const_names && p->const_names[i]; i++) {
        if (strmatch(p->s, p->const_names[i])) {
204
            p->s+= strlen(p->const_names[i]);
205 206
            d->type = e_const;
            d->a.const_index = i;
207 208
            *e = d;
            return 0;
209 210
        }
    }
211

212
    p->s= strchr(p->s, '(');
213
    if (p->s==NULL) {
214
        av_log(p, AV_LOG_ERROR, "Undefined constant or missing '(' in '%s'\n", s0);
Michael Niedermayer's avatar
Michael Niedermayer committed
215
        p->s= next;
216
        av_free_expr(d);
217
        return AVERROR(EINVAL);
218 219
    }
    p->s++; // "("
220 221
    if (*next == '(') { // special case do-nothing
        av_freep(&d);
222 223
        if ((ret = parse_expr(&d, p)) < 0)
            return ret;
224
        if (p->s[0] != ')') {
225
            av_log(p, AV_LOG_ERROR, "Missing ')' in '%s'\n", s0);
226
            av_free_expr(d);
227
            return AVERROR(EINVAL);
228 229
        }
        p->s++; // ")"
230 231 232 233
        *e = d;
        return 0;
    }
    if ((ret = parse_expr(&(d->param[0]), p)) < 0) {
234
        av_free_expr(d);
235
        return ret;
236
    }
237
    if (p->s[0]== ',') {
238
        p->s++; // ","
239
        parse_expr(&d->param[1], p);
240
    }
241
    if (p->s[0] != ')') {
242
        av_log(p, AV_LOG_ERROR, "Missing ')' or too many args in '%s'\n", s0);
243
        av_free_expr(d);
244
        return AVERROR(EINVAL);
245 246
    }
    p->s++; // ")"
247

248
    d->type = e_func0;
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
         if (strmatch(next, "sinh"  )) d->a.func0 = sinh;
    else if (strmatch(next, "cosh"  )) d->a.func0 = cosh;
    else if (strmatch(next, "tanh"  )) d->a.func0 = tanh;
    else if (strmatch(next, "sin"   )) d->a.func0 = sin;
    else if (strmatch(next, "cos"   )) d->a.func0 = cos;
    else if (strmatch(next, "tan"   )) d->a.func0 = tan;
    else if (strmatch(next, "atan"  )) d->a.func0 = atan;
    else if (strmatch(next, "asin"  )) d->a.func0 = asin;
    else if (strmatch(next, "acos"  )) d->a.func0 = acos;
    else if (strmatch(next, "exp"   )) d->a.func0 = exp;
    else if (strmatch(next, "log"   )) d->a.func0 = log;
    else if (strmatch(next, "abs"   )) d->a.func0 = fabs;
    else if (strmatch(next, "squish")) d->type = e_squish;
    else if (strmatch(next, "gauss" )) d->type = e_gauss;
    else if (strmatch(next, "mod"   )) d->type = e_mod;
    else if (strmatch(next, "max"   )) d->type = e_max;
    else if (strmatch(next, "min"   )) d->type = e_min;
    else if (strmatch(next, "eq"    )) d->type = e_eq;
    else if (strmatch(next, "gte"   )) d->type = e_gte;
    else if (strmatch(next, "gt"    )) d->type = e_gt;
    else if (strmatch(next, "lte"   )) { AVExpr *tmp = d->param[1]; d->param[1] = d->param[0]; d->param[0] = tmp; d->type = e_gt; }
    else if (strmatch(next, "lt"    )) { AVExpr *tmp = d->param[1]; d->param[1] = d->param[0]; d->param[0] = tmp; d->type = e_gte; }
    else if (strmatch(next, "ld"    )) d->type = e_ld;
    else if (strmatch(next, "st"    )) d->type = e_st;
    else if (strmatch(next, "while" )) d->type = e_while;
274
    else {
275 276
        for (i=0; p->func1_names && p->func1_names[i]; i++) {
            if (strmatch(next, p->func1_names[i])) {
277
                d->a.func1 = p->funcs1[i];
278
                d->type = e_func1;
279 280
                *e = d;
                return 0;
281 282 283
            }
        }

284 285
        for (i=0; p->func2_names && p->func2_names[i]; i++) {
            if (strmatch(next, p->func2_names[i])) {
286
                d->a.func2 = p->funcs2[i];
287
                d->type = e_func2;
288 289
                *e = d;
                return 0;
290 291 292
            }
        }

293
        av_log(p, AV_LOG_ERROR, "Unknown function in '%s'\n", s0);
294
        av_free_expr(d);
295
        return AVERROR(EINVAL);
296
    }
297

298 299
    *e = d;
    return 0;
300
}
Michael Niedermayer's avatar
Michael Niedermayer committed
301

302 303 304
static AVExpr *new_eval_expr(int type, int value, AVExpr *p0, AVExpr *p1)
{
    AVExpr *e = av_mallocz(sizeof(AVExpr));
305 306
    if (!e)
        return NULL;
307 308 309 310 311 312 313
    e->type     =type   ;
    e->value    =value  ;
    e->param[0] =p0     ;
    e->param[1] =p1     ;
    return e;
}

314 315
static int parse_pow(AVExpr **e, Parser *p, int *sign)
{
316 317
    *sign= (*p->s == '+') - (*p->s == '-');
    p->s += *sign&1;
318
    return parse_primary(e, p);
319 320
}

321 322 323 324 325 326
static int parse_factor(AVExpr **e, Parser *p)
{
    int sign, sign2, ret;
    AVExpr *e0, *e1, *e2;
    if ((ret = parse_pow(&e0, p, &sign)) < 0)
        return ret;
327
    while(p->s[0]=='^'){
328
        e1 = e0;
329
        p->s++;
330
        if ((ret = parse_pow(&e2, p, &sign2)) < 0) {
331
            av_free_expr(e1);
332 333 334 335
            return ret;
        }
        e0 = new_eval_expr(e_pow, 1, e1, e2);
        if (!e0) {
336 337
            av_free_expr(e1);
            av_free_expr(e2);
338 339 340
            return AVERROR(ENOMEM);
        }
        if (e0->param[1]) e0->param[1]->value *= (sign2|1);
341
    }
342 343 344 345
    if (e0) e0->value *= (sign|1);

    *e = e0;
    return 0;
346 347
}

348 349 350 351 352 353
static int parse_term(AVExpr **e, Parser *p)
{
    int ret;
    AVExpr *e0, *e1, *e2;
    if ((ret = parse_factor(&e0, p)) < 0)
        return ret;
354
    while (p->s[0]=='*' || p->s[0]=='/') {
355
        int c= *p->s++;
356 357
        e1 = e0;
        if ((ret = parse_factor(&e2, p)) < 0) {
358
            av_free_expr(e1);
359 360 361 362
            return ret;
        }
        e0 = new_eval_expr(c == '*' ? e_mul : e_div, 1, e1, e2);
        if (!e0) {
363 364
            av_free_expr(e1);
            av_free_expr(e2);
365 366
            return AVERROR(ENOMEM);
        }
367
    }
368 369
    *e = e0;
    return 0;
370 371
}

372 373 374 375 376 377
static int parse_subexpr(AVExpr **e, Parser *p)
{
    int ret;
    AVExpr *e0, *e1, *e2;
    if ((ret = parse_term(&e0, p)) < 0)
        return ret;
378
    while (*p->s == '+' || *p->s == '-') {
379 380
        e1 = e0;
        if ((ret = parse_term(&e2, p)) < 0) {
381
            av_free_expr(e1);
382 383 384 385
            return ret;
        }
        e0 = new_eval_expr(e_add, 1, e1, e2);
        if (!e0) {
386 387
            av_free_expr(e1);
            av_free_expr(e2);
388 389
            return AVERROR(ENOMEM);
        }
390 391
    };

392 393
    *e = e0;
    return 0;
394 395
}

396 397 398 399
static int parse_expr(AVExpr **e, Parser *p)
{
    int ret;
    AVExpr *e0, *e1, *e2;
400
    if (p->stack_index <= 0) //protect against stack overflows
401
        return AVERROR(EINVAL);
Michael Niedermayer's avatar
Michael Niedermayer committed
402 403
    p->stack_index--;

404 405
    if ((ret = parse_subexpr(&e0, p)) < 0)
        return ret;
406
    while (*p->s == ';') {
407 408
        e1 = e0;
        if ((ret = parse_subexpr(&e2, p)) < 0) {
409
            av_free_expr(e1);
410 411
            return ret;
        }
412
        p->s++;
413 414
        e0 = new_eval_expr(e_last, 1, e1, e2);
        if (!e0) {
415 416
            av_free_expr(e1);
            av_free_expr(e2);
417 418
            return AVERROR(ENOMEM);
        }
419
    };
Michael Niedermayer's avatar
Michael Niedermayer committed
420 421

    p->stack_index++;
422 423
    *e = e0;
    return 0;
424 425
}

426 427
static int verify_expr(AVExpr *e)
{
428 429 430 431 432 433 434
    if (!e) return 0;
    switch (e->type) {
        case e_value:
        case e_const: return 1;
        case e_func0:
        case e_func1:
        case e_squish:
435
        case e_ld:
436 437 438 439 440
        case e_gauss: return verify_expr(e->param[0]);
        default: return verify_expr(e->param[0]) && verify_expr(e->param[1]);
    }
}

441
int av_parse_expr(AVExpr **expr, const char *s,
442 443 444
                  const char * const *const_names,
                  const char * const *func1_names, double (* const *funcs1)(void *, double),
                  const char * const *func2_names, double (* const *funcs2)(void *, double, double),
445
                  int log_offset, void *log_ctx)
446
{
447
    Parser p;
448
    AVExpr *e = NULL;
449 450
    char *w = av_malloc(strlen(s) + 1);
    char *wp = w;
451
    const char *s0 = s;
452
    int ret = 0;
453 454

    if (!w)
455
        return AVERROR(ENOMEM);
456 457 458 459

    while (*s)
        if (!isspace(*s++)) *wp++ = s[-1];
    *wp++ = 0;
460

461
    p.class      = &class;
Michael Niedermayer's avatar
Michael Niedermayer committed
462
    p.stack_index=100;
463
    p.s= w;
464 465 466 467 468
    p.const_names = const_names;
    p.funcs1      = funcs1;
    p.func1_names = func1_names;
    p.funcs2      = funcs2;
    p.func2_names = func2_names;
469 470
    p.log_offset = log_offset;
    p.log_ctx    = log_ctx;
471

472 473
    if ((ret = parse_expr(&e, &p)) < 0)
        goto end;
474 475 476 477 478
    if (*p.s) {
        av_log(&p, AV_LOG_ERROR, "Invalid chars '%s' at the end of expression '%s'\n", p.s, s0);
        ret = AVERROR(EINVAL);
        goto end;
    }
479
    if (!verify_expr(e)) {
480
        av_free_expr(e);
481 482
        ret = AVERROR(EINVAL);
        goto end;
483
    }
484
    *expr = e;
485 486
end:
    av_free(w);
487
    return ret;
488 489
}

490
double av_eval_expr(AVExpr *e, const double *const_values, void *opaque)
491
{
492 493
    Parser p;

494
    p.const_values = const_values;
495 496 497 498
    p.opaque     = opaque;
    return eval_expr(&p, e);
}

499
int av_parse_and_eval_expr(double *d, const char *s,
500 501 502
                           const char * const *const_names, const double *const_values,
                           const char * const *func1_names, double (* const *funcs1)(void *, double),
                           const char * const *func2_names, double (* const *funcs2)(void *, double, double),
503
                           void *opaque, int log_offset, void *log_ctx)
504
{
505
    AVExpr *e = NULL;
506
    int ret = av_parse_expr(&e, s, const_names, func1_names, funcs1, func2_names, funcs2, log_offset, log_ctx);
507 508 509 510 511

    if (ret < 0) {
        *d = NAN;
        return ret;
    }
512 513
    *d = av_eval_expr(e, const_values, opaque);
    av_free_expr(e);
514
    return isnan(*d) ? AVERROR(EINVAL) : 0;
515
}
516 517

#ifdef TEST
518
#undef printf
519
static double const_values[] = {
520 521 522 523
    M_PI,
    M_E,
    0
};
524

525
static const char *const_names[] = {
526 527 528 529
    "PI",
    "E",
    0
};
530

531 532
int main(void)
{
Michael Niedermayer's avatar
Michael Niedermayer committed
533
    int i;
534
    double d;
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
    const char **expr, *exprs[] = {
        "",
        "1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
        "80G/80Gi"
        "1k",
        "1Gi",
        "1gi",
        "1GiFoo",
        "1k+1k",
        "1Gi*3foo",
        "foo",
        "foo(",
        "foo()",
        "foo)",
        "sin",
        "sin(",
        "sin()",
        "sin)",
        "sin 10",
        "sin(1,2,3)",
        "sin(1 )",
        "1",
        "1foo",
        "bar + PI + E + 100f*2 + foo",
        "13k + 12f - foo(1, 2)",
        "1gi",
        "1Gi",
        NULL
    };

    for (expr = exprs; *expr; expr++) {
        printf("Evaluating '%s'\n", *expr);
        av_parse_and_eval_expr(&d, *expr,
                               const_names, const_values,
                               NULL, NULL, NULL, NULL, NULL, 0, NULL);
        printf("'%s' -> %f\n\n", *expr, d);
    }

573
    av_parse_and_eval_expr(&d, "1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
574 575
                           const_names, const_values,
                           NULL, NULL, NULL, NULL, NULL, 0, NULL);
576
    printf("%f == 12.7\n", d);
577
    av_parse_and_eval_expr(&d, "80G/80Gi",
578
                           const_names, const_values,
579
                           NULL, NULL, NULL, NULL, NULL, 0, NULL);
580
    printf("%f == 0.931322575\n", d);
581

582
    for (i=0; i<1050; i++) {
Michael Niedermayer's avatar
Michael Niedermayer committed
583
        START_TIMER
584
            av_parse_and_eval_expr(&d, "1+(5-2)^(3-1)+1/2+sin(PI)-max(-2.2,-3.1)",
585 586
                                   const_names, const_values,
                                   NULL, NULL, NULL, NULL, NULL, 0, NULL);
587
        STOP_TIMER("av_parse_and_eval_expr")
Michael Niedermayer's avatar
Michael Niedermayer committed
588
    }
589
    return 0;
590 591
}
#endif