/* $Id: fifo.c,v 1.17 2005/10/15 18:05:03 titer Exp $
This file is part of the HandBrake source code.
Homepage: .
It may be used under the terms of the GNU General Public License. */
#include "hb.h"
#ifndef SYS_DARWIN
#include
#endif
/* Fifo */
struct hb_fifo_s
{
hb_lock_t * lock;
int capacity;
int size;
int buffer_size;
hb_buffer_t * first;
hb_buffer_t * last;
};
#define MAX_BUFFER_POOLS 15
#define BUFFER_POOL_MAX_ELEMENTS 2048
struct hb_buffer_pools_s
{
int entries;
int allocated;
hb_fifo_t *pool[MAX_BUFFER_POOLS];
hb_lock_t *lock;
};
struct hb_buffer_pools_s buffers;
void hb_buffer_pool_init( void )
{
hb_fifo_t *buffer_pool;
int size = 512;
int max_size = 32768;;
buffers.entries = 0;
buffers.lock = hb_lock_init();
buffers.allocated = 0;
while(size <= max_size) {
buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
buffer_pool->buffer_size = size;
size *= 2;
}
}
void hb_buffer_pool_free( void )
{
int i;
int count;
int freed = 0;
hb_buffer_t *b;
hb_lock(buffers.lock);
for( i = 0; i < buffers.entries; i++)
{
count = 0;
while( ( b = hb_fifo_get(buffers.pool[i]) ) )
{
freed += b->alloc;
if( b->data )
{
free( b->data );
b->data = NULL;
}
free( b );
count++;
}
hb_log("Freed %d buffers of size %d", count, buffers.pool[i]->buffer_size);
}
hb_log("Allocated %d bytes of buffers on this pass and Freed %d bytes, %d bytes leaked",
buffers.allocated, freed, buffers.allocated - freed);
buffers.allocated = 0;
hb_unlock(buffers.lock);
}
hb_buffer_t * hb_buffer_init( int size )
{
hb_buffer_t * b;
int i;
hb_fifo_t *buffer_pool = NULL;
uint8_t *data;
int b_alloc;
int resize = 0;
if( size > 0 )
{
/*
* The buffer pools are allocated in increasing size
*/
for( i = 0; i < buffers.entries; i++ )
{
if( buffers.pool[i]->buffer_size >= size )
{
/*
* This pool is big enough, but are there any buffers in it?
*/
if( hb_fifo_size( buffers.pool[i] ) )
{
/*
* We've found a matching buffer pool, with buffers.
*/
buffer_pool = buffers.pool[i];
resize = buffers.pool[i]->buffer_size;
} else {
/*
* Buffer pool is empty,
*/
if( resize ) {
/*
* This is the second time through, so break
* out of here to avoid using too large a
* buffer for a small job.
*/
break;
}
resize = buffers.pool[i]->buffer_size;
}
}
}
}
/*
* Don't reuse the 0 size buffers, not much gain.
*/
if( size != 0 && buffer_pool )
{
b = hb_fifo_get( buffer_pool );
if( b )
{
/*
* Zero the contents of the buffer, would be nice if we
* didn't have to do this.
*
hb_log("Reused buffer size %d for size %d from pool %d depth %d",
b->alloc, size, smallest_pool->buffer_size,
hb_fifo_size(smallest_pool));
*/
data = b->data;
b_alloc = b->alloc;
memset( b, 0, sizeof(hb_buffer_t) );
b->alloc = b_alloc;
b->size = size;
b->data = data;
return( b );
}
}
/*
* No existing buffers, create a new one
*/
if( !( b = calloc( sizeof( hb_buffer_t ), 1 ) ) )
{
hb_log( "out of memory" );
return NULL;
}
b->size = size;
if( resize )
{
size = resize;
}
b->alloc = size;
/*
hb_log("Allocating new buffer of size %d for size %d",
b->alloc,
b->size);
*/
if (!size)
return b;
#if defined( SYS_DARWIN ) || defined( SYS_FREEBSD )
b->data = malloc( b->alloc );
#elif defined( SYS_CYGWIN )
/* FIXME */
b->data = malloc( b->alloc + 17 );
#else
b->data = memalign( 16, b->alloc );
#endif
if( !b->data )
{
hb_log( "out of memory" );
free( b );
return NULL;
}
buffers.allocated += b->alloc;
return b;
}
void hb_buffer_realloc( hb_buffer_t * b, int size )
{
/* No more alignment, but we don't care */
if( size < 2048 ) {
size = 2048;
}
b->data = realloc( b->data, size );
buffers.allocated -= b->alloc;
b->alloc = size;
buffers.allocated += b->alloc;
}
void hb_buffer_close( hb_buffer_t ** _b )
{
hb_buffer_t * b = *_b;
hb_fifo_t *buffer_pool = NULL;
int i;
/*
* Put the buffer into our free list in the matching buffer pool, if there is one.
*/
if( b->alloc != 0 )
{
for( i = 0; i < buffers.entries; i++ )
{
if( b->alloc == buffers.pool[i]->buffer_size )
{
buffer_pool = buffers.pool[i];
break;
}
}
}
if( buffer_pool )
{
if( !hb_fifo_is_full( buffer_pool ) )
{
if(b->data)
{
/*
hb_log("Putting a buffer of size %d on pool %d, depth %d",
b->alloc,
buffer_pool->buffer_size,
hb_fifo_size(buffer_pool));
*/
hb_fifo_push( buffer_pool, b );
} else {
free(b);
}
} else {
/*
* Got a load of these size ones already, free this buffer.
*
hb_log("Buffer pool for size %d full, freeing buffer", b->alloc);
*/
if( b->data )
{
free( b->data );
}
buffers.allocated -= b->alloc;
free( b );
}
} else {
/*
* Need a new buffer pool for this size.
*/
hb_lock(buffers.lock);
if ( b->alloc != 0 && buffers.entries < MAX_BUFFER_POOLS)
{
buffer_pool = buffers.pool[buffers.entries++] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS);
buffer_pool->buffer_size = b->alloc;
hb_fifo_push( buffer_pool, b );
/*
hb_log("*** Allocated a new buffer pool for size %d [%d]", b->alloc,
buffers.entries );
*/
} else {
if( b->alloc != 0 )
{
for( i = buffers.entries-1; i >= 0; i-- )
{
if( hb_fifo_size(buffers.pool[i]) == 0 )
{
/*
* Reuse this pool as it is empty.
*/
buffers.pool[i]->buffer_size = b->alloc;
hb_fifo_push( buffers.pool[i], b );
b = NULL;
break;
}
}
}
if( b )
{
if( b->data )
{
free( b->data );
b->data = NULL;
buffers.allocated -= b->alloc;
}
free( b );
}
}
hb_unlock(buffers.lock);
}
*_b = NULL;
}
void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src )
{
dst->start = src->start;
dst->stop = src->stop;
dst->new_chap = src->new_chap;
dst->frametype = src->frametype;
dst->flags = src->flags;
}
hb_fifo_t * hb_fifo_init( int capacity )
{
hb_fifo_t * f;
f = calloc( sizeof( hb_fifo_t ), 1 );
f->lock = hb_lock_init();
f->capacity = capacity;
f->buffer_size = 0;
return f;
}
int hb_fifo_size( hb_fifo_t * f )
{
int ret;
hb_lock( f->lock );
ret = f->size;
hb_unlock( f->lock );
return ret;
}
int hb_fifo_is_full( hb_fifo_t * f )
{
int ret;
hb_lock( f->lock );
ret = ( f->size >= f->capacity );
hb_unlock( f->lock );
return ret;
}
float hb_fifo_percent_full( hb_fifo_t * f )
{
float ret;
hb_lock( f->lock );
ret = f->size / f->capacity;
hb_unlock( f->lock );
return ret;
}
hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
{
hb_buffer_t * b;
hb_lock( f->lock );
if( f->size < 1 )
{
hb_unlock( f->lock );
return NULL;
}
b = f->first;
f->first = b->next;
b->next = NULL;
f->size -= 1;
hb_unlock( f->lock );
return b;
}
hb_buffer_t * hb_fifo_see( hb_fifo_t * f )
{
hb_buffer_t * b;
hb_lock( f->lock );
if( f->size < 1 )
{
hb_unlock( f->lock );
return NULL;
}
b = f->first;
hb_unlock( f->lock );
return b;
}
hb_buffer_t * hb_fifo_see2( hb_fifo_t * f )
{
hb_buffer_t * b;
hb_lock( f->lock );
if( f->size < 2 )
{
hb_unlock( f->lock );
return NULL;
}
b = f->first->next;
hb_unlock( f->lock );
return b;
}
void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
{
if( !b )
{
return;
}
hb_lock( f->lock );
if( f->size > 0 )
{
f->last->next = b;
}
else
{
f->first = b;
}
f->last = b;
f->size += 1;
while( f->last->next )
{
f->size += 1;
f->last = f->last->next;
}
hb_unlock( f->lock );
}
void hb_fifo_close( hb_fifo_t ** _f )
{
hb_fifo_t * f = *_f;
hb_buffer_t * b;
hb_log( "fifo_close: trashing %d buffer(s)", hb_fifo_size( f ) );
while( ( b = hb_fifo_get( f ) ) )
{
hb_buffer_close( &b );
}
hb_lock_close( &f->lock );
free( f );
*_f = NULL;
}