On Wed, 14 Mar 2018 11:13:52 -0300
James Almer <jamr...@gmail.com> wrote:

> On 3/14/2018 4:14 AM, wm4 wrote:
> > On Tue, 13 Mar 2018 20:48:56 -0300
> > James Almer <jamr...@gmail.com> wrote:
> >   
> >> Same concept as av_fast_malloc(). If the buffer passed to it is writable
> >> and big enough it will be reused, otherwise a new one will be allocated
> >> instead.
> >>
> >> Signed-off-by: James Almer <jamr...@gmail.com>
> >> ---
> >> TODO: Changelog and APIChanges entries, version bump.
> >>
> >>  libavutil/buffer.c | 63 
> >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>  libavutil/buffer.h | 42 ++++++++++++++++++++++++++++++++++++
> >>  2 files changed, 105 insertions(+)
> >>
> >> diff --git a/libavutil/buffer.c b/libavutil/buffer.c
> >> index 8d1aa5fa84..16ce1b82e2 100644
> >> --- a/libavutil/buffer.c
> >> +++ b/libavutil/buffer.c
> >> @@ -215,6 +215,69 @@ int av_buffer_realloc(AVBufferRef **pbuf, int size)
> >>      return 0;
> >>  }
> >>  
> >> +static av_always_inline int buffer_fast_alloc(AVBufferRef **pbuf, int 
> >> size, int zero_alloc)
> >> +{
> >> +    AVBufferRef *buf = *pbuf;
> >> +    AVBuffer *b;
> >> +    uint8_t *data;
> >> +
> >> +    if (!buf || !av_buffer_is_writable(buf) || buf->data != 
> >> buf->buffer->data) {
> >> +        /* Buffer can't be reused, and neither can the entire AVBufferRef.
> >> +         * Unref the latter and alloc a new one. */
> >> +        av_buffer_unref(pbuf);
> >> +
> >> +        buf = av_buffer_alloc(size);
> >> +        if (!buf)
> >> +            return AVERROR(ENOMEM);
> >> +
> >> +        if (zero_alloc)
> >> +            memset(buf->data, 0, size);
> >> +
> >> +        *pbuf = buf;
> >> +        return 0;
> >> +    }
> >> +    b = buf->buffer;
> >> +
> >> +    if (size <= b->size) {
> >> +        /* Buffer can be reused. Update the size of AVBufferRef but leave 
> >> the
> >> +         * AVBuffer untouched. */
> >> +        buf->size = FFMAX(0, size);
> >> +        return 0;
> >> +    }
> >> +
> >> +    /* Buffer can't be reused, but there's no need to alloc new AVBuffer 
> >> and
> >> +     * AVBufferRef structs. Free the existing buffer, allocate a new one, 
> >> and
> >> +     * reset AVBuffer and AVBufferRef to default values. */
> >> +    b->free(b->opaque, b->data);
> >> +    b->free   = av_buffer_default_free;
> >> +    b->opaque = NULL;
> >> +    b->flags  = 0;
> >> +
> >> +    data = av_malloc(size);
> >> +    if (!data) {
> >> +        av_buffer_unref(pbuf);
> >> +        return AVERROR(ENOMEM);
> >> +    }
> >> +
> >> +    if (zero_alloc)
> >> +        memset(data, 0, size);
> >> +
> >> +    b->data = buf->data = data;
> >> +    b->size = buf->size = size;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +int av_buffer_fast_alloc(AVBufferRef **pbuf, int size)
> >> +{
> >> +    return buffer_fast_alloc(pbuf, size, 0);
> >> +}
> >> +
> >> +int av_buffer_fast_allocz(AVBufferRef **pbuf, int size)
> >> +{
> >> +    return buffer_fast_alloc(pbuf, size, 1);
> >> +}
> >> +
> >>  AVBufferPool *av_buffer_pool_init2(int size, void *opaque,
> >>                                     AVBufferRef* (*alloc)(void *opaque, 
> >> int size),
> >>                                     void (*pool_free)(void *opaque))
> >> diff --git a/libavutil/buffer.h b/libavutil/buffer.h
> >> index 73b6bd0b14..1166017d22 100644
> >> --- a/libavutil/buffer.h
> >> +++ b/libavutil/buffer.h
> >> @@ -197,6 +197,48 @@ int av_buffer_make_writable(AVBufferRef **buf);
> >>   */
> >>  int av_buffer_realloc(AVBufferRef **buf, int size);
> >>  
> >> +/**
> >> + * Allocate a buffer, reusing the given one if writable and large enough.
> >> + *
> >> + * @code{.c}
> >> + * AVBufferRef *buf = ...;
> >> + * int ret = av_buffer_fast_alloc(&buf, size);
> >> + * if (ret < 0) {
> >> + *     // Allocation failed; buf already freed
> >> + *     return ret;
> >> + * }
> >> + * @endcode
> >> + *
> >> + * @param buf  A buffer reference. *buf may be NULL. On success, a new 
> >> buffer
> >> + *             reference will be written in its place. On failure, it 
> >> will be
> >> + *             unreferenced and set to NULL.
> >> + * @param size Required buffer size.
> >> + *
> >> + * @return 0 on success, a negative AVERROR on failure.
> >> + *
> >> + * @see av_buffer_realloc()
> >> + * @see av_buffer_fast_allocz()
> >> + */
> >> +int av_buffer_fast_alloc(AVBufferRef **buf, int size);
> >> +
> >> +/**
> >> + * Allocate and clear a buffer, reusing the given one if writable and 
> >> large
> >> + * enough.
> >> + *
> >> + * Like av_buffer_fast_alloc(), but all newly allocated space is initially
> >> + * cleared. Reused buffer is not cleared.
> >> + *
> >> + * @param buf  A buffer reference. *buf may be NULL. On success, a new 
> >> buffer
> >> + *             reference will be written in its place. On failure, it 
> >> will be
> >> + *             unreferenced and set to NULL.
> >> + * @param size Required buffer size.
> >> + *
> >> + * @return 0 on success, a negative AVERROR on failure.
> >> + *
> >> + * @see av_buffer_fast_alloc()
> >> + */
> >> +int av_buffer_fast_allocz(AVBufferRef **buf, int size);
> >> +
> >>  /**
> >>   * @}
> >>   */  
> > 
> > Wouldn't it be better to avoid writing a lot of new allocation code
> > (with possibly subtle problems), and just use av_buffer_realloc() to
> > handle reallocation? (Possibly it could be moved to an internal
> > function to avoid the memcpy() required by realloc.)  
> 
> There are some differences that make most code in av_buffer_realloc()
> hard to be reused.
> In this one I'm using av_malloc instead of av_realloc, I update the
> AVBufferRef size with the requested size if it's equal or smaller than
> the available one and return doing nothing else instead of returning
> doing nothing at all only if the requested size is the exact same as the
> available one, I need to take precautions about what kind of buffer is
> passed to the function by properly freeing it using its callback then
> replacing it with default values, instead of knowing beforehand that the
> buffer was effectively created by av_buffer_realloc() itself where it
> can simply call av_realloc, etc.
> 
> Trying to reuse code to follow all those details would make it harder to
> read and probably more prone to mistakes than just carefully writing the
> two separate functions doing the specific checks and allocations they
> require.

Fine, if you say so. I'd probably argue we should still try to share
some code, since it duplicates all the allocation _and_ deallocation
code, only for the additional check to skip doing anything if the
underlying buffer is big enough. Anyway, I'm not blocking the patch
over this, and I see no technical issues in the patch.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to