[FFmpeg-devel] [PATCH] lavu: add stringinterp interface

wm4 nfxjfg at googlemail.com
Sun Dec 8 12:05:00 CET 2013


On Sat,  7 Dec 2013 10:43:17 +0100
Stefano Sabatini <stefasab at gmail.com> wrote:

> From: Clément Bœsch <u at pkh.me>
> 
> With very minor changes by Stefano Sabatini.
> 
> Based on thread:
> From: Clément Bœsch <ubitux at gmail.com>
> Subject: [FFmpeg-devel] [PATCH] WIP/lavu: add string interpolation helper.
> Date: 2012-02-01 11:01:02 GMT
> 
> Signed-off-by: Stefano Sabatini <stefasab at gmail.com>
> 
> TODO: bump major, add APIchanges entry, mark as experimental?
> ---
>  libavutil/Makefile       |   2 +
>  libavutil/stringinterp.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++
>  libavutil/stringinterp.h |  66 +++++++++++++++
>  3 files changed, 275 insertions(+)
>  create mode 100644 libavutil/stringinterp.c
>  create mode 100644 libavutil/stringinterp.h
> 
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index 5e0c4ae..c30caae 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -110,6 +110,7 @@ OBJS = adler32.o                                                        \
>         samplefmt.o                                                      \
>         sha.o                                                            \
>         sha512.o                                                         \
> +       stringinterp.o                                                   \
>         time.o                                                           \
>         timecode.o                                                       \
>         tree.o                                                           \
> @@ -158,6 +159,7 @@ TESTPROGS = adler32                                                     \
>              random_seed                                                 \
>              rational                                                    \
>              ripemd                                                      \
> +            stringinterp                                                \
>              sha                                                         \
>              sha512                                                      \
>              tree                                                        \
> diff --git a/libavutil/stringinterp.c b/libavutil/stringinterp.c
> new file mode 100644
> index 0000000..d1d4fc1
> --- /dev/null
> +++ b/libavutil/stringinterp.c
> @@ -0,0 +1,207 @@
> +/*
> + * Copyright (c) 2012 Clément Bœsch
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * 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
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * string interpolation
> + */
> +
> +#include "avutil.h"
> +#include "avstring.h"
> +#include "stringinterp.h"
> +
> +struct node {
> +    const char *key;
> +    const char *value;
> +    struct node *next;
> +};
> +
> +typedef struct AVStringInterp {
> +    struct node *hashtable[512];
> +    char *buf;
> +    size_t buf_size;
> +} AVStringInterp;
> +
> +AVStringInterp *av_stringinterp_alloc(void)
> +{
> +    AVStringInterp *ctx = av_mallocz(sizeof(*ctx));
> +
> +    if (!ctx)
> +        return NULL;
> +    ctx->buf = av_malloc(128);
> +    if (!ctx->buf) {
> +        av_free(ctx);
> +        return NULL;
> +    }
> +    ctx->buf_size = 128;
> +    return ctx;
> +}
> +
> +void av_stringinterp_free(AVStringInterp **ctx)
> +{
> +    int i;
> +    AVStringInterp *s = *ctx;
> +    for (i = 0; i < FF_ARRAY_ELEMS(s->hashtable); i++) {
> +        struct node *tmp, *node = s->hashtable[i];
> +        while (node) {
> +            tmp = node;
> +            node = node->next;
> +            av_freep(&tmp);
> +        }
> +    }
> +    av_freep(&s->buf);
> +    av_freep(ctx);
> +}
> +
> +static uint32_t get_hash(const char *key, size_t len, int tbl_len)
> +{
> +    size_t i;
> +    uint32_t hash = 0;
> +    for (i = 0; i < len; i++)
> +        hash = (hash<<5) ^ (hash>>27) ^ key[i];
> +    return hash % tbl_len;
> +}
> +
> +static struct node *create_node(const char *key, const char *value)
> +{
> +    struct node *node = av_mallocz(sizeof(*node));
> +    if (!node)
> +        return NULL;
> +    node->key   = key;
> +    node->value = value;
> +    return node;
> +}
> +
> +int av_stringinterp_map(AVStringInterp *ctx, const char *key, const char *value)
> +{
> +    uint32_t id = get_hash(key, strlen(key), FF_ARRAY_ELEMS(ctx->hashtable));
> +    struct node *node = ctx->hashtable[id];
> +    struct node *insert_after = NULL;
> +
> +    while (node) {
> +        if (!strcmp(key, node->key)) {
> +            node->value = value;
> +            return 0;
> +        }
> +        insert_after = node;
> +        node = node->next;
> +    }
> +    node = create_node(key, value);
> +    if (!node)
> +        return AVERROR(ENOMEM);
> +    if (insert_after)
> +        insert_after->next = node;
> +    else
> +        ctx->hashtable[id] = node;
> +    return 0;
> +}
> +
> +static const char *get_value(AVStringInterp *ctx, const char *key)
> +{
> +    int id;
> +    size_t keylen;
> +    const char *p = strchr(key, ')');
> +    struct node *node;
> +
> +    if (!p)
> +        return NULL;
> +    keylen = p - key;
> +    id = get_hash(key, keylen, FF_ARRAY_ELEMS(ctx->hashtable));
> +    for (node = ctx->hashtable[id]; node; node = node->next)
> +        if (!strncmp(key, node->key, keylen))
> +            return node->value;
> +    return NULL;
> +}
> +
> +#define APPEND_STRING(str, slen) do {               \
> +    size_t new_size = pos + slen + 1;               \
> +    if (new_size > ctx->buf_size) {                 \
> +        char *ptr = av_realloc(ctx->buf, new_size); \
> +        if (!ptr)                                   \
> +            return NULL;                            \
> +        ctx->buf      = ptr;                        \
> +        ctx->buf_size = new_size;                   \
> +    }                                               \
> +    av_strlcpy(ctx->buf + pos, str, slen + 1);      \
> +    pos += slen;                                    \
> +} while (0)

