heap.c

00001 /* cp_heap is a generic heap implementation based on libheap and contributed 
00002  * to cprops by Kyle Wheeler. The original copyright notice follows.
00003  *
00004  * This file (a generic C heap implementation) is licensed under the BSD
00005  * license, and is copyrighted to Kyle Wheeler and Branden Moore, 2003 and
00006  * 2004. Feel free to distribute and use as you see fit, as long as this
00007  * copyright notice stays with the code.
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 /* functions */
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         /* c points to the largest among the children of p */
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         /* resize heap */
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 /* no resize */
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 /* set mode bits */
00418 int cp_heap_set_mode(cp_heap *heap, int mode)
00419 {
00420     int rc;
00421     int nosync;
00422 
00423     /* can't set nosync in the middle of a transaction */
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     /* handle the special case of unsetting NOSYNC */
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 

Generated on Mon Dec 5 23:00:22 2011 for cprops by  doxygen 1.4.7