2001-11-28 12:13:00 +03:00
/*
2002-02-12 17:12:13 +03:00
* Copyright ( C ) 2001 - 2002 Sistina Software ( UK ) Limited .
2001-11-28 12:13:00 +03:00
*
* This file is released under the LGPL .
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2001-11-28 12:13:00 +03:00
# include "pool.h"
2002-11-18 17:01:16 +03:00
# include "lvm-types.h"
2001-11-28 12:13:00 +03:00
struct chunk {
char * begin , * end ;
struct chunk * prev ;
} ;
struct pool {
2002-04-24 22:20:51 +04:00
struct chunk * chunk , * spare_chunk ; /* spare_chunk is a one entry free
list to stop ' bobbling ' */
2001-11-28 12:13:00 +03:00
size_t chunk_size ;
size_t object_len ;
2002-04-24 22:20:51 +04:00
unsigned object_alignment ;
2001-11-28 12:13:00 +03:00
} ;
void _align_chunk ( struct chunk * c , unsigned alignment ) ;
struct chunk * _new_chunk ( struct pool * p , size_t s ) ;
/* by default things come out aligned for doubles */
# define DEFAULT_ALIGNMENT __alignof__ (double)
struct pool * pool_create ( size_t chunk_hint )
{
size_t new_size = 1024 ;
struct pool * p = dbg_malloc ( sizeof ( * p ) ) ;
if ( ! p ) {
2003-04-02 23:11:23 +04:00
log_error ( " Couldn't create memory pool (size % " PRIsize_t " ) " ,
2002-01-10 19:47:25 +03:00
sizeof ( * p ) ) ;
2001-11-28 12:13:00 +03:00
return 0 ;
}
memset ( p , 0 , sizeof ( * p ) ) ;
/* round chunk_hint up to the next power of 2 */
p - > chunk_size = chunk_hint + sizeof ( struct chunk ) ;
while ( new_size < p - > chunk_size )
new_size < < = 1 ;
p - > chunk_size = new_size ;
return p ;
}
void pool_destroy ( struct pool * p )
{
struct chunk * c , * pr ;
dbg_free ( p - > spare_chunk ) ;
c = p - > chunk ;
while ( c ) {
pr = c - > prev ;
dbg_free ( c ) ;
c = pr ;
}
dbg_free ( p ) ;
}
void * pool_alloc ( struct pool * p , size_t s )
{
return pool_alloc_aligned ( p , s , DEFAULT_ALIGNMENT ) ;
}
void * pool_alloc_aligned ( struct pool * p , size_t s , unsigned alignment )
{
struct chunk * c = p - > chunk ;
void * r ;
2002-04-24 22:20:51 +04:00
/* realign begin */
2001-11-28 12:13:00 +03:00
if ( c )
_align_chunk ( c , alignment ) ;
/* have we got room ? */
2002-04-24 22:20:51 +04:00
if ( ! c | | ( c - > begin > c - > end ) | | ( c - > end - c - > begin < s ) ) {
2001-11-28 12:13:00 +03:00
/* allocate new chunk */
int needed = s + alignment + sizeof ( struct chunk ) ;
c = _new_chunk ( p , ( needed > p - > chunk_size ) ?
needed : p - > chunk_size ) ;
2001-12-12 17:54:24 +03:00
if ( ! c )
return NULL ;
2001-11-28 12:13:00 +03:00
_align_chunk ( c , alignment ) ;
}
r = c - > begin ;
c - > begin + = s ;
return r ;
}
void pool_empty ( struct pool * p )
{
struct chunk * c ;
for ( c = p - > chunk ; c & & c - > prev ; c = c - > prev )
;
if ( p - > chunk )
pool_free ( p , ( char * ) ( p - > chunk + 1 ) ) ;
}
void pool_free ( struct pool * p , void * ptr )
{
struct chunk * c = p - > chunk ;
while ( c ) {
if ( ( ( char * ) c < ( char * ) ptr ) & &
( ( char * ) c - > end > ( char * ) ptr ) ) {
c - > begin = ptr ;
break ;
}
if ( p - > spare_chunk )
dbg_free ( p - > spare_chunk ) ;
p - > spare_chunk = c ;
c = c - > prev ;
}
if ( ! c )
2002-01-28 00:30:47 +03:00
log_error ( " Internal error: pool_free asked to free pointer "
" not in pool " ) ;
2001-11-28 12:13:00 +03:00
else
p - > chunk = c ;
}
2001-12-20 14:52:54 +03:00
int pool_begin_object ( struct pool * p , size_t hint )
2001-11-28 12:13:00 +03:00
{
struct chunk * c = p - > chunk ;
2001-12-12 17:25:20 +03:00
const size_t align = DEFAULT_ALIGNMENT ;
2001-11-28 12:13:00 +03:00
p - > object_len = 0 ;
p - > object_alignment = align ;
2002-01-09 16:06:02 +03:00
if ( c )
_align_chunk ( c , align ) ;
if ( ! c | | ( c - > begin > c - > end ) | | ( c - > end - c - > begin < hint ) ) {
2001-11-28 12:13:00 +03:00
/* allocate a new chunk */
c = _new_chunk ( p ,
2001-12-20 14:52:54 +03:00
hint > ( p - > chunk_size - sizeof ( struct chunk ) ) ?
hint + sizeof ( struct chunk ) + align :
2001-11-28 12:13:00 +03:00
p - > chunk_size ) ;
2001-12-12 17:54:24 +03:00
if ( ! c )
2001-12-20 14:52:54 +03:00
return 0 ;
2001-12-12 17:54:24 +03:00
2001-11-28 12:13:00 +03:00
_align_chunk ( c , align ) ;
}
2001-12-20 14:52:54 +03:00
return 1 ;
2001-11-28 12:13:00 +03:00
}
2001-12-20 14:52:54 +03:00
int pool_grow_object ( struct pool * p , const void * extra , size_t n )
2001-11-28 12:13:00 +03:00
{
2001-12-12 17:54:24 +03:00
struct chunk * c = p - > chunk , * nc ;
2001-11-28 12:13:00 +03:00
if ( c - > end - ( c - > begin + p - > object_len ) < n ) {
/* move into a new chunk */
if ( p - > object_len + n > ( p - > chunk_size / 2 ) )
2001-12-12 17:54:24 +03:00
nc = _new_chunk ( p , ( p - > object_len + n ) * 2 ) ;
2001-11-28 12:13:00 +03:00
else
2001-12-12 17:54:24 +03:00
nc = _new_chunk ( p , p - > chunk_size ) ;
if ( ! nc )
2001-12-20 14:52:54 +03:00
return 0 ;
2001-11-28 12:13:00 +03:00
_align_chunk ( p - > chunk , p - > object_alignment ) ;
memcpy ( p - > chunk - > begin , c - > begin , p - > object_len ) ;
c = p - > chunk ;
}
2001-12-20 14:52:54 +03:00
memcpy ( c - > begin + p - > object_len , extra , n ) ;
2001-11-28 12:13:00 +03:00
p - > object_len + = n ;
2001-12-20 14:52:54 +03:00
return 1 ;
2001-11-28 12:13:00 +03:00
}
void * pool_end_object ( struct pool * p )
{
struct chunk * c = p - > chunk ;
void * r = c - > begin ;
c - > begin + = p - > object_len ;
p - > object_len = 0u ;
p - > object_alignment = DEFAULT_ALIGNMENT ;
return r ;
}
void pool_abandon_object ( struct pool * p )
{
p - > object_len = 0 ;
p - > object_alignment = DEFAULT_ALIGNMENT ;
}
void _align_chunk ( struct chunk * c , unsigned alignment )
{
c - > begin + = alignment - ( ( unsigned long ) c - > begin & ( alignment - 1 ) ) ;
}
struct chunk * _new_chunk ( struct pool * p , size_t s )
{
struct chunk * c ;
if ( p - > spare_chunk & &
( ( p - > spare_chunk - > end - ( char * ) p - > spare_chunk ) > = s ) ) {
/* reuse old chunk */
c = p - > spare_chunk ;
p - > spare_chunk = 0 ;
} else {
2002-01-10 12:35:55 +03:00
if ( ! ( c = dbg_malloc ( s ) ) ) {
2003-04-02 23:11:23 +04:00
log_err ( " Out of memory. Requested % " PRIsize_t
" bytes. " , s ) ;
2001-12-12 17:54:24 +03:00
return NULL ;
2002-01-10 12:35:55 +03:00
}
2001-12-12 17:54:24 +03:00
2001-11-28 12:13:00 +03:00
c - > end = ( char * ) c + s ;
}
c - > prev = p - > chunk ;
c - > begin = ( char * ) ( c + 1 ) ;
p - > chunk = c ;
return c ;
}