Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On 3/27/2018 2:58 PM, wm4 wrote: > On Tue, 27 Mar 2018 14:16:15 -0300 > James Almer wrote: > >> On 3/20/2018 7:44 PM, James Almer wrote: >>> On 3/16/2018 3:21 PM, James Almer wrote: Signed-off-by: James Almer --- This is a proof of concept for a dynamic size buffer pool API. For the purpose of easy testing and reviewing I replaced the current linked list used to keep a pool of fixed size buffers with the tree based pool that will be used to keep a pool of varying size buffers, instead of adding a new set of functions exclusively for the new API. The final committed work doesn't necessarely have to do the above, as there's no real benefit using a tree when you only need a fixed size buffer pool, other than simplying things. I'm open to suggestions about how to introduce this. Completely separate set of functions and struct names? Sharing the struct and init/uninit functions and only adding a new get() one like in this patch? Any preferences with function/struct naming, for that matter? >>> >>> Ping? >> >> No comments or preferences at all on how to introduce this? > > Well, it's a pretty big mess (so many things to consider). That's > mostly about the implementation details. > > I think the API you picked is relatively nice. It feels weird that a > pool that is initialized with a size has a function to allocate buffers > with a different size. I submitted it like this for easy review and testing. I wasn't really expecting to push it without changes precisely because the init() function is meant for fixed sized buffers. > So if you want some bikeshedding, sure, I can > provide you with some colors: > > AVDynamicBufferPool *av_dynamic_buffer_pool_create(alloc_fn, free_fn); > AVBufferRef *av_dynamic_buffer_pool_get(AVDynamicBufferPool *pool, > size_t size); > void av_dynamic_buffer_pool_uininit(AVDynamicBufferPool **pool); > > or: > > // sets *pool if it was NULL > // (where to put alloc/free FNs for custom alloc?) > AVBufferRef *av_dynamic_buffer_pool_get(AVDynamicBufferPool **pool, > size_t size); > void av_dynamic_buffer_pool_uininit(AVDynamicBufferPool **pool); > > or: > > // sets *pool if it was NULL > // free the pool with av_buffer_unref() > AVBufferRef *av_dynamic_buffer_pool_get(AVBufferRef **pool, > size_t size); Ok so, separate set of struct and functions. Probably best in order to avoid having to add all kinds of workarounds for things like calling dyn functions using pools created with the fixed size init(). > > or just force the API user to pass size=0 to av_buffer_pool_init() and > enforce the use of the correct alloc function (fixed size/dynamic size) > at runtime. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On Tue, 27 Mar 2018 14:16:15 -0300 James Almer wrote: > On 3/20/2018 7:44 PM, James Almer wrote: > > On 3/16/2018 3:21 PM, James Almer wrote: > >> Signed-off-by: James Almer > >> --- > >> This is a proof of concept for a dynamic size buffer pool API. > >> > >> For the purpose of easy testing and reviewing I replaced the current > >> linked list used to keep a pool of fixed size buffers with the tree > >> based pool that will be used to keep a pool of varying size buffers, > >> instead of adding a new set of functions exclusively for the new API. > >> The final committed work doesn't necessarely have to do the above, as > >> there's no real benefit using a tree when you only need a fixed size > >> buffer pool, other than simplying things. > >> > >> I'm open to suggestions about how to introduce this. Completely > >> separate set of functions and struct names? Sharing the struct and > >> init/uninit functions and only adding a new get() one like in this > >> patch? > >> Any preferences with function/struct naming, for that matter? > > > > Ping? > > No comments or preferences at all on how to introduce this? Well, it's a pretty big mess (so many things to consider). That's mostly about the implementation details. I think the API you picked is relatively nice. It feels weird that a pool that is initialized with a size has a function to allocate buffers with a different size. So if you want some bikeshedding, sure, I can provide you with some colors: AVDynamicBufferPool *av_dynamic_buffer_pool_create(alloc_fn, free_fn); AVBufferRef *av_dynamic_buffer_pool_get(AVDynamicBufferPool *pool, size_t size); void av_dynamic_buffer_pool_uininit(AVDynamicBufferPool **pool); or: // sets *pool if it was NULL // (where to put alloc/free FNs for custom alloc?) AVBufferRef *av_dynamic_buffer_pool_get(AVDynamicBufferPool **pool, size_t size); void av_dynamic_buffer_pool_uininit(AVDynamicBufferPool **pool); or: // sets *pool if it was NULL // free the pool with av_buffer_unref() AVBufferRef *av_dynamic_buffer_pool_get(AVBufferRef **pool, size_t size); or just force the API user to pass size=0 to av_buffer_pool_init() and enforce the use of the correct alloc function (fixed size/dynamic size) at runtime. ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On 3/20/2018 7:44 PM, James Almer wrote: > On 3/16/2018 3:21 PM, James Almer wrote: >> Signed-off-by: James Almer >> --- >> This is a proof of concept for a dynamic size buffer pool API. >> >> For the purpose of easy testing and reviewing I replaced the current >> linked list used to keep a pool of fixed size buffers with the tree >> based pool that will be used to keep a pool of varying size buffers, >> instead of adding a new set of functions exclusively for the new API. >> The final committed work doesn't necessarely have to do the above, as >> there's no real benefit using a tree when you only need a fixed size >> buffer pool, other than simplying things. >> >> I'm open to suggestions about how to introduce this. Completely >> separate set of functions and struct names? Sharing the struct and >> init/uninit functions and only adding a new get() one like in this >> patch? >> Any preferences with function/struct naming, for that matter? > > Ping? No comments or preferences at all on how to introduce this? ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On 3/16/2018 3:21 PM, James Almer wrote: > Signed-off-by: James Almer > --- > This is a proof of concept for a dynamic size buffer pool API. > > For the purpose of easy testing and reviewing I replaced the current > linked list used to keep a pool of fixed size buffers with the tree > based pool that will be used to keep a pool of varying size buffers, > instead of adding a new set of functions exclusively for the new API. > The final committed work doesn't necessarely have to do the above, as > there's no real benefit using a tree when you only need a fixed size > buffer pool, other than simplying things. > > I'm open to suggestions about how to introduce this. Completely > separate set of functions and struct names? Sharing the struct and > init/uninit functions and only adding a new get() one like in this > patch? > Any preferences with function/struct naming, for that matter? Ping? ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On Sat, Mar 17, 2018 at 10:46:22PM -0300, James Almer wrote: > On 3/17/2018 10:33 PM, Michael Niedermayer wrote: > > On Sat, Mar 17, 2018 at 09:26:32PM -0300, James Almer wrote: > >> On 3/17/2018 8:48 PM, Michael Niedermayer wrote: > >>> On Fri, Mar 16, 2018 at 03:21:41PM -0300, James Almer wrote: > Signed-off-by: James Almer > --- > This is a proof of concept for a dynamic size buffer pool API. > > For the purpose of easy testing and reviewing I replaced the current > linked list used to keep a pool of fixed size buffers with the tree > based pool that will be used to keep a pool of varying size buffers, > instead of adding a new set of functions exclusively for the new API. > The final committed work doesn't necessarely have to do the above, as > there's no real benefit using a tree when you only need a fixed size > buffer pool, other than simplying things. > > I'm open to suggestions about how to introduce this. Completely > separate set of functions and struct names? Sharing the struct and > init/uninit functions and only adding a new get() one like in this > patch? > Any preferences with function/struct naming, for that matter? > > libavutil/buffer.c | 98 > - > libavutil/buffer.h | 2 + > libavutil/buffer_internal.h | 6 ++- > 3 files changed, 85 insertions(+), 21 deletions(-) > >>> > >>> not sure its not intended but this causes differences > >>> in error concealment on many files > >> > >> Not intended by me, at least. And not sure how using a tree instead of a > >> linked list to keep a pool of buffers could affect that. The way they > >> are allocated or returned is not changed, and all the existing users are > >> still only creating fixed sized buffers. > >> > > > >> Do you have an idea of what could be happening? The way the tree is > > > > is always the same buffer used as before ? > > Was thinking that, yes. The pool organized by the tree most assuredly > orders the buffers in a different way than the linked list (Which i > think is simply FIFO), so the h264 decoder or whatever is handling the > pool here now gets a different buffer after calling av_buffer_pool_get(). > This would mean the EC code is overreading bytes, and that the buffer is > not zeroed after being requested. The former should probably be fixed. the problem is probably that damaged video frames leave some areas uninitialized. Quite possibly a consequence of some corner cases not being supported in the EC code. This is a bug of course if its what happens and should be fixed. [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB If you drop bombs on a foreign country and kill a hundred thousand innocent people, expect your government to call the consequence "unprovoked inhuman terrorist attacks" and use it to justify dropping more bombs and killing more people. The technology changed, the idea is old. signature.asc Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On 3/17/2018 10:33 PM, Michael Niedermayer wrote: > On Sat, Mar 17, 2018 at 09:26:32PM -0300, James Almer wrote: >> On 3/17/2018 8:48 PM, Michael Niedermayer wrote: >>> On Fri, Mar 16, 2018 at 03:21:41PM -0300, James Almer wrote: Signed-off-by: James Almer --- This is a proof of concept for a dynamic size buffer pool API. For the purpose of easy testing and reviewing I replaced the current linked list used to keep a pool of fixed size buffers with the tree based pool that will be used to keep a pool of varying size buffers, instead of adding a new set of functions exclusively for the new API. The final committed work doesn't necessarely have to do the above, as there's no real benefit using a tree when you only need a fixed size buffer pool, other than simplying things. I'm open to suggestions about how to introduce this. Completely separate set of functions and struct names? Sharing the struct and init/uninit functions and only adding a new get() one like in this patch? Any preferences with function/struct naming, for that matter? libavutil/buffer.c | 98 - libavutil/buffer.h | 2 + libavutil/buffer_internal.h | 6 ++- 3 files changed, 85 insertions(+), 21 deletions(-) >>> >>> not sure its not intended but this causes differences >>> in error concealment on many files >> >> Not intended by me, at least. And not sure how using a tree instead of a >> linked list to keep a pool of buffers could affect that. The way they >> are allocated or returned is not changed, and all the existing users are >> still only creating fixed sized buffers. >> > >> Do you have an idea of what could be happening? The way the tree is > > is always the same buffer used as before ? Was thinking that, yes. The pool organized by the tree most assuredly orders the buffers in a different way than the linked list (Which i think is simply FIFO), so the h264 decoder or whatever is handling the pool here now gets a different buffer after calling av_buffer_pool_get(). This would mean the EC code is overreading bytes, and that the buffer is not zeroed after being requested. The former should probably be fixed. Fortunately not a bug from this patch/implementation, in that case. > a different buffer with different content could probably leak the content > through into the output on some errors. > > >> being handled is afaik correct, judging by the doxy. Removing and adding >> one element at a time using a simple cmp() function and with no way to >> have races since it's all controlled by a mutex. >> >>> an example would be something like this: >>> >>> ffmpeg -threads 1 -i 1069/green-block-artifacts-from-canon-100-hs.MOV >>> test.avi >>> >>> >>> [...] >>> >>> >>> >>> ___ >>> ffmpeg-devel mailing list >>> ffmpeg-devel@ffmpeg.org >>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel >>> >> >> ___ >> ffmpeg-devel mailing list >> ffmpeg-devel@ffmpeg.org >> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > > > ___ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On Sat, Mar 17, 2018 at 09:26:32PM -0300, James Almer wrote: > On 3/17/2018 8:48 PM, Michael Niedermayer wrote: > > On Fri, Mar 16, 2018 at 03:21:41PM -0300, James Almer wrote: > >> Signed-off-by: James Almer > >> --- > >> This is a proof of concept for a dynamic size buffer pool API. > >> > >> For the purpose of easy testing and reviewing I replaced the current > >> linked list used to keep a pool of fixed size buffers with the tree > >> based pool that will be used to keep a pool of varying size buffers, > >> instead of adding a new set of functions exclusively for the new API. > >> The final committed work doesn't necessarely have to do the above, as > >> there's no real benefit using a tree when you only need a fixed size > >> buffer pool, other than simplying things. > >> > >> I'm open to suggestions about how to introduce this. Completely > >> separate set of functions and struct names? Sharing the struct and > >> init/uninit functions and only adding a new get() one like in this > >> patch? > >> Any preferences with function/struct naming, for that matter? > >> > >> libavutil/buffer.c | 98 > >> - > >> libavutil/buffer.h | 2 + > >> libavutil/buffer_internal.h | 6 ++- > >> 3 files changed, 85 insertions(+), 21 deletions(-) > > > > not sure its not intended but this causes differences > > in error concealment on many files > > Not intended by me, at least. And not sure how using a tree instead of a > linked list to keep a pool of buffers could affect that. The way they > are allocated or returned is not changed, and all the existing users are > still only creating fixed sized buffers. > > Do you have an idea of what could be happening? The way the tree is is always the same buffer used as before ? a different buffer with different content could probably leak the content through into the output on some errors. > being handled is afaik correct, judging by the doxy. Removing and adding > one element at a time using a simple cmp() function and with no way to > have races since it's all controlled by a mutex. > > > an example would be something like this: > > > > ffmpeg -threads 1 -i 1069/green-block-artifacts-from-canon-100-hs.MOV > > test.avi > > > > > > [...] > > > > > > > > ___ > > ffmpeg-devel mailing list > > ffmpeg-devel@ffmpeg.org > > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > > > ___ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB While the State exists there can be no freedom; when there is freedom there will be no State. -- Vladimir Lenin signature.asc Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On 3/17/2018 8:48 PM, Michael Niedermayer wrote: > On Fri, Mar 16, 2018 at 03:21:41PM -0300, James Almer wrote: >> Signed-off-by: James Almer >> --- >> This is a proof of concept for a dynamic size buffer pool API. >> >> For the purpose of easy testing and reviewing I replaced the current >> linked list used to keep a pool of fixed size buffers with the tree >> based pool that will be used to keep a pool of varying size buffers, >> instead of adding a new set of functions exclusively for the new API. >> The final committed work doesn't necessarely have to do the above, as >> there's no real benefit using a tree when you only need a fixed size >> buffer pool, other than simplying things. >> >> I'm open to suggestions about how to introduce this. Completely >> separate set of functions and struct names? Sharing the struct and >> init/uninit functions and only adding a new get() one like in this >> patch? >> Any preferences with function/struct naming, for that matter? >> >> libavutil/buffer.c | 98 >> - >> libavutil/buffer.h | 2 + >> libavutil/buffer_internal.h | 6 ++- >> 3 files changed, 85 insertions(+), 21 deletions(-) > > not sure its not intended but this causes differences > in error concealment on many files Not intended by me, at least. And not sure how using a tree instead of a linked list to keep a pool of buffers could affect that. The way they are allocated or returned is not changed, and all the existing users are still only creating fixed sized buffers. Do you have an idea of what could be happening? The way the tree is being handled is afaik correct, judging by the doxy. Removing and adding one element at a time using a simple cmp() function and with no way to have races since it's all controlled by a mutex. > an example would be something like this: > > ffmpeg -threads 1 -i 1069/green-block-artifacts-from-canon-100-hs.MOV test.avi > > > [...] > > > > ___ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Re: [FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
On Fri, Mar 16, 2018 at 03:21:41PM -0300, James Almer wrote: > Signed-off-by: James Almer > --- > This is a proof of concept for a dynamic size buffer pool API. > > For the purpose of easy testing and reviewing I replaced the current > linked list used to keep a pool of fixed size buffers with the tree > based pool that will be used to keep a pool of varying size buffers, > instead of adding a new set of functions exclusively for the new API. > The final committed work doesn't necessarely have to do the above, as > there's no real benefit using a tree when you only need a fixed size > buffer pool, other than simplying things. > > I'm open to suggestions about how to introduce this. Completely > separate set of functions and struct names? Sharing the struct and > init/uninit functions and only adding a new get() one like in this > patch? > Any preferences with function/struct naming, for that matter? > > libavutil/buffer.c | 98 > - > libavutil/buffer.h | 2 + > libavutil/buffer_internal.h | 6 ++- > 3 files changed, 85 insertions(+), 21 deletions(-) not sure its not intended but this causes differences in error concealment on many files an example would be something like this: ffmpeg -threads 1 -i 1069/green-block-artifacts-from-canon-100-hs.MOV test.avi [...] -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB If you think the mosad wants you dead since a long time then you are either wrong or dead since a long time. signature.asc Description: PGP signature ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] [RFC][WIP] avutil/buffer: add add a dynamnic size buffer pool API
Signed-off-by: James Almer --- This is a proof of concept for a dynamic size buffer pool API. For the purpose of easy testing and reviewing I replaced the current linked list used to keep a pool of fixed size buffers with the tree based pool that will be used to keep a pool of varying size buffers, instead of adding a new set of functions exclusively for the new API. The final committed work doesn't necessarely have to do the above, as there's no real benefit using a tree when you only need a fixed size buffer pool, other than simplying things. I'm open to suggestions about how to introduce this. Completely separate set of functions and struct names? Sharing the struct and init/uninit functions and only adding a new get() one like in this patch? Any preferences with function/struct naming, for that matter? libavutil/buffer.c | 98 - libavutil/buffer.h | 2 + libavutil/buffer_internal.h | 6 ++- 3 files changed, 85 insertions(+), 21 deletions(-) diff --git a/libavutil/buffer.c b/libavutil/buffer.c index 8d1aa5fa84..9fe5aa9825 100644 --- a/libavutil/buffer.c +++ b/libavutil/buffer.c @@ -251,19 +251,27 @@ AVBufferPool *av_buffer_pool_init(int size, AVBufferRef* (*alloc)(int size)) return pool; } +/* Callback function to free every pooled buffer during uninit, + * to be used with av_tree_enumerate(). */ +static int free_node(void *opaque, void *elem) +{ +BufferPoolEntry *buf = elem; + +buf->free(buf->opaque, buf->data); +av_free(buf); + +return 0; +} + /* * This function gets called when the pool has been uninited and * all the buffers returned to it. */ static void buffer_pool_free(AVBufferPool *pool) { -while (pool->pool) { -BufferPoolEntry *buf = pool->pool; -pool->pool = buf->next; +av_tree_enumerate(pool->root, NULL, NULL, free_node); +av_tree_destroy(pool->root); -buf->free(buf->opaque, buf->data); -av_freep(&buf); -} ff_mutex_destroy(&pool->mutex); if (pool->pool_free) @@ -285,17 +293,29 @@ void av_buffer_pool_uninit(AVBufferPool **ppool) buffer_pool_free(pool); } +/* Callback function to compare two nodes, order them based + * on size, and retrieve them, to be used with av_tree_insert(). */ +static int cmp_insert(const void *key, const void *node) +{ +int ret = FFDIFFSIGN(((const BufferPoolEntry *) key)->size, ((const BufferPoolEntry *) node)->size); + +if (!ret) +ret = FFDIFFSIGN(((const BufferPoolEntry *) key)->data, ((const BufferPoolEntry *) node)->data); +return ret; +} + static void pool_release_buffer(void *opaque, uint8_t *data) { BufferPoolEntry *buf = opaque; AVBufferPool *pool = buf->pool; if(CONFIG_MEMORY_POISONING) -memset(buf->data, FF_MEMORY_POISON, pool->size); +memset(buf->data, FF_MEMORY_POISON, buf->size); ff_mutex_lock(&pool->mutex); -buf->next = pool->pool; -pool->pool = buf; +/* Add the buffer into the pool, using the preallocated + * AVTreeNode stored in buf->node */ +av_tree_insert(&pool->root, buf, cmp_insert, &buf->node); ff_mutex_unlock(&pool->mutex); if (atomic_fetch_add_explicit(&pool->refcount, -1, memory_order_acq_rel) == 1) @@ -304,13 +324,13 @@ static void pool_release_buffer(void *opaque, uint8_t *data) /* allocate a new buffer and override its free() callback so that * it is returned to the pool on free */ -static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) +static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool, int size) { BufferPoolEntry *buf; AVBufferRef *ret; -ret = pool->alloc2 ? pool->alloc2(pool->opaque, pool->size) : - pool->alloc(pool->size); +ret = pool->alloc2 ? pool->alloc2(pool->opaque, size) : + pool->alloc(size); if (!ret) return NULL; @@ -320,9 +340,17 @@ static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) return NULL; } +buf->node = av_tree_node_alloc(); +if (!buf->node) { +av_free(buf); +av_buffer_unref(&ret); +return NULL; +} + buf->data = ret->buffer->data; buf->opaque = ret->buffer->opaque; buf->free = ret->buffer->free; +buf->size = size; buf->pool = pool; ret->buffer->opaque = buf; @@ -331,22 +359,49 @@ static AVBufferRef *pool_alloc_buffer(AVBufferPool *pool) return ret; } -AVBufferRef *av_buffer_pool_get(AVBufferPool *pool) +/* Callback function to compare the requested size with the + * size of existing buffers in the pool, to be used with + * av_tree_find(). */ +static int cmp_int(const void *key, const void *node) +{ +return FFDIFFSIGN(*(const int *)key, ((const BufferPoolEntry *) node)->size); +} + +AVBufferRef *av_buffer_pool_get_dyn(AVBufferPool *pool, int size) { AVBufferRef *ret; -BufferPoolEntry *buf; +BufferPoolEntry *buf, *next[2] = { NUL