1 /**
2 * collectd - src/utils_heap.c
3 * Copyright (C) 2009 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Florian octo Forster <octo at verplant.org>
21 **/
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <pthread.h>
29 #include "utils_heap.h"
31 struct c_heap_s
32 {
33 pthread_mutex_t lock;
34 int (*compare) (const void *, const void *);
36 void **list;
37 size_t list_len; /* # entries used */
38 size_t list_size; /* # entries allocated */
39 };
41 enum reheap_direction
42 {
43 DIR_UP,
44 DIR_DOWN
45 };
47 static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
48 {
49 size_t left;
50 size_t right;
51 size_t min;
52 int status;
54 /* Calculate the positions of the children */
55 left = (2 * root) + 1;
56 if (left >= h->list_len)
57 left = 0;
59 right = (2 * root) + 2;
60 if (right >= h->list_len)
61 right = 0;
63 /* Check which one of the children is smaller. */
64 if ((left == 0) && (right == 0))
65 return;
66 else if (left == 0)
67 min = right;
68 else if (right == 0)
69 min = left;
70 else
71 {
72 status = h->compare (h->list[left], h->list[right]);
73 if (status > 0)
74 min = right;
75 else
76 min = left;
77 }
79 status = h->compare (h->list[root], h->list[min]);
80 if (status <= 0)
81 {
82 /* We didn't need to change anything, so the rest of the tree should be
83 * okay now. */
84 return;
85 }
86 else /* if (status > 0) */
87 {
88 void *tmp;
90 tmp = h->list[root];
91 h->list[root] = h->list[min];
92 h->list[min] = tmp;
93 }
95 if ((dir == DIR_UP) && (root == 0))
96 return;
98 if (dir == DIR_UP)
99 reheap (h, (root - 1) / 2, dir);
100 else if (dir == DIR_DOWN)
101 reheap (h, min, dir);
102 } /* void reheap */
104 c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
105 {
106 c_heap_t *h;
108 if (compare == NULL)
109 return (NULL);
111 h = malloc (sizeof (*h));
112 if (h == NULL)
113 return (NULL);
115 memset (h, 0, sizeof (*h));
116 pthread_mutex_init (&h->lock, /* attr = */ NULL);
117 h->compare = compare;
119 h->list = NULL;
120 h->list_len = 0;
121 h->list_size = 0;
123 return (h);
124 } /* c_heap_t *c_heap_create */
126 void c_heap_destroy (c_heap_t *h)
127 {
128 if (h == NULL)
129 return;
131 h->list_len = 0;
132 h->list_size = 0;
133 free (h->list);
134 h->list = NULL;
136 pthread_mutex_destroy (&h->lock);
138 free (h);
139 } /* void c_heap_destroy */
141 int c_heap_insert (c_heap_t *h, void *ptr)
142 {
143 size_t index;
145 if ((h == NULL) || (ptr == NULL))
146 return (-EINVAL);
148 pthread_mutex_lock (&h->lock);
150 assert (h->list_len <= h->list_size);
151 if (h->list_len == h->list_size)
152 {
153 void **tmp;
155 tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
156 if (tmp == NULL)
157 {
158 pthread_mutex_unlock (&h->lock);
159 return (-ENOMEM);
160 }
162 h->list = tmp;
163 h->list_size += 16;
164 }
166 /* Insert the new node as a leaf. */
167 index = h->list_len;
168 h->list[index] = ptr;
169 h->list_len++;
171 /* Reorganize the heap from bottom up. */
172 reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
174 pthread_mutex_unlock (&h->lock);
175 return (0);
176 } /* int c_heap_insert */
178 void *c_heap_get_root (c_heap_t *h)
179 {
180 void *ret = NULL;
182 if (h == NULL)
183 return (NULL);
185 pthread_mutex_lock (&h->lock);
187 if (h->list_len == 0)
188 {
189 pthread_mutex_unlock (&h->lock);
190 return (NULL);
191 }
192 else if (h->list_len == 1)
193 {
194 ret = h->list[0];
195 h->list[0] = NULL;
196 h->list_len = 0;
197 }
198 else /* if (h->list_len > 1) */
199 {
200 ret = h->list[0];
201 h->list[0] = h->list[h->list_len - 1];
202 h->list[h->list_len - 1] = NULL;
203 h->list_len--;
205 reheap (h, /* root = */ 0, DIR_DOWN);
206 }
208 /* free some memory */
209 if ((h->list_len + 32) < h->list_size)
210 {
211 void **tmp;
213 tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
214 if (tmp != NULL)
215 {
216 h->list = tmp;
217 h->list_size = h->list_len + 16;
218 }
219 }
221 pthread_mutex_unlock (&h->lock);
223 return (ret);
224 } /* void *c_heap_get_root */
226 /* vim: set sw=2 sts=2 et fdm=marker : */