[FFmpeg-devel] [RFC][PATCH] avstring: Add new function av_for_each_pair

Nicolas George nicolas.george at normalesup.org
Mon Feb 27 13:59:41 CET 2012


Le septidi 7 ventôse, an CCXX, Alexander Strasser a écrit :
>   This function takes a string and tokenizes it into pairs. The
> caller will be called back for each pair.

It this was Caml, I would clap with both hands (well, clapping with only one
hand is awkward anyway), but in C, this kind of API, while theoretically
capable of implementing anything, is often very impractical for most tasks.
And since it does not allow to avoid mallocs anyway, I would rather like to
propose this one: (see below).

Compared to yours, it returns all key-value pairs in a single array. A
significant difference is that if a syntax error occurs late in the string,
your version calls the callback for the early pairs while mine returns only
an error.

There are a few open questions:

- Do we accept unescaped '=' in the value string? Yours does not, mine does;
  and I believe the answer should be yes.

- Do we accept keys without '=' and value? It could return vale as NULL.

- Do we accept empty keys?

Regards,

-- 
  Nicolas George

#include <string.h>
#include <libavutil/mem.h>
#include <libavutil/avassert.h>

/**
 * Split a string into key-value pairs
 *
 * @param ret_kv       used to return the key-value pairs:
 *                     kv[i][0] and kv[i][1] are respectively the key and
 *                     value for the i-th pair
 * @param str          input string
 * @param str_end      end of the input string; does not need to be '\0'
 * @param comp_delim   ASCII character to separate key and value, usually '='
 * @param pair_delim   ASCII character to separate pairs, usually ',' or ':'
 * @param escape_char  ASCII character to escape comp_delim, pair_delim and
 *                     itself, usually '\\'
 * @param error_pos    used to return a pointer near the position of the
 *                     error, if any; can be NULL
 * @return  the number of pairs, or AVERROR for failure
 * @note  The caller is responsible for freeing the pointer returned in
 *        ret_kv; it will free all allocated memory.
 */

int av_parse_kv_pairs(char *(**ret_kv)[2], const char *str, const char *str_end,
                      char comp_delim, char pair_delim, char escape_char,
                      const char **error_pos)
{
    unsigned nb_kv = 1, kv_no = 0, buf_size = 1;
    const char *p;
    char *(*kv)[2], *buf;

    for (p = str; p < str_end; p++) {
        if (*p == escape_char) {
            if (p + 1 == str_end || (p[1] != comp_delim &&
                p[1] != pair_delim && p[1] != escape_char)) {
                if (error_pos)
                    *error_pos = p;
                return AVERROR(EINVAL);
            }
            p++;
        } else if (*p == pair_delim) {
            nb_kv++;
        }
        buf_size++;
    }
    kv = av_malloc((nb_kv + 1) * sizeof(*kv) + buf_size);
    if (!kv) {
        if (error_pos)
            *error_pos = str;
        return AVERROR(ENOMEM);
    }
    kv[0][0] = buf = (char *)&kv[nb_kv + 1];
    kv[0][1] = NULL;
    p = str;
    while (1) {
        if (p == str_end || *p == pair_delim) {
            *(buf++) = 0;
            if (!kv[kv_no][1]) {
                if (error_pos)
                    *error_pos = p;
                av_free(kv);
                return AVERROR(EINVAL);
            }
            kv_no++;
            kv[kv_no][0] = buf;
            kv[kv_no][1] = NULL;
            if (p == str_end)
                break;
        } else if (*p == comp_delim && !kv[kv_no][1]) {
            *(buf++) = 0;
            kv[kv_no][1] = buf;
        } else {
            if (*p == escape_char)
                p++;
            *(buf++) = *p;
        }
        p++;
    }
    av_assert0(kv_no == nb_kv);
    av_assert0(buf == (char *)&kv[nb_kv + 1] + buf_size);
    kv[kv_no][0] = kv[kv_no][1] = NULL;
    *ret_kv = kv;
    return nb_kv;
}

int main(void)
{
    unsigned i;
    char *(*kv)[2];

    printf("Testing av_for_each_pair()\n");
    {
        int ret, j;
        char err_str[128];
        const char *err_pos = NULL;
        const char *strings[] = {
            "",
            ",",
            "=",
            "=\\",
            "=\\\\",
            "=,=",
            "==",
            "=,,",
            "a=b",
            "a=\\b",
            "c=d,e=f",
            "c=d,e=f,",
            "\\==\\,",
            "\\\\=1\\, 2\\, 3",
            "1\\, 2\\, 3=\\\\",
            "\\,=\\=",
        };

        for (i=0; i < FF_ARRAY_ELEMS(strings); i++) {
            const char *p = strings[i];

            printf("|%s|", p); fflush(stdout);
            ret = av_parse_kv_pairs(&kv, p, p + strlen(p), '=', ',', '\\',
                                    &err_pos);
            if (ret < 0) {
                av_strerror(ret, err_str, sizeof(err_str));
                printf(" (ret: %s near \"%s\")\n", err_str, err_pos);
            } else {
                for (j = 0; j < ret; j++)
                    printf(" -> |%s| + |%s|", kv[j][0], kv[j][1]);
                printf(" (ret: Success)\n");
                av_free(kv);
            }
            fflush(stdout);
        }
    }

    return 0;
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20120227/06380ffc/attachment.asc>


More information about the ffmpeg-devel mailing list