forked from FFmpeg/FFmpeg
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Nicolas George
committed
Mar 21, 2012
1 parent
594a3d6
commit b75c67d
Showing
5 changed files
with
350 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 */ |