1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3 * Copyright (c) 1999-2004 Carnegie Mellon University. All rights
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * This work was supported in part by funding from the Defense Advanced
19 * Research Projects Agency and the National Science Foundation of the
20 * United States of America, and the CMU Sphinx Speech Consortium.
22 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * ====================================================================
41 #include "sphinxbase/err.h"
42 #include "sphinxbase/ckd_alloc.h"
43 #include "sphinxbase/listelem_alloc.h"
44 #include "sphinxbase/glist.h"
47 * Fast linked list allocator.
49 * We keep a separate linked list for each element-size. Element-size
50 * must be a multiple of pointer-size.
52 * Initially a block of empty elements is allocated, where the first
53 * machine word in each element points to the next available element.
54 * To allocate, we use this pointer to move the freelist to the next
55 * element, then return the current element.
57 * The last element in the list starts with a NULL pointer, which is
58 * used as a signal to allocate a new block of elements.
60 * In order to be able to actually release the memory allocated, we
61 * have to add a linked list of block pointers. This shouldn't create
62 * much overhead since we never access it except when freeing the
65 struct listelem_alloc_s {
66 char **freelist; /**< ptr to first element in freelist */
67 glist_t blocks; /**< Linked list of blocks allocated. */
68 glist_t blocksize; /**< Number of elements in each block */
69 size_t elemsize; /**< Number of (char *) in element */
70 size_t blk_alloc; /**< Number of alloc operations before increasing blocksize */
76 #define MIN_ALLOC 50 /**< Minimum number of elements to allocate in one block */
77 #define BLKID_SHIFT 16 /**< Bit position of block number in element ID */
78 #define BLKID_MASK ((1<<BLKID_SHIFT)-1)
81 * Allocate a new block of elements.
83 static void listelem_add_block(listelem_alloc_t *list,
84 char *caller_file, int caller_line);
87 listelem_alloc_init(size_t elemsize)
89 listelem_alloc_t *list;
91 if ((elemsize % sizeof(void *)) != 0) {
92 size_t rounded = (elemsize + sizeof(void *) - 1) & ~(sizeof(void *)-1);
94 ("List item size (%lu) not multiple of sizeof(void *), rounding to %lu\n",
95 (unsigned long)elemsize,
96 (unsigned long)rounded);
99 list = ckd_calloc(1, sizeof(*list));
100 list->freelist = NULL;
102 list->elemsize = elemsize;
103 /* Intent of this is to increase block size once we allocate
104 * 256KiB (i.e. 1<<18). If somehow the element size is big enough
105 * to overflow that, just fail, people should use malloc anyway. */
106 list->blk_alloc = (1 << 18) / (MIN_ALLOC * elemsize);
107 if (list->blk_alloc <= 0) {
108 E_ERROR("Element size * block size exceeds 256k, use malloc instead.\n");
115 /* Allocate an initial block to minimize latency. */
116 listelem_add_block(list, __FILE__, __LINE__);
121 listelem_alloc_free(listelem_alloc_t *list)
126 for (gn = list->blocks; gn; gn = gnode_next(gn))
127 ckd_free(gnode_ptr(gn));
128 glist_free(list->blocks);
129 glist_free(list->blocksize);
134 listelem_add_block(listelem_alloc_t *list, char *caller_file, int caller_line)
140 blocksize = list->blocksize ? gnode_int32(list->blocksize) : MIN_ALLOC;
141 /* Check if block size should be increased (if many requests for this size) */
142 if (list->blk_alloc == 0) {
143 /* See above. No sense in allocating blocks bigger than
144 * 256KiB (well, actually, there might be, but we'll worry
145 * about that later). */
147 if (blocksize * list->elemsize > (1 << 18))
148 blocksize = (1 << 18) / list->elemsize;
149 list->blk_alloc = (1 << 18) / (blocksize * list->elemsize);
153 cpp = list->freelist =
154 (char **) __ckd_calloc__(blocksize, list->elemsize,
155 caller_file, caller_line);
156 list->blocks = glist_add_ptr(list->blocks, cpp);
157 list->blocksize = glist_add_int32(list->blocksize, blocksize);
159 /* Link up the blocks via their first machine word. */
160 for (j = blocksize - 1; j > 0; --j) {
161 cp += list->elemsize;
165 /* Make sure the last element's forward pointer is NULL */
173 __listelem_malloc__(listelem_alloc_t *list, char *caller_file, int caller_line)
177 /* Allocate a new block if list empty */
178 if (list->freelist == NULL)
179 listelem_add_block(list, caller_file, caller_line);
181 /* Unlink and return first element in freelist */
182 ptr = list->freelist;
183 list->freelist = (char **) (*(list->freelist));
190 __listelem_malloc_id__(listelem_alloc_t *list, char *caller_file,
191 int caller_line, int32 *out_id)
195 /* Allocate a new block if list empty */
196 if (list->freelist == NULL)
197 listelem_add_block(list, caller_file, caller_line);
199 /* Unlink and return first element in freelist */
200 ptr = list->freelist;
201 list->freelist = (char **) (*(list->freelist));
205 int32 blksize, blkidx, ptridx;
209 gn2 = list->blocksize;
212 for (gn = list->blocks; gn; gn = gnode_next(gn)) {
213 block = gnode_ptr(gn);
214 blksize = gnode_int32(gn2) * list->elemsize / sizeof(*block);
215 if (ptr >= block && ptr < block + blksize)
217 gn2 = gnode_next(gn2);
221 E_ERROR("Failed to find block index for pointer %p!\n", ptr);
223 ptridx = (ptr - block) / (list->elemsize / sizeof(*block));
224 E_DEBUG(4,("ptr %p block %p blkidx %d ptridx %d\n",
225 ptr, block, list->n_blocks - blkidx - 1, ptridx));
226 *out_id = ((list->n_blocks - blkidx - 1) << BLKID_SHIFT) | ptridx;
233 listelem_get_item(listelem_alloc_t *list, int32 id)
235 int32 blkidx, ptridx, i;
238 blkidx = (id >> BLKID_SHIFT) & BLKID_MASK;
239 ptridx = id & BLKID_MASK;
242 blkidx = list->n_blocks - blkidx;
243 for (gn = list->blocks; gn; gn = gnode_next(gn)) {
248 E_ERROR("Failed to find block index %d\n", blkidx);
252 return (void *)((char **)gnode_ptr(gn)
253 + ptridx * (list->elemsize / sizeof(void *)));
257 __listelem_free__(listelem_alloc_t *list, void *elem,
258 char *caller_file, int caller_line)
263 * Insert freed item at head of list.
265 cpp = (char **) elem;
266 *cpp = (char *) list->freelist;
267 list->freelist = cpp;
273 listelem_stats(listelem_alloc_t *list)
279 E_INFO("Linklist stats:\n");
280 for (n = 0, cpp = list->freelist; cpp;
281 cpp = (char **) (*cpp), n++);
283 ("elemsize %lu, #alloc %lu, #freed %lu, #freelist %lu\n",
284 (unsigned long)list->elemsize,
285 (unsigned long)list->n_alloc,
286 (unsigned long)list->n_freed,
288 E_INFO("Allocated blocks:\n");
289 gn2 = list->blocksize;
290 for (gn = list->blocks; gn; gn = gnode_next(gn)) {
291 E_INFO("%p (%d * %d bytes)\n", gnode_ptr(gn), gnode_int32(gn2), list->elemsize);
292 gn2 = gnode_next(gn2);