lavu: add av_bprintf and related.
authorNicolas George <nicolas.george@normalesup.org>
Wed, 1 Feb 2012 19:37:50 +0000 (20:37 +0100)
committerNicolas George <nicolas.george@normalesup.org>
Wed, 21 Mar 2012 12:39:28 +0000 (13:39 +0100)
doc/APIchanges
libavutil/Makefile
libavutil/avutil.h
libavutil/bprint.c [new file with mode: 0644]
libavutil/bprint.h [new file with mode: 0644]

index 26a3882..1cdcd18 100644 (file)
@@ -13,6 +13,9 @@ libavutil:   2011-04-18
 
 API changes, most recent first:
 
+2012-03-21 - xxxxxxx - lavu 51.43.100
+  Add bprint.h for bprint API.
+
 2012-02-21 - xxxxxxx - lavc 54.4.100
   Add av_get_pcm_codec() function.
 
index 77d23d4..b02a1e3 100644 (file)
@@ -10,6 +10,7 @@ HEADERS = adler32.h                                                     \
           avstring.h                                                    \
           avutil.h                                                      \
           base64.h                                                      \
+          bprint.h                                                      \
           bswap.h                                                       \
           common.h                                                      \
           cpu.h                                                         \
@@ -47,6 +48,7 @@ OBJS = adler32.o                                                        \
        audioconvert.o                                                   \
        avstring.o                                                       \
        base64.o                                                         \
+       bprint.o                                                         \
        cpu.o                                                            \
        crc.o                                                            \
        des.o                                                            \
@@ -81,8 +83,8 @@ OBJS-$(ARCH_PPC) += ppc/cpu.o
 OBJS-$(ARCH_X86) += x86/cpu.o
 
 
-TESTPROGS = adler32 aes avstring base64 cpu crc des eval file fifo lfg lls \
-            md5 opt pca parseutils random_seed rational sha tree
+TESTPROGS = adler32 aes avstring base64 bprint cpu crc des eval file fifo \
+            lfg lls md5 opt pca parseutils random_seed rational sha tree
 TESTPROGS-$(HAVE_LZO1X_999_COMPRESS) += lzo
 
 TOOLS = ffeval
index 2ec2f2e..db3bc5c 100644 (file)
  */
 
 #define LIBAVUTIL_VERSION_MAJOR 51
