00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <stdio.h>
00012 #include <stdlib.h>
00013 #include "heap.h"
00014
00015 #define PARENT(x) ((x) >> 1)
00016 #define LEVELSIZE(x) (1 << (x))
00017 #define LEFTCHILD(x) ((x<<1))
00018
00019
00020 static int heap_resize(cp_heap *);
00021 static void siftup(cp_heap *h);
00022 static void siftdown(cp_heap *, unsigned int);
00023 static void swap(void **, void **);
00024
00025 unsigned int cp_heap_count(cp_heap *h)
00026 {
00027 return h->heap_end - 1;
00028 }
00029
00030 unsigned int cp_heap_size(cp_heap *h)
00031 {
00032 return h->heap_length;
00033 }
00034
00035 cp_heap *cp_heap_create_by_option(int mode,
00036 int initial_size,
00037 cp_compare_fn comp,
00038 cp_copy_fn copy,
00039 cp_destructor_fn dtr)
00040 {
00041 int rc;
00042 cp_heap *h = (cp_heap *) calloc(1, sizeof(cp_heap));
00043 if (h == NULL) return NULL;
00044
00045 if ((mode & COLLECTION_MODE_NOSYNC) == 0)
00046 {
00047 h->lock = malloc(sizeof(cp_mutex));
00048 if (h->lock == NULL)
00049 {
00050 free(h);
00051 return NULL;
00052 }
00053 if ((rc = cp_mutex_init(h->lock, NULL)))
00054 {
00055 free(h->lock);
00056 free(h);
00057 return NULL;
00058 }
00059 }
00060
00061 if (initial_size)
00062 {
00063 for (h->heap_length = 0;
00064 h->heap_length < initial_size;
00065 h->heap_height++)
00066 h->heap_length += LEVELSIZE(h->heap_height) + 1;
00067 if ((h->heap = malloc(h->heap_length * sizeof(void *))) == NULL)
00068 {
00069 cp_heap_destroy(h);
00070 return NULL;
00071 }
00072 }
00073 else
00074 {
00075 h->heap = NULL;
00076 h->heap_height = 0;
00077 h->heap_length = 0;
00078 }
00079
00080 h->heap_end = 1;
00081 h->comp = comp;
00082 h->copy = copy;
00083 h->dtr = dtr;
00084 h->mode = mode;
00085
00086 #ifdef __TRACE__
00087 DEBUGMSG("cp_heap_create_by_option\n");
00088 heap_info(h);
00089 #endif
00090 return h;
00091 }
00092
00093 cp_heap *cp_heap_create(cp_compare_fn cmp)
00094 {
00095 return cp_heap_create_by_option(0, 0, cmp, NULL, NULL);
00096 }
00097
00098 void cp_heap_destroy(cp_heap *h)
00099 {
00100 #ifdef __TRACE__
00101 DEBUGMSG("cp_heap_destroy\n");
00102 heap_info(h);
00103 #endif
00104 if (h)
00105 {
00106 if (h->heap)
00107 {
00108 if ((h->mode & COLLECTION_MODE_DEEP) && h->dtr)
00109 {
00110 int i;
00111 for (i = 1; i < h->heap_end; ++i)
00112 if (h->heap[i]) (*h->dtr)(h->heap[i]);
00113 }
00114 free(h->heap);
00115 }
00116
00117 if (h->lock)
00118 {
00119 cp_mutex_destroy(h->lock);
00120 free(h->lock);
00121 }
00122
00123 free(h);
00124 }
00125 }
00126
00127 int cp_heap_push(cp_heap *h, void *in)
00128 {
00129 int rc = 0;
00130 unsigned int he;
00131
00132 if ((rc = cp_heap_txlock(h))) return rc;
00133
00134 he = h->heap_end;
00135
00136 #ifdef _HEAP_TRACE
00137 DEBUGMSG("cp_heap_push: %p\n", in);
00138 heap_info(h);
00139 #endif
00140 if ((rc = heap_resize(h))) goto DONE;
00141 if ((h->mode & COLLECTION_MODE_COPY) && h->copy)
00142 h->heap[he] = (*h->copy)(in);
00143 else
00144 h->heap[he] = in;
00145 siftup(h);
00146 #ifdef _HEAP_TRACE
00147 DEBUGMSG("cp_heap_push end\n");
00148 heap_info(h);
00149 #endif
00150 DONE:
00151 cp_heap_txunlock(h);
00152 return rc;
00153 }
00154
00155 void *cp_heap_peek(cp_heap *h)
00156 {
00157 void *item = NULL;
00158 if (h == NULL) return NULL;
00159 errno = 0;
00160 if (cp_heap_txlock(h)) return NULL;
00161 if (h->heap && h->heap_end > 1) item = (void *)(h->heap[1]);
00162 cp_heap_txunlock(h);
00163 return item;
00164 }
00165
00166 void *cp_heap_pop(cp_heap *h)
00167 {
00168 int rc;
00169 void *retval = NULL;
00170
00171 if (h == NULL) return NULL;
00172
00173 if ((rc = cp_heap_txlock(h))) return NULL;
00174
00175 if (h->heap != NULL && h->heap_end > 1)
00176 {
00177 retval = h->heap[1];
00178 #ifdef _HEAP_TRACE
00179 DEBUGMSG("cp_heap_pop: %p\n", retval);
00180 #endif
00181 if ((h->mode & COLLECTION_MODE_DEEP) && h->dtr)
00182 (*h->dtr)(retval);
00183
00184 h->heap[1] = h->heap[h->heap_end - 1];
00185 h->heap_end--;
00186 siftdown(h, 1);
00187 }
00188
00189 cp_heap_txunlock(h);
00190
00191 return retval;
00192 }
00193
00194 void cp_heap_callback(cp_heap *h, cp_callback_fn cb, void *prm)
00195 {
00196 int rc;
00197 unsigned int i;
00198
00199 if (h == NULL) return;
00200
00201 if ((rc = cp_heap_txlock(h))) return;
00202
00203 if (!h->heap || h->heap_end <= 1) goto DONE;
00204
00205 for (i = 1; i < h->heap_end; i++)
00206 (*cb)(h->heap[i], prm);
00207
00208 DONE:
00209 cp_heap_txunlock(h);
00210 }
00211
00212 static void siftdown(cp_heap *h, unsigned int p)
00213 {
00214 unsigned int c;
00215
00216 #ifdef _HEAP_TRACE
00217 DEBUGMSG("siftdown start\n");
00218 heap_info(h);
00219 fflush(stdout);
00220 #endif
00221 for (c = LEFTCHILD(p); c < h->heap_end; p = c, c = LEFTCHILD(p))
00222 {
00223 #ifdef _HEAP_TRACE
00224 DEBUGMSG(" c = %i, h->heap_end = %i\n", c, h->heap_end);
00225 #endif
00226 if (h->comp(h->heap[c], h->heap[c + 1]) <= 0)
00227 c++;
00228
00229
00230 if (h->comp(h->heap[p], h->heap[c]) <= 0)
00231 swap(&(h->heap[p]), &(h->heap[c]));
00232 else
00233 return;
00234 }
00235 if (c == h->heap_end && h->comp(h->heap[p], h->heap[c]) <= 0)
00236 swap(&(h->heap[p]), &(h->heap[c]));
00237 }
00238
00239 static int heap_resize(cp_heap *h)
00240 {
00241 if (h->heap_end + 1 >= h->heap_length)
00242 {
00243
00244 void **temp;
00245 unsigned int new_length =
00246 h->heap_length + LEVELSIZE(h->heap_height) + 1;
00247
00248 #ifdef __TRACE__
00249 DEBUGMSG("heap [%p] resize %d -> %d", h, h->heap_length, new_length);
00250 #endif
00251
00252 temp = realloc(h->heap, new_length * sizeof(void *));
00253 if (temp == NULL)
00254 {
00255 cp_error(CP_MEMORY_ALLOCATION_FAILURE, "can\'t resize heap");
00256 return -1;
00257 }
00258
00259 h->heap = temp;
00260 h->heap_height++;
00261 h->heap_length = new_length;
00262 h->heap_end++;
00263 }
00264 else
00265 h->heap_end++;
00266
00267 return 0;
00268 }
00269
00270 int cp_heap_contract(cp_heap *h)
00271 {
00272 int rc;
00273 unsigned int new_length;
00274 unsigned int tmp_length;
00275 unsigned int new_height;
00276
00277 if ((rc = cp_heap_txlock(h))) return rc;
00278
00279 tmp_length = h->heap_length;
00280 new_height = h->heap_height;
00281
00282 while (1)
00283 {
00284 new_length = tmp_length;
00285 tmp_length -= LEVELSIZE(new_height - 1) + 1;
00286 if (tmp_length < h->heap_end) break;
00287 new_height--;
00288 }
00289
00290 if (new_length != h->heap_length)
00291 {
00292 void **new_heap;
00293 #ifdef __TRACE__
00294 DEBUGMSG("heap [%p] contracting %d to %d",
00295 h, h->heap_length, new_length);
00296 #endif
00297 new_heap = realloc(h->heap, new_length * sizeof(void *));
00298 if (new_heap == NULL)
00299 {
00300 rc = -1;
00301 goto DONE;
00302 }
00303 h->heap = new_heap;
00304 h->heap_length = new_length;
00305 h->heap_height = new_height;
00306 }
00307
00308 DONE:
00309 cp_heap_txunlock(h);
00310 return rc;
00311 }
00312
00313 static void swap(void **a, void **b)
00314 {
00315 void *c = *a;
00316
00317 *a = *b;
00318 *b = c;
00319 }
00320
00321 static void siftup(cp_heap *h)
00322 {
00323 unsigned int c = h->heap_end - 1;
00324
00325 while (c != 1 && h->comp(h->heap[PARENT(c)], h->heap[c]) <= 0)
00326 {
00327 #ifdef _HEAP_TRACE
00328 DEBUGMSG("siftup\n");
00329 heap_info(h);
00330 fflush(stdout);
00331 #endif
00332 swap(&(h->heap[PARENT(c)]), &(h->heap[c]));
00333 c = PARENT(c);
00334 }
00335 }
00336
00337 #if defined(DEBUG) || defined(_HEAP_TRACE) || defined (__TRACE__)
00338 void heap_info(cp_heap *h)
00339 {
00340 DEBUGMSG(" heap->heap: %p\n", h->heap);
00341 DEBUGMSG(" ->heap_height: %i\n", h->heap_height);
00342 DEBUGMSG(" ->heap_end: %i\n", h->heap_end);
00343 DEBUGMSG(" ->heap_length: %i\n", h->heap_length);
00344 DEBUGMSG(" ->comp: %p\n", h->comp);
00345 }
00346 #endif
00347
00348 #ifdef DEBUG
00349 int cp_heap_verify(cp_heap *h)
00350 {
00351 unsigned int i;
00352
00353 if (!h || !h->heap || h->heap_end <= 1) return 1;
00354
00355 for (i = h->heap_end - 1; i > PARENT(h->heap_end - 1); --i)
00356 {
00357 unsigned int c = i;
00358
00359 while (c != 1 && !(h->comp(h->heap[PARENT(c)], h->heap[c])))
00360 c = PARENT(c);
00361
00362 if (c != 1) return 0;
00363 }
00364 return 1;
00365 }
00366 #endif
00367
00368 int cp_heap_txlock(cp_heap *heap)
00369 {
00370 if (heap->mode & COLLECTION_MODE_NOSYNC) return 0;
00371 if (heap->mode & COLLECTION_MODE_IN_TRANSACTION)
00372 {
00373 cp_thread self = cp_thread_self();
00374 if (cp_thread_equal(self, heap->txowner)) return 0;
00375 }
00376 errno = 0;
00377 return cp_mutex_lock(heap->lock);
00378 }
00379
00380 int cp_heap_txunlock(cp_heap *heap)
00381 {
00382 if (heap->mode & COLLECTION_MODE_NOSYNC) return 0;
00383 if (heap->mode & COLLECTION_MODE_IN_TRANSACTION)
00384 {
00385 cp_thread self = cp_thread_self();
00386 if (cp_thread_equal(self, heap->txowner)) return 0;
00387 }
00388 return cp_mutex_unlock(heap->lock);
00389 }
00390
00391 int cp_heap_lock(cp_heap *heap)
00392 {
00393 int rc;
00394 if ((heap->mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00395 if ((rc = cp_mutex_lock(heap->lock))) return rc;
00396 heap->txowner = cp_thread_self();
00397 heap->mode |= COLLECTION_MODE_IN_TRANSACTION;
00398 return 0;
00399 }
00400
00401 int cp_heap_unlock(cp_heap *heap)
00402 {
00403 cp_thread self = cp_thread_self();
00404 if (heap->txowner == self)
00405 {
00406 heap->txowner = 0;
00407 heap->mode ^= COLLECTION_MODE_IN_TRANSACTION;
00408 }
00409 return cp_mutex_unlock(heap->lock);
00410 }
00411
00412 int cp_heap_get_mode(cp_heap *heap)
00413 {
00414 return heap->mode;
00415 }
00416
00417
00418 int cp_heap_set_mode(cp_heap *heap, int mode)
00419 {
00420 int rc;
00421 int nosync;
00422
00423
00424 if ((heap->mode & COLLECTION_MODE_IN_TRANSACTION) &&
00425 (mode & COLLECTION_MODE_NOSYNC)) return EINVAL;
00426
00427 if ((rc = cp_heap_txlock(heap))) return rc;
00428
00429 nosync = heap->mode & COLLECTION_MODE_NOSYNC;
00430
00431 heap->mode |= mode;
00432
00433 if (!nosync)
00434 cp_heap_txunlock(heap);
00435
00436 return 0;
00437 }
00438
00439 int cp_heap_unset_mode(cp_heap *heap, int mode)
00440 {
00441 int rc;
00442 int nosync;
00443
00444 if ((rc = cp_heap_txlock(heap))) return rc;
00445
00446 nosync = heap->mode & COLLECTION_MODE_NOSYNC;
00447
00448
00449 if ((mode & COLLECTION_MODE_NOSYNC) && heap->lock == NULL)
00450 {
00451 if ((heap->lock = malloc(sizeof(cp_mutex))) == NULL)
00452 return -1;
00453 if ((rc = cp_mutex_init(heap->lock, NULL)))
00454 {
00455 free(heap->lock);
00456 heap->lock = NULL;
00457 return -1;
00458 }
00459 }
00460
00461 heap->mode &= heap->mode ^ mode;
00462 if (!nosync)
00463 cp_heap_txunlock(heap);
00464
00465 return 0;
00466 }
00467