Merge branch 'collectd-4.6' into collectd-4.7
[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 / 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   if ((h == NULL) || (ptr == NULL))
144     return (-EINVAL);
145
146   pthread_mutex_lock (&h->lock);
147
148   assert (h->list_len <= h->list_size);
149   if (h->list_len == h->list_size)
150   {
151     void **tmp;
152
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     }
159
160     h->list = tmp;
161     h->list_size += 16;
162   }
163
164   /* Insert the new node as a leaf. */
165   h->list[h->list_len] = ptr;
166   h->list_len++;
167
168   /* Reorganize the heap from bottom up. */
169   reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP);
170   
171   pthread_mutex_unlock (&h->lock);
172   return (0);
173 } /* int c_heap_insert */
174
175 void *c_head_get_root (c_heap_t *h)
176 {
177   void *ret = NULL;
178
179   if (h == NULL)
180     return (NULL);
181
182   pthread_mutex_lock (&h->lock);
183
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--;
201
202     reheap (h, /* root = */ 0, DIR_DOWN);
203   }
204
205   /* free some memory */
206   if ((h->list_len + 32) < h->list_size)
207   {
208     void **tmp;
209
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   }
217
218   pthread_mutex_unlock (&h->lock);
219
220   return (ret);
221 } /* void *c_head_get_root */
222
223 /* vim: set sw=2 sts=2 et fdm=marker : */