From 5d36d431ffe4c8ba0f698d0c288ebc16b83f0bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Tue, 14 Jun 2022 13:35:18 +0200 Subject: [PATCH 06/13] lavu/mem: Add ff_fast_recalloc() --- libavutil/mem.c | 24 +++++++++++++++++++++ libavutil/mem.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/libavutil/mem.c b/libavutil/mem.c index a0c9a42849..7781b715a0 100644 --- a/libavutil/mem.c +++ b/libavutil/mem.c @@ -530,6 +530,30 @@ void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size) return ptr; } +int ff_fast_recalloc(void *ptr, unsigned int *size, size_t nelem, size_t elsize) +{ + void *val; + void *new_ptr; + unsigned int new_size = *size; + size_t product; + int ret; + memcpy(&val, ptr, sizeof(val)); + + if ((ret = av_size_mult(nelem, elsize, &product)) < 0) + return ret; + + if (!(new_ptr = av_fast_realloc(val, &new_size, product))) + return AVERROR(ENOMEM); + + if (new_size > *size) { + memset((uint8_t*)new_ptr + *size, 0, new_size - *size); + *size = new_size; + memcpy(ptr, &new_ptr, sizeof(new_ptr)); + } + + return 0; +} + static inline void fast_malloc(void *ptr, unsigned int *size, size_t min_size, int zero_realloc) { size_t max_size; diff --git a/libavutil/mem.h b/libavutil/mem.h index d91174196c..74abf3dce2 100644 --- a/libavutil/mem.h +++ b/libavutil/mem.h @@ -380,6 +380,61 @@ int av_reallocp_array(void *ptr, size_t nmemb, size_t size); */ void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size); +/** + * Reallocate the pointed-to buffer if it is not large enough, otherwise do + * nothing. Old data is memcpy()'d to the start of the new buffer. The newly + * allocated space at the end of the buffer is zero-initialized. In other + * words the buffer is expanded with zeroes when necessary. + * + * If the pointed-to buffer is `NULL`, then a new zero-initialized buffer is + * allocated. + * + * If the pointed-to buffer is not large enough, and reallocation fails, + * `AVERROR(ENOMEM)` is returned. + * + * If nelem*elsize is too large then `AVERROR(EINVAL)` is returned. + * + * Contrary to av_fast_malloc(), *ptr and *size are not touched in case of + * error, to allow for proper cleanup. + * + * *ptr is not guaranteed to be an exact multiple of elsize bytes. + * + * This function is intended for use with arrays of structures that contain + * pointers that are allowed to grow and typically don't shrink. + * + * A typical use pattern follows: + * + * @code{.c} + * int foo_work(SomeContext *s) { + * if (ff_fast_recalloc(&s->foo, &s->foo_size, s->nfoo, sizeof(Foo))) + * return AVERROR(ENOMEM); + * for (x = 0; x < s->nfoo; x++) + * do stuff with s->foo[x] + * return 0; + * } + * + * void foo_close(SomeContext *s) { + * // note the use of s->foo_size, not s->nfoo + * for (x = 0; x < s->foo_size/sizeof(Foo); x++) + * av_freep(&s->foo[x].bar); + * av_freep(&s->foo); + * } + * @endcode + * + * @param[in,out] ptr Pointer to pointer to an already allocated buffer. + * `*ptr` will be overwritten with pointer to new + * buffer on success and will be left alone on failure + * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is + * updated to the new allocated size and will be left + * along on failure. + * @param[in] nelem Number of desired elements in *ptr + * @param[in] elsize Size of each element in *ptr + * @return Zero on success, <0 on error. + * @see av_fast_realloc() + * @see av_fast_malloc() + */ +int ff_fast_recalloc(void *ptr, unsigned int *size, size_t nelem, size_t elsize); + /** * Allocate a buffer, reusing the given one if large enough. * -- 2.30.2