listelem_alloc.c
/* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* ====================================================================
* Copyright (c) 1999-2004 Carnegie Mellon University. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* This work was supported in part by funding from the Defense Advanced
* Research Projects Agency and the National Science Foundation of the
* United States of America, and the CMU Sphinx Speech Consortium.
*
* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
* NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
*
*/
#include <stdio.h>
#include <stdlib.h>
#include "sphinxbase/err.h"
#include "sphinxbase/ckd_alloc.h"
#include "sphinxbase/listelem_alloc.h"
#include "sphinxbase/glist.h"
struct listelem_alloc_s {
char **freelist;
glist_t blocks;
glist_t blocksize;
size_t elemsize;
size_t blk_alloc;
size_t n_blocks;
size_t n_alloc;
size_t n_freed;
};
#define MIN_ALLOC 50
#define BLKID_SHIFT 16
#define BLKID_MASK ((1<<BLKID_SHIFT)-1)
static void listelem_add_block(listelem_alloc_t *list,
char *caller_file, int caller_line);
listelem_alloc_t *
listelem_alloc_init(size_t elemsize)
{
listelem_alloc_t *list;
if ((elemsize % sizeof(void *)) != 0) {
size_t rounded = (elemsize + sizeof(void *) - 1) & ~(sizeof(void *)-1);
E_WARN
("List item size (%lu) not multiple of sizeof(void *), rounding to %lu\n",
(unsigned long)elemsize,
(unsigned long)rounded);
elemsize = rounded;
}
list = ckd_calloc(1, sizeof(*list));
list->freelist = NULL;
list->blocks = NULL;
list->elemsize = elemsize;
/* Intent of this is to increase block size once we allocate
* 256KiB (i.e. 1<<18). If somehow the element size is big enough
* to overflow that, just fail, people should use malloc anyway. */
list->blk_alloc = (1 << 18) / (MIN_ALLOC * elemsize);
if (list->blk_alloc <= 0) {
E_ERROR("Element size * block size exceeds 256k, use malloc instead.\n");
ckd_free(list);
return NULL;
}
list->n_alloc = 0;
list->n_freed = 0;
/* Allocate an initial block to minimize latency. */
listelem_add_block(list, __FILE__, __LINE__);
return list;
}
void
listelem_alloc_free(listelem_alloc_t *list)
{
gnode_t *gn;
if (list == NULL)
return;
for (gn = list->blocks; gn; gn = gnode_next(gn))
ckd_free(gnode_ptr(gn));
glist_free(list->blocks);
glist_free(list->blocksize);
ckd_free(list);
}
static void
listelem_add_block(listelem_alloc_t *list, char *caller_file, int caller_line)
{
char **cpp, *cp;
size_t j;
int32 blocksize;
blocksize = list->blocksize ? gnode_int32(list->blocksize) : MIN_ALLOC;
/* Check if block size should be increased (if many requests for this size) */
if (list->blk_alloc == 0) {
/* See above. No sense in allocating blocks bigger than
* 256KiB (well, actually, there might be, but we'll worry
* about that later). */
blocksize <<= 1;
if (blocksize * list->elemsize > (1 << 18))
blocksize = (1 << 18) / list->elemsize;
list->blk_alloc = (1 << 18) / (blocksize * list->elemsize);
}
/* Allocate block */
cpp = list->freelist =
(char **) __ckd_calloc__(blocksize, list->elemsize,
caller_file, caller_line);
list->blocks = glist_add_ptr(list->blocks, cpp);
list->blocksize = glist_add_int32(list->blocksize, blocksize);
cp = (char *) cpp;
/* Link up the blocks via their first machine word. */
for (j = blocksize - 1; j > 0; --j) {
cp += list->elemsize;
*cpp = cp;
cpp = (char **) cp;
}
/* Make sure the last element's forward pointer is NULL */
*cpp = NULL;
--list->blk_alloc;
++list->n_blocks;
}
void *
__listelem_malloc__(listelem_alloc_t *list, char *caller_file, int caller_line)
{
char **ptr;
/* Allocate a new block if list empty */
if (list->freelist == NULL)
listelem_add_block(list, caller_file, caller_line);
/* Unlink and return first element in freelist */
ptr = list->freelist;
list->freelist = (char **) (*(list->freelist));
(list->n_alloc)++;
return (void *)ptr;
}
void *
__listelem_malloc_id__(listelem_alloc_t *list, char *caller_file,
int caller_line, int32 *out_id)
{
char **ptr;
/* Allocate a new block if list empty */
if (list->freelist == NULL)
listelem_add_block(list, caller_file, caller_line);
/* Unlink and return first element in freelist */
ptr = list->freelist;
list->freelist = (char **) (*(list->freelist));
(list->n_alloc)++;
if (out_id) {
int32 blksize, blkidx, ptridx;
gnode_t *gn, *gn2;
char **block;
gn2 = list->blocksize;
block = NULL;
blkidx = 0;
for (gn = list->blocks; gn; gn = gnode_next(gn)) {
block = gnode_ptr(gn);
blksize = gnode_int32(gn2) * list->elemsize / sizeof(*block);
if (ptr >= block && ptr < block + blksize)
break;
gn2 = gnode_next(gn2);
++blkidx;
}
if (gn == NULL) {
E_ERROR("Failed to find block index for pointer %p!\n", ptr);
}
ptridx = (ptr - block) / (list->elemsize / sizeof(*block));
E_DEBUG(4,("ptr %p block %p blkidx %d ptridx %d\n",
ptr, block, list->n_blocks - blkidx - 1, ptridx));
*out_id = ((list->n_blocks - blkidx - 1) << BLKID_SHIFT) | ptridx;
}
return ptr;
}
void *
listelem_get_item(listelem_alloc_t *list, int32 id)
{
int32 blkidx, ptridx, i;
gnode_t *gn;
blkidx = (id >> BLKID_SHIFT) & BLKID_MASK;
ptridx = id & BLKID_MASK;
i = 0;
blkidx = list->n_blocks - blkidx;
for (gn = list->blocks; gn; gn = gnode_next(gn)) {
if (++i == blkidx)
break;
}
if (gn == NULL) {
E_ERROR("Failed to find block index %d\n", blkidx);
return NULL;
}
return (void *)((char **)gnode_ptr(gn)
+ ptridx * (list->elemsize / sizeof(void *)));
}
void
__listelem_free__(listelem_alloc_t *list, void *elem,
char *caller_file, int caller_line)
{
char **cpp;
/*
* Insert freed item at head of list.
*/
cpp = (char **) elem;
*cpp = (char *) list->freelist;
list->freelist = cpp;
(list->n_freed)++;
}
void
listelem_stats(listelem_alloc_t *list)
{
gnode_t *gn, *gn2;
char **cpp;
size_t n;
E_INFO("Linklist stats:\n");
for (n = 0, cpp = list->freelist; cpp;
cpp = (char **) (*cpp), n++);
E_INFO
("elemsize %lu, #alloc %lu, #freed %lu, #freelist %lu\n",
(unsigned long)list->elemsize,
(unsigned long)list->n_alloc,
(unsigned long)list->n_freed,
(unsigned long)n);
E_INFO("Allocated blocks:\n");
gn2 = list->blocksize;
for (gn = list->blocks; gn; gn = gnode_next(gn)) {
E_INFO("%p (%d * %d bytes)\n", gnode_ptr(gn), gnode_int32(gn2), list->elemsize);
gn2 = gnode_next(gn2);
}
}
ckd_calloc
#define ckd_calloc(n, sz)
Macros to simplify the use of above functions.
Definition: ckd_alloc.h:248
glist_add_int32
SPHINXBASE_EXPORT glist_t glist_add_int32(glist_t g, int32 val)
Create and prepend a new list node containing an integer.
Definition: glist.c:86
E_DEBUG
#define E_DEBUG(level, x)
Print debugging information to standard error stream.
Definition: err.h:212
E_INFO
#define E_INFO
Print logging information to standard error stream.
Definition: err.h:147
ckd_alloc.h
Sphinx's memory allocation/deallocation routines.
gnode_s
A node in a generic list.
Definition: glist.h:100
ckd_free
SPHINXBASE_EXPORT void ckd_free(void *ptr)
Test and free a 1-D array.
Definition: ckd_alloc.c:241
glist_add_ptr
SPHINXBASE_EXPORT glist_t glist_add_ptr(glist_t g, void *ptr)
Create and prepend a new list node, with the given user-defined data, at the HEAD of the given generi...
Definition: glist.c:74
E_WARN
#define E_WARN
Print warning information to standard error stream.
Definition: err.h:164
listelem_alloc_s::blocks
glist_t blocks
Linked list of blocks allocated.
Definition: listelem_alloc.c:67
listelem_alloc_s
Fast linked list allocator.
Definition: listelem_alloc.c:65
glist_free
SPHINXBASE_EXPORT void glist_free(glist_t g)
Free the given generic list; user-defined data contained within is not automatically freed...
Definition: glist.c:133
listelem_get_item
SPHINXBASE_EXPORT void * listelem_get_item(listelem_alloc_t *le, int32 id)
Retrieve a list element by its identifier.
Definition: listelem_alloc.c:233
gnode_ptr
#define gnode_ptr(g)
Head of a list of gnodes.
Definition: glist.h:109
listelem_alloc.h
Fast memory allocator for uniformly sized objects.
err.h
Implementation of logging routines.
listelem_alloc_s::freelist
char ** freelist
ptr to first element in freelist
Definition: listelem_alloc.c:66
glist.h
Generic linked-lists maintenance.
E_ERROR
#define E_ERROR
Print error message to standard error stream.
Definition: err.h:169
listelem_alloc_s::blk_alloc
size_t blk_alloc
Number of alloc operations before increasing blocksize.
Definition: listelem_alloc.c:70
listelem_alloc_init
SPHINXBASE_EXPORT listelem_alloc_t * listelem_alloc_init(size_t elemsize)
Initialize and return a list element allocator.
Definition: listelem_alloc.c:87
listelem_alloc_s::blocksize
glist_t blocksize
Number of elements in each block.
Definition: listelem_alloc.c:68
listelem_alloc_s::elemsize
size_t elemsize
Number of (char *) in element.
Definition: listelem_alloc.c:69
listelem_stats
SPHINXBASE_EXPORT void listelem_stats(listelem_alloc_t *le)
Print number of allocation, numer of free operation stats.
Definition: listelem_alloc.c:273
__listelem_free__
SPHINXBASE_EXPORT void __listelem_free__(listelem_alloc_t *le, void *elem, char *file, int line)
Free list element of given size.
Definition: listelem_alloc.c:257
listelem_alloc_free
SPHINXBASE_EXPORT void listelem_alloc_free(listelem_alloc_t *le)
Finalize and release all memory associated with a list element allocator.
Definition: listelem_alloc.c:121