A really evil macro, which doesn't even (quote) its macro parameters.
Why is this a macro, just so you can return from it?

> +char *av_stringinterp_interpolate(AVStringInterp *ctx, const char *s)
> +{
> +    const char *p;
> +    size_t pos = 0;
> +
> +    while (*s) {
> +        if (s[0] == '%' && s[1] == '(') {
> +            const char *value = get_value(ctx, s + 2);
> +            if (value) {
> +                APPEND_STRING(value, strlen(value));
> +                s = strchr(s, ')') + 1;
> +                continue;
> +            }
> +        }
> +        p = strchr(s + 1, '%');
> +        if (!p) {
> +            APPEND_STRING(s, strlen(s));
> +            break;
> +        }
> +        APPEND_STRING(s, p - s);
> +        s = p;
> +    }
> +    return ctx->buf;
> +}
> +
> +#ifdef TEST
> +
> +static void dump_table(AVStringInterp *ctx)
> +{
> +    int i;
> +    printf("Hashtable dump:\n");
> +    for (i = 0; i < FF_ARRAY_ELEMS(ctx->hashtable); i++) {
> +        struct node *node = ctx->hashtable[i];
> +        if (!node)
> +            continue;
> +        printf("  [%02d/%02ld]:", i, FF_ARRAY_ELEMS(ctx->hashtable));
> +        while (node) {
> +            printf(" \"%s\"=>\"%s\"", node->key, node->value);
> +            node = node->next;
> +        }
> +        printf("\n");
> +    }
> +}
> +
> +int main(int ac, char **av)
> +{
> +    AVStringInterp *si = av_stringinterp_alloc();
> +
> +    av_stringinterp_map(si, "ts1",      "0.4123");
> +    av_stringinterp_map(si, "ts2",      "54.153");
> +    av_stringinterp_map(si, "filename", "FooBar");
> +    av_stringinterp_map(si, "ext",      ".mpg");
> +    av_stringinterp_map(si, "ext",      ".mp4");
> +
> +    dump_table(si);
> +
> +    printf("%s\n",
> +           av_stringinterp_interpolate(si, "%(filename)_%(ts1)-%(ts2)_%(nokey)%(ext)"));
> +    av_stringinterp_free(&si);
> +    return 0;
> +}
> +#endif
> diff --git a/libavutil/stringinterp.h b/libavutil/stringinterp.h
> new file mode 100644
> index 0000000..a2331ad
> --- /dev/null
> +++ b/libavutil/stringinterp.h
> @@ -0,0 +1,66 @@
> +/*
> + * Copyright (c) 2012 Clément Bœsch
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * 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
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * string interpolation header
> + */
> +
> +#ifndef AVUTIL_STRINGINTERP_H
> +#define AVUTIL_STRINGINTERP_H
> +
> +typedef struct AVStringInterp AVStringInterp;
> +
> +/**
> + * Allocate a new string interpolation context.
> + */
> +AVStringInterp *av_stringinterp_alloc(void);
> +
> +/**
> + * Free a AVStringInterp context previously created with
> + * av_stringinterp_alloc().
> + */
> +void av_stringinterp_free(AVStringInterp **ctx);
> +
> +/**
> + * Map a key string with a value string.
> + *
> + * If the key already exists, the value is replaced.
> + * @return 0 in case of success, a negative AVERROR code otherwise
> + * @note key and value are not duplicated, you must keep a reference
> + * to them.
> + */
> +int av_stringinterp_map(AVStringInterp *ctx, const char *key, const char *value);
> +
> +/**
> + * Do the string interpolation.
> + *
> + * The value to interpolate must have the form "%(key)", where key is
> + * the name to be changed with the value previously stored in the
> + * AVStringInterp structure. In case the key is not recognized, it is
> + * ignored.
> + *
> + * @return a pointer to the printable string, or NULL in case of failure
> + * @note The returned buffer is kept in the AVStringInterp context, you must
> + *       not free it.
> + */
> +char *av_stringinterp_interpolate(AVStringInterp *ctx, const char *s);
> +
> +#endif/* AVUTIL_STRINGINTERP_H */

Is there a reason why this is so complex, including hashtables etc.? It
could be a single function that takes a string list (even a statically
allocated one).


More information about the ffmpeg-devel mailing list