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 / 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 if ((h == NULL) || (ptr == NULL))
144 return (-EINVAL);
146 pthread_mutex_lock (&h->lock);
148 assert (h->list_len <= h->list_size);
149 if (h->list_len == h->list_size)
150 {
151 void **tmp;
153 tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
154 if (tmp == NULL)
155 {
156 pthread_mutex_unlock (&h->lock);
157 return (-ENOMEM);
158 }
160 h->list = tmp;
161 h->list_size += 16;
162 }
164 /* Insert the new node as a leaf. */
165 h->list[h->list_len] = ptr;
166 h->list_len++;
168 /* Reorganize the heap from bottom up. */
169 reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP);
171 pthread_mutex_unlock (&h->lock);
172 return (0);
173 } /* int c_heap_insert */
175 void *c_heap_get_root (c_heap_t *h)
176 {
177 void *ret = NULL;
179 if (h == NULL)
180 return (NULL);
182 pthread_mutex_lock (&h->lock);
184 if (h->list_len == 0)
185 {
186 pthread_mutex_unlock (&h->lock);
187 return (NULL);
188 }
189 else if (h->list_len == 1)
190 {
191 ret = h->list[0];
192 h->list[0] = NULL;
193 h->list_len = 0;
194 }
195 else /* if (h->list_len > 1) */
196 {
197 ret = h->list[0];
198 h->list[0] = h->list[h->list_len - 1];
199 h->list[h->list_len - 1] = NULL;
200 h->list_len--;
202 reheap (h, /* root = */ 0, DIR_DOWN);
203 }
205 /* free some memory */
206 if ((h->list_len + 32) < h->list_size)
207 {
208 void **tmp;
210 tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
211 if (tmp != NULL)
212 {
213 h->list = tmp;
214 h->list_size = h->list_len + 16;
215 }
216 }
218 pthread_mutex_unlock (&h->lock);
220 return (ret);
221 } /* void *c_heap_get_root */
223 /* vim: set sw=2 sts=2 et fdm=marker : */