-#define LIBAVUTIL_VERSION_MINOR 42
+#define LIBAVUTIL_VERSION_MINOR 43
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
diff --git a/libavutil/bprint.c b/libavutil/bprint.c
new file mode 100644 (file)
index 0000000..08383b2
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2012 Nicolas George
+ *
+ * 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
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "bprint.h"
+#include "common.h"
+#include "error.h"
+#include "mem.h"
+
+#define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size))
+#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
+
+static int av_bprint_alloc(AVBPrint *buf, unsigned room)
+{
+    char *old_str, *new_str;
+    unsigned min_size, new_size;
+
+    if (buf->size == buf->size_max)
+        return AVERROR(EIO);
+    if (!av_bprint_is_complete(buf))
+        return AVERROR_INVALIDDATA; /* it is already truncated anyway */
+    min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);
+    new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;
+    if (new_size < min_size)
+        new_size = FFMIN(buf->size_max, min_size);
+    old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;
+    new_str = av_realloc(old_str, new_size);
+    if (!new_str)
+        return AVERROR(ENOMEM);
+    if (!old_str)
+        memcpy(new_str, buf->str, buf->len + 1);
+    buf->str  = new_str;
+    buf->size = new_size;
+    return 0;
+}
+
+static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)
+{
+    /* arbitrary margin to avoid small overflows */
+    extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);
+    buf->len += extra_len;
+    if (buf->size)
+        buf->str[FFMIN(buf->len, buf->size - 1)] = 0;
+}
+
+void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
+{
+    unsigned size_auto = (char *)buf + sizeof(*buf) -
+                         buf->reserved_internal_buffer;
+
+    if (size_max == 1)
+        size_max = size_auto;
+    buf->str      = buf->reserved_internal_buffer;
+    buf->len      = 0;
+    buf->size     = FFMIN(size_auto, size_max);
+    buf->size_max = size_max;
+    *buf->str = 0;
+    if (size_init > buf->size)
+        av_bprint_alloc(buf, size_init - 1);
+}
+
+void av_bprintf(AVBPrint *buf, const char *fmt, ...)
+{
+    unsigned room;
+    char *dst;
+    va_list vl;
+    int extra_len;
+
+    while (1) {
+        room = av_bprint_room(buf);
+        dst = room ? buf->str + buf->len : NULL;
+        va_start(vl, fmt);
+        extra_len = vsnprintf(dst, room, fmt, vl);
+        va_end(vl);
+        if (extra_len <= 0)
+            return;
+        if (extra_len < room)
+            break;
+        if (av_bprint_alloc(buf, extra_len))
+            break;
+    }
+    av_bprint_grow(buf, extra_len);
+}
+
+void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
+{
+    unsigned room, real_n;
+
+    while (1) {
+        room = av_bprint_room(buf);
+        if (n < room)
+            break;
+        if (av_bprint_alloc(buf, n))
+            break;
+    }
+    if (room) {
+        real_n = FFMIN(n, room - 1);
+        memset(buf->str + buf->len, c, real_n);
+    }
+    av_bprint_grow(buf, n);
+}
+
+int av_bprint_finalize(AVBPrint *buf, char **ret_str)
+{
+    unsigned real_size = FFMIN(buf->len + 1, buf->size);
+    char *str;
+    int ret = 0;
+
+    if (ret_str) {
+        if (av_bprint_is_allocated(buf)) {
+            str = av_realloc(buf->str, real_size);
+            if (!str)
+                str = buf->str;
+            buf->str = NULL;
+        } else {
+            str = av_malloc(real_size);
+            if (str)
+                memcpy(str, buf->str, real_size);
+            else
+                ret = AVERROR(ENOMEM);
+        }
+        *ret_str = str;
+    } else {
+        if (av_bprint_is_allocated(buf))
+            av_freep(&buf->str);
+    }
+    buf->size = real_size;
+    return ret;
+}
+
+#ifdef TEST
+
+#undef printf
+
+static void bprint_pascal(AVBPrint *b, unsigned size)
+{
+    unsigned p[size + 1], i, j;
+
+    p[0] = 1;
+    av_bprintf(b, "%8d\n", 1);
+    for (i = 1; i <= size; i++) {
+        p[i] = 1;
+        for (j = i - 1; j > 0; j--)
+            p[j] = p[j] + p[j - 1];
+        for (j = 0; j <= i; j++)
+            av_bprintf(b, "%8d", p[j]);
+        av_bprintf(b, "\n");
+    }
+}
+
+int main(void)
+{
+    AVBPrint b;
+
+    av_bprint_init(&b, 0, -1);
+    bprint_pascal(&b, 5);
+    printf("Short text in unlimited buffer: %zu/%u\n", strlen(b.str), b.len);
+    printf("%s\n", b.str);
+    av_bprint_finalize(&b, NULL);
+
+    av_bprint_init(&b, 0, -1);
+    bprint_pascal(&b, 25);
+    printf("Long text in unlimited buffer: %zu/%u\n", strlen(b.str), b.len);
+    av_bprint_finalize(&b, NULL);
+
+    av_bprint_init(&b, 0, 2048);
+    bprint_pascal(&b, 25);
+    printf("Long text in limited buffer: %zu/%u\n", strlen(b.str), b.len);
+    av_bprint_finalize(&b, NULL);
+
+    av_bprint_init(&b, 0, 1);
+    bprint_pascal(&b, 5);
+    printf("Short text in automatic buffer: %zu/%u\n", strlen(b.str), b.len);
+
+    av_bprint_init(&b, 0, 1);
+    bprint_pascal(&b, 25);
+    printf("Long text in automatic buffer: %zu/%u\n", strlen(b.str), b.len);
+    /* Note that the size of the automatic buffer is arch-dependant. */
+
+    av_bprint_init(&b, 0, 0);
+    bprint_pascal(&b, 25);
+    printf("Long text count only buffer: %zu/%u\n", strlen(b.str), b.len);
+
+    return 0;
+}
+
+#endif
diff --git a/libavutil/bprint.h b/libavutil/bprint.h
new file mode 100644 (file)
index 0000000..c5d750d
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2012 Nicolas George
+ *
+ * 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
+ */
+
+#ifndef AVUTIL_BPRINT_H
+#define AVUTIL_BPRINT_H
+
+#include "attributes.h"
+
+/**
+ * Define a structure with extra padding to a fixed size
+ * This helps ensuring binary compatibility with future versions.
+ */
+#define FF_PAD_STRUCTURE(size, ...) \
+    __VA_ARGS__ \
+    char reserved_padding[size - sizeof(struct { __VA_ARGS__ })];
+
+/**
+ * Buffer to print data progressively
+ *
+ * The string buffer grows as necessary and is always 0-terminated.
+ * The content of the string is never accessed, and thus is
+ * encoding-agnostic and can even hold binary data.
+ *
+ * Small buffers are kept in the structure itself, and thus require no
+ * memory allocation at all (unless the contents of the buffer is needed
+ * after the structure goes out of scope). This is almost as lightweight as
+ * declaring a local "char buf[512]".
+ *
+ * The length of the string can go beyond the allocated size: the buffer is
+ * then truncated, but the functions still keep account of the actual total
+ * length.
+ *
+ * In other words, buf->len can be greater than buf->size and records the
+ * total length of what would have been to the buffer if there had been
+ * enough memory.
+ *
+ * Append operations do not need to be tested for failure: if a memory
+ * allocation fails, data stop being appended to the buffer, but the length
+ * is still updated. This situation can be tested with
+ * av_bprint_is_complete().
+ *
+ * The size_max field determines several possible behaviours:
+ *
+ * size_max = -1 (= UINT_MAX) or any large value will let the buffer be
+ * reallocated as necessary, with an amortized linear cost.
+ *
+ * size_max = 0 prevents writing anything to the buffer: only the total
+ * length is computed. The write operations can then possibly be repeated in
+ * a buffer with exactly the necessary size
+ * (using size_init = size_max = len + 1).
+ *
+ * size_max = 1 is automatically replaced by the exact size available in the
+ * structure itself, thus ensuring no dynamic memory allocation. The
+ * internal buffer is large enough to hold a reasonable paragraph of text,
+ * such as the current paragraph.
+ */
+typedef struct AVBPrint {
+    FF_PAD_STRUCTURE(1024,
+    char *str;         /** string so far */
+    unsigned len;      /** length so far */
+    unsigned size;     /** allocated memory */
+    unsigned size_max; /** maximum allocated memory */
+    char reserved_internal_buffer[1];
+    )
+} AVBPrint;
+
+/**
+ * Init a print buffer.
+ *
+ * @param buf        buffer to init
+ * @param size_init  initial size (including the final 0)
+ * @param size_max   maximum size;
+ *                   0 means do not write anything, just count the length;
+ *                   1 is replaced by the maximum value for automatic storage
+ */
+void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max);
+
+/**
+ * Convenience macros for special values for size_max.
+ */
+#define AV_BPRINT_SIZE_UNLIMITED  ((unsigned)-1)
+#define AV_BPRINT_SIZE_AUTOMATIC  1
+#define AV_BPRINT_SIZE_COUNT_ONLY 0
+
+/**
+ * Append a formated string to a print buffer.
+ */
+void av_bprintf(AVBPrint *buf, const char *fmt, ...) av_printf_format(2, 3);
+
+/**
+ * Append char c n times to a print buffer.
+ */
+void av_bprint_chars(AVBPrint *buf, char c, unsigned n);
+
+/**
+ * Test if the print buffer is complete (not truncated).
+ *
+ * It may have been truncated due to a memory allocation failure
+ * or the size_max limit (compare size and size_max if necessary).
+ */
+static inline int av_bprint_is_complete(AVBPrint *buf)
+{
+    return buf->len < buf->size;
+}
+
+/**
+ * Finalize a print buffer.
+ *
+ * The print buffer can no longer be used afterwards,
+ * but the len and size fields are still valid.
+ *
+ * @arg[out] ret_str  if not NULL, used to return a permanent copy of the
+ *                    buffer contents, or NULL if memory allocation fails;
+ *                    if NULL, the buffer is discarded and freed
+ * @return  0 for success or error code (probably AVERROR(ENOMEM))
+ */
+int av_bprint_finalize(AVBPrint *buf, char **ret_str);
+
+#endif /* AVUTIL_BPRINT_H */