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

Alexander Strasser eclipse7 at gmx.net
Sat Feb 25 22:43:51 CET 2012


  This function takes a string and tokenizes it into pairs. The
caller will be called back for each pair.

Signed-off-by: Alexander Strasser <eclipse7 at gmx.net>
---
 libavutil/avstring.c |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libavutil/avstring.h |   35 ++++++++++++
 2 files changed, 178 insertions(+), 0 deletions(-)

diff --git a/libavutil/avstring.c b/libavutil/avstring.c
index 76f6bb2..c4ee2ed 100644
--- a/libavutil/avstring.c
+++ b/libavutil/avstring.c
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include "avstring.h"
 #include "mem.h"
+#include "error.h"
 
 int av_strstart(const char *str, const char *pfx, const char **ptr)
 {
@@ -210,10 +211,114 @@ int av_strncasecmp(const char *a, const char *b, size_t n)
     return c1 - c2;
 }
 
+int av_for_each_pair(each_pair_fn cb, void *cb_data, const char * str,
+                     char comp_delim, char pair_delim, char escape_char)
+{
+    size_t buf_size = 0, len = 0, buf_idx = 0, comp2_idx = 0;
+    char *buf = NULL;
+    int ret = 0, i = 0;
+
+    for (;;) {
+        const char *ptr;
+        char *tmp_ptr;
+        size_t off = 0;
+
+        while (str[off]
+               && str[off] != escape_char
+               && str[off] != comp_delim
+               && str[off] != pair_delim) {
+            ++off;
+        }
+        len += off + 1;
+
+        if (len >= buf_size) {
+            buf_size = len;
+
+            tmp_ptr = av_realloc(buf, buf_size);
+            if (!tmp_ptr) {
+                ret = AVERROR(ENOMEM);
+                break;
+            }
+            buf = tmp_ptr;
+        }
+
+        ptr = &str[off];
+
+        //av_log(NULL, 0, "\ni == %i, *ptr == 0x%x, off=%u, len == %u, str: %s\n", i, *ptr, (unsigned)off, (unsigned)len, str);
+
+        memcpy(&buf[buf_idx], str, off);
+        buf_idx += off;
+        if (*ptr == escape_char) {
+            if (ptr[1] == comp_delim ||
+                ptr[1] == pair_delim ||
+                ptr[1] == escape_char) {
+                buf[buf_idx++] = ptr[1];
+                str += off + 2; // advance over escape and escaped char
+
+                continue;
+            } else {
+                ret = AVERROR_INVALIDDATA;
+                break;
+            }
+        } else if (*ptr == comp_delim) {
+            if (i & 1) {
+                ret = AVERROR_INVALIDDATA;
+                break;
+            }
+
+            buf[buf_idx++] = 0;
+            comp2_idx = buf_idx;
+        } else if (*ptr == pair_delim) {
+            if (!(i & 1)) {
+                ret = AVERROR_INVALIDDATA;
+                break;
+            }
+
+            buf[buf_idx] = 0;
+        }
+
+        if (!ret && (i & 1)) {
+            if (!*ptr) // value part of the last pair has no delimiter
+                buf[buf_idx] = 0;
+            buf_idx = len = 0;
+
+            ret = cb(buf, &buf[comp2_idx], cb_data);
+            if (ret)
+                break;
+        }
+
+        ++i;
+
+        if (!*ptr)
+            break;
+
+        str = ptr + 1;
+    }
+
+    av_free(buf);
+
+    if (!ret && (i & 1)) // missing pair's second component
+        ret = AVERROR_INVALIDDATA;
+
+    return ret;
+}
+
+
 #ifdef TEST
 
 #undef printf
 
+static int print_pair(char *a, char *b, void *cb_data)
+{
+    printf(" -> |%s| + |%s|", a, b);
+
+    return 0;
+}
+static int return_averror(char *a, char *b, void *cb_data)
+{
+    return AVERROR_PATCHWELCOME;
+}
+
 int main(void)
 {
     int i;
@@ -257,6 +362,44 @@ int main(void)
         }
     }
 
+    printf("Testing av_for_each_pair()\n");
+    {
+        int ret;
+        char err_str[128];
+        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_for_each_pair(print_pair, NULL, p, '=', ',', '\\');
+            av_strerror(ret, err_str, sizeof(err_str));
+            printf(" (ret: %s)\n", err_str);
+        }
+
+        printf("|%s|", strings[4]); fflush(stdout);
+        ret = av_for_each_pair(return_averror, NULL, "k=v", '=', ',', '\\');
+        av_strerror(ret, err_str, sizeof(err_str));
+        printf(" (ret: %s)\n", err_str);
+    }
+
     return 0;
 }
 
diff --git a/libavutil/avstring.h b/libavutil/avstring.h
index f73d6e7..acb5c7a 100644
--- a/libavutil/avstring.h
+++ b/libavutil/avstring.h
@@ -202,6 +202,41 @@ int av_strcasecmp(const char *a, const char *b);
  */
 int av_strncasecmp(const char *a, const char *b, size_t n);
 
+
+/**
+ * Will be called with a pair of strings a and b.
+ *
+ *   Where a is the first component of the pair and b is the second.
+ *
+ *   Each string can be modified at will, except you may never
+ * overwrite its zero terminator which denotes the end of the
+ * string. The strings are only guaranteed to exist for the time
+ * of the function call. Do never try to access them after the
+ * function returned.
+ *
+ *   Any return code other than 0 (OK) will stop the calling function
+ * and that return code will be passed through.
+ *
+ */
+typedef int (*each_pair_fn)(char *a, char *b, void *cb_data);
+
+/**
+ * Split a string into pairs and loop through them.
+ *
+ *   Calls the passed in function pointer cb for each tokenized pair.
+ * See the documentation of the callback function type for explanation
+ * of its function signature.
+ *
+ * @param cb callback function that gets executed for each pair in str
+ * @param cb_data pointer that will get passed to callback on each invocation
+ * @param str input string
+ * @param comp_delim ASCII character to separate the pairs components
+ * @param pair_delim ASCII character to delimit the pairs themselves
+ * @param escape_char ASCII character to escape comp_delim, pair_delim and itself
+ * @return 0 for success and <0 (AVERROR value) for failure
+ */
+int av_for_each_pair(each_pair_fn cb, void *cb_data, const char * str, char comp_delim, char delim, char escape_char);
+
 /**
  * @}
  */
-- 
1.7.5.4


More information about the ffmpeg-devel mailing list