Merge branch 'collectd-5.5' into collectd-5.6
[collectd.git] / src / daemon / utils_heap.c
1 /**
2  * collectd - src/utils_heap.c
3  * Copyright (C) 2009       Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <pthread.h>
31
32 #include "utils_heap.h"
33
34 struct c_heap_s
35 {
36   pthread_mutex_t lock;
37   int (*compare) (const void *, const void *);
38
39   void **list;
40   size_t list_len; /* # entries used */
41   size_t list_size; /* # entries allocated */
42 };
43
44 enum reheap_direction
45 {
46   DIR_UP,
47   DIR_DOWN
48 };
49
50 static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
51 {
52   size_t left;
53   size_t right;
54   size_t min;
55   int status;
56
57   /* Calculate the positions of the children */
58   left = (2 * root) + 1;
59   if (left >= h->list_len)
60     left = 0;
61
62   right = (2 * root) + 2;
63   if (right >= h->list_len)
64     right = 0;
65
66   /* Check which one of the children is smaller. */
67   if ((left == 0) && (right == 0))
68     return;
69   else if (left == 0)
70     min = right;
71   else if (right == 0)
72     min = left;
73   else
74   {
75     status = h->compare (h->list[left], h->list[right]);
76     if (status > 0)
77       min = right;
78     else
79       min = left;
80   }
81
82   status = h->compare (h->list[root], h->list[min]);
83   if (status <= 0)
84   {
85     /* We didn't need to change anything, so the rest of the tree should be
86      * okay now. */
87     return;
88   }
89   else /* if (status > 0) */
90   {
91     void *tmp;
92
93     tmp = h->list[root];
94     h->list[root] = h->list[min];
95     h->list[min] = tmp;
96   }
97
98   if ((dir == DIR_UP) && (root == 0))
99     return;
100
101   if (dir == DIR_UP)
102     reheap (h, (root - 1) / 2, dir);
103   else if (dir == DIR_DOWN)
104     reheap (h, min, dir);
105 } /* void reheap */
106
107 c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
108 {
109   c_heap_t *h;
110
111   if (compare == NULL)
112     return (NULL);
113
114   h = calloc (1, sizeof (*h));
115   if (h == NULL)
116     return (NULL);
117
118   pthread_mutex_init (&h->lock, /* attr = */ NULL);
119   h->compare = compare;
120
121   h->list = NULL;
122   h->list_len = 0;
123   h->list_size = 0;
124
125   return (h);
126 } /* c_heap_t *c_heap_create */
127
128 void c_heap_destroy (c_heap_t *h)
129 {
130   if (h == NULL)
131     return;
132
133   h->list_len = 0;
134   h->list_size = 0;
135   free (h->list);
136   h->list = NULL;
137
138   pthread_mutex_destroy (&h->lock);
139
140   free (h);
141 } /* void c_heap_destroy */
142
143 int c_heap_insert (c_heap_t *h, void *ptr)
144 {
145   size_t index;
146
147   if ((h == NULL) || (ptr == NULL))
148     return (-EINVAL);
149
150   pthread_mutex_lock (&h->lock);
151
152   assert (h->list_len <= h->list_size);
153   if (h->list_len == h->list_size)
154   {
155     void **tmp;
156
157     tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
158     if (tmp == NULL)
159     {
160       pthread_mutex_unlock (&h->lock);
161       return (-ENOMEM);
162     }
163
164     h->list = tmp;
165     h->list_size += 16;
166   }
167
168   /* Insert the new node as a leaf. */
169   index = h->list_len;
170   h->list[index] = ptr;
171   h->list_len++;
172
173   /* Reorganize the heap from bottom up. */
174   reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
175
176   pthread_mutex_unlock (&h->lock);
177   return (0);
178 } /* int c_heap_insert */
179
180 void *c_heap_get_root (c_heap_t *h)
181 {
182   void *ret = NULL;
183
184   if (h == NULL)
185     return (NULL);
186
187   pthread_mutex_lock (&h->lock);
188
189   if (h->list_len == 0)
190   {
191     pthread_mutex_unlock (&h->lock);
192     return (NULL);
193   }
194   else if (h->list_len == 1)
195   {
196     ret = h->list[0];
197     h->list[0] = NULL;
198     h->list_len = 0;
199   }
200   else /* if (h->list_len > 1) */
201   {
202     ret = h->list[0];
203     h->list[0] = h->list[h->list_len - 1];
204     h->list[h->list_len - 1] = NULL;
205     h->list_len--;
206
207     reheap (h, /* root = */ 0, DIR_DOWN);
208   }
209
210   /* free some memory */
211   if ((h->list_len + 32) < h->list_size)
212   {
213     void **tmp;
214
215     tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
216     if (tmp != NULL)
217     {
218       h->list = tmp;
219       h->list_size = h->list_len + 16;
220     }
221   }
222
223   pthread_mutex_unlock (&h->lock);
224
225   return (ret);
226 } /* void *c_heap_get_root */
227
228 /* vim: set sw=2 sts=2 et fdm=marker : */