在FFmpeg的基础库中,有一个可以支持Buffer空间动态扩容的功能支持,在C99之前的C语言支持动态扩容相对比较麻烦,这个AVBPrint可以提取出来单独使用,比较方便,下面详细介绍一下其使用方式及实现。
AVBPrint 结构体的定义
AVBPrint的结构体定义主要用来做Buffer的存储于信息记录用,下面看一下代码的注释及结构体内容
/**
* 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.
*/
FF_PAD_STRUCTURE(AVBPrint, 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的字符串长度是可以动态增加的。代码实现比较奇怪的部分是结构体声明,并不是常见的struct name {};格式,而是通过FF_PAD_STRUCTURE来声明的,这个声明也是一个宏,打开这个宏看一下定义实现:
/**
* Define a structure with extra padding to a fixed size
* This helps ensuring binary compatibility with future versions.
*/
#define FF_PAD_STRUCTURE(name, size, ...) \
struct ff_pad_helper_##name { __VA_ARGS__ }; \
typedef struct name { \
__VA_ARGS__ \
char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \
} name;
其实只是声明的时候加了在编译时动态生成的内容,在成员变量列表之后追加了一个成员变量char reserved_padding;编译时这一段宏的内容为:
struct ff_pad_helper_AVBPrint {
char *str;
unsigned len;
unsigned size;
unsigned size_max;
char reserved_internal_buffer[1];;
};
typedef struct ABPrint {
char *str;
unsigned len;
unsigned size;
unsigned size_max;
char reserved_internal_buffer[1];
char reserved_padding[1024 - sizeof(struct ff_pad_helper_AVBPrint )];
} AVBPrint;
关键的结构体介绍完毕。声明完毕之后,再介绍一下三个用在AVBPrint初始化时定义AVBPrint使用的初始方法的宏:
/**
* Convenience macros for special values for av_bprint_init() size_max
* parameter.
*/
#define AV_BPRINT_SIZE_UNLIMITED ((unsigned)-1) // is converted to UINT_MAX, the largest limit possible.
#define AV_BPRINT_SIZE_AUTOMATIC 1 // is replaced by the maximum value for automatic storage; any large value means that the internal buffer will be reallocated as needed up to that limit;
#define AV_BPRINT_SIZE_COUNT_ONLY 0 //means do not write anything, just count the length;
常用的一般可以为AV_BPRINT_SIZE_UNLIMITED即可。
还有两个接口在使用AVBPrint时会经常用到:
#define av_bprint_room(buf) ((buf)->size – FFMIN((buf)->len, (buf)->size))
#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
前置部分介绍完毕。接下来了解一下几个比较关键的接口。
av_bprint_init 接口
在使用bprint之前需要调用av_bprint_init进行bprint初始化,为AVBPrint申请一个空间,例如。
使用示例:
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
接口实现:
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);
}
av_bprint_init_for_buffer 接口
在初始化AVBPrint时即设置好buffer内容,
使用示例:
AVBPrint b;
char buf[256];
av_bprint_init_for_buffer(&b, buf, sizeof(buf));
接口实现:
void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
{
buf->str = buffer;
buf->len = 0;
buf->size = size;
buf->size_max = size;
*buf->str = 0;
}
av_bprint_alloc 接口
申请AVBPrint内存空间,内部是使用realloc来实现的。
使用示例:
在介绍av_bprint_init时可以查到;
接口实现:
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;
}
av_bprint_grow 接口
动态扩容AVBPrint的buffer的大小。
使用示例:
在介绍av_bprint_append_data接口实现时会看到;
接口实现:
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;
}
通过代码可以看到,这里并未申请内存,而是对buffer的长度进行了扩容。这个扩容接口尝尝是与av_bprint_alloc配合使用。
av_bprintf 与 av_vbprintf 接口
类似sprintf于vsprintf的方式对AVBPrint进行操作。
使用示例:
AVBPrint buf;
av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
av_bprintf(&buf, “%d%c%d”, 1, ‘/‘, 15);
接口实现:
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_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg)
{
unsigned room;
char *dst;
int extra_len;
va_list vl;
while (1) {
room = av_bprint_room(buf);
dst = room ? buf->str + buf->len : NULL;
va_copy(vl, vl_arg);
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);
}
buffer空间不足时通过av_bprint_alloc动态扩容空间,然后通过av_bprint_grow扩充buffer长度后的size。
av_bprint_append_data 接口
在AVBPrint的缓存内容中增加内容。
使用示例:
av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
av_bprint_append_data(&bprint, &key_val_sep, 1);
av_bprint_finalize(&bprint, buffer);
接口实现:
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
{
unsigned room, real_n;
while (1) {
room = av_bprint_room(buf);
if (size < room)
break;
if (av_bprint_alloc(buf, size))
break;
}
if (room) {
real_n = FFMIN(size, room - 1);
memcpy(buf->str + buf->len, data, real_n);
}
av_bprint_grow(buf, size);
}
av_bprint_finalize 接口
AVBPrint接口使用完成。这个接口比较特别,在使用这个接口之前,最好判断一下操作AVBPrint期间扩容buffer时是否生效,可以使用接口av_bprint_is_complete来进行判断,如果有失败,那么可以通过传递NULL给av_bprint_finalize来释放AVBPrint空间。
使用示例:
if (!av_bprint_is_complete(&header)) {
av_bprint_finalize(&header, NULL);
} else {
av_bprint_finalize(&header, &header_str);
}
接口实现:
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;
}
AVBPrint完整使用示例
关于AVBPrint接口部分介绍到这里已经完毕,下面来看一个完整的使用AVBPrint的常见应用实例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "buffer_print.h"
static int toupper(int c)
{
if (c >= 'a' && c <= 'z') {
c ^= 0x20;
}
return c;
}
int stristart(const char *str, const char *pfx, const char **ptr)
{
while (*pfx && toupper((unsigned)*pfx) == toupper((unsigned)*str)) {
pfx++;
str++;
}
if (!*pfx && ptr) {
*ptr = str;
}
return !*pfx;
}
char *stristr(const char *s1, const char *s2)
{
if (!*s2) {
return (char*)(intptr_t)s1;
}
do {
if (stristart(s1, s2, NULL)) {
return (char*)(intptr_t)s1;
}
} while (*s1++);
return NULL;
}
char *string_replace(const char *str, const char *from, const char *to)
{
char *ret = NULL;
const char *pstr2, *pstr = str;
size_t tolen = strlen(to), fromlen = strlen(from);
BPrint pbuf;
bprint_init(&pbuf, 1, BPRINT_SIZE_UNLIMITED);
while ((pstr2 = stristr(pstr, from))) {
bprint_append_data(&pbuf, pstr, pstr2 - pstr);
pstr = pstr2 + fromlen;
bprint_append_data(&pbuf, to, tolen);
}
bprint_append_data(&pbuf, pstr, strlen(pstr));
if (!bprint_is_complete(&pbuf)) {
bprint_finalize(&pbuf, NULL);
} else {
bprint_finalize(&pbuf, &ret);
}
return ret;
}
int main(int argc, char *argv[])
{
const char *haystack = "Education consists mainly in what we have unlearned.";
const char * const needle[] = {"learned.", "unlearned.", "Unlearned"};
char *ptr;
#define TEST_STRING_REPLACE(haystack, needle, expected) \
ptr = string_replace(haystack, needle, "instead"); \
if (ptr == NULL) { \
printf("error, received null pointer!\n"); \
} else { \
if (strcmp(ptr, expected) != 0) \
printf( "expected: %s, received: %s\n", expected, ptr); \
free(ptr); \
}
printf("Test start\n");
TEST_STRING_REPLACE(haystack, needle [0], "Education consists mainly in what we have uninstead");
TEST_STRING_REPLACE(haystack, needle [1], “Education consists mainly in what we have instead”);
TEST_STRING_REPLACE(haystack, needle [2], “Education consists mainly in what we have instead.”);
TEST_STRING_REPLACE(haystack, needle [1], “Education consists mainly in what we have instead”);
printf(“Test end\n”);
return 0;
}
本例子是使用AVBPrint实现一个字符串替换的工作。到这里,使用示例介绍完毕。
作者:悟空
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。