Merge branch 'collectd-5.1'
[collectd.git] / src / utils_heap.c
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  **/
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <pthread.h>
28
29 #include "utils_heap.h"
30
31 struct c_heap_s
32 {
33   pthread_mutex_t lock;
34   int (*compare) (const void *, const void *);
35
36   void **list;
37   size_t list_len; /* # entries used */
38   size_t list_size; /* # entries allocated */
39 };
40
41 enum reheap_direction
42 {
43   DIR_UP,
44   DIR_DOWN
45 };
46
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;
53
54   /* Calculate the positions of the children */
55   left = (2 * root) + 1;
56   if (left >= h->list_len)
57     left = 0;
58
59   right = (2 * root) + 2;
60   if (right >= h->list_len)
61     right = 0;
62
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   }
78
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;
89
90     tmp = h->list[root];
91     h->list[root] = h->list[min];
92     h->list[min] = tmp;
93   }
94
95   if ((dir == DIR_UP) && (root == 0))
96     return;
97
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 */
103
104 c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
105 {
106   c_heap_t *h;
107
108   if (compare == NULL)
109     return (NULL);
110
111   h = malloc (sizeof (*h));
112   if (h == NULL)
113     return (NULL);
114
115   memset (h, 0, sizeof (*h));
116   pthread_mutex_init (&h->lock, /* attr = */ NULL);
117   h->compare = compare;
118   
119   h->list = NULL;
120   h->list_len = 0;
121   h->list_size = 0;
122
123   return (h);
124 } /* c_heap_t *c_heap_create */
125
126 void c_heap_destroy (c_heap_t *h)
127 {
128   if (h == NULL)
129     return;
130
131   h->list_len = 0;
132   h->list_size = 0;
133   free (h->list);
134   h->list = NULL;
135
136   pthread_mutex_destroy (&h->lock);
137
138   free (h);
139 } /* void c_heap_destroy */
140
141 int c_heap_insert (c_heap_t *h, void *ptr)
142 {
143   size_t index;
144
145   if ((h == NULL) || (ptr == NULL))
146     return (-EINVAL);
147
148   pthread_mutex_lock (&h->lock);
149
150   assert (h->list_len <= h->list_size);
151   if (h->list_len == h->list_size)
152   {
153     void **tmp;
154
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     }
161
162     h->list = tmp;
163     h->list_size += 16;
164   }
165
166   /* Insert the new node as a leaf. */
167   index = h->list_len;
168   h->list[index] = ptr;
169   h->list_len++;
170
171   /* Reorganize the heap from bottom up. */
172   reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
173   
174   pthread_mutex_unlock (&h->lock);
175   return (0);
176 } /* int c_heap_insert */
177
178 void *c_heap_get_root (c_heap_t *h)
179 {
180   void *ret = NULL;
181
182   if (h == NULL)
183     return (NULL);
184
185   pthread_mutex_lock (&h->lock);
186
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--;
204
205     reheap (h, /* root = */ 0, DIR_DOWN);
206   }
207
208   /* free some memory */
209   if ((h->list_len + 32) < h->list_size)
210   {
211     void **tmp;
212
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   }
220
221   pthread_mutex_unlock (&h->lock);
222
223   return (ret);
224 } /* void *c_heap_get_root */
225
226 /* vim: set sw=2 sts=2 et fdm=marker : */