Merge branch 'collectd-5.5'
[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 <string.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <pthread.h>
32
33 #include "utils_heap.h"
34
35 struct c_heap_s
36 {
37   pthread_mutex_t lock;
38   int (*compare) (const void *, const void *);
39
40   void **list;
41   size_t list_len; /* # entries used */
42   size_t list_size; /* # entries allocated */
43 };
44
45 enum reheap_direction
46 {
47   DIR_UP,
48   DIR_DOWN
49 };
50
51 static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir)
52 {
53   size_t left;
54   size_t right;
55   size_t min;
56   int status;
57
58   /* Calculate the positions of the children */
59   left = (2 * root) + 1;
60   if (left >= h->list_len)
61     left = 0;
62
63   right = (2 * root) + 2;
64   if (right >= h->list_len)
65     right = 0;
66
67   /* Check which one of the children is smaller. */
68   if ((left == 0) && (right == 0))
69     return;
70   else if (left == 0)
71     min = right;
72   else if (right == 0)
73     min = left;
74   else
75   {
76     status = h->compare (h->list[left], h->list[right]);
77     if (status > 0)
78       min = right;
79     else
80       min = left;
81   }
82
83   status = h->compare (h->list[root], h->list[min]);
84   if (status <= 0)
85   {
86     /* We didn't need to change anything, so the rest of the tree should be
87      * okay now. */
88     return;
89   }
90   else /* if (status > 0) */
91   {
92     void *tmp;
93
94     tmp = h->list[root];
95     h->list[root] = h->list[min];
96     h->list[min] = tmp;
97   }
98
99   if ((dir == DIR_UP) && (root == 0))
100     return;
101
102   if (dir == DIR_UP)
103     reheap (h, (root - 1) / 2, dir);
104   else if (dir == DIR_DOWN)
105     reheap (h, min, dir);
106 } /* void reheap */
107
108 c_heap_t *c_heap_create (int (*compare) (const void *, const void *))
109 {
110   c_heap_t *h;
111
112   if (compare == NULL)
113     return (NULL);
114
115   h = calloc (1, sizeof (*h));
116   if (h == NULL)
117     return (NULL);
118
119   pthread_mutex_init (&h->lock, /* attr = */ NULL);
120   h->compare = compare;
121
122   h->list = NULL;
123   h->list_len = 0;
124   h->list_size = 0;
125
126   return (h);
127 } /* c_heap_t *c_heap_create */
128
129 void c_heap_destroy (c_heap_t *h)
130 {
131   if (h == NULL)
132     return;
133
134   h->list_len = 0;
135   h->list_size = 0;
136   free (h->list);
137   h->list = NULL;
138
139   pthread_mutex_destroy (&h->lock);
140
141   free (h);
142 } /* void c_heap_destroy */
143
144 int c_heap_insert (c_heap_t *h, void *ptr)
145 {
146   size_t index;
147
148   if ((h == NULL) || (ptr == NULL))
149     return (-EINVAL);
150
151   pthread_mutex_lock (&h->lock);
152
153   assert (h->list_len <= h->list_size);
154   if (h->list_len == h->list_size)
155   {
156     void **tmp;
157
158     tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
159     if (tmp == NULL)
160     {
161       pthread_mutex_unlock (&h->lock);
162       return (-ENOMEM);
163     }
164
165     h->list = tmp;
166     h->list_size += 16;
167   }
168
169   /* Insert the new node as a leaf. */
170   index = h->list_len;
171   h->list[index] = ptr;
172   h->list_len++;
173
174   /* Reorganize the heap from bottom up. */
175   reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
176
177   pthread_mutex_unlock (&h->lock);
178   return (0);
179 } /* int c_heap_insert */
180
181 void *c_heap_get_root (c_heap_t *h)
182 {
183   void *ret = NULL;
184
185   if (h == NULL)
186     return (NULL);
187
188   pthread_mutex_lock (&h->lock);
189
190   if (h->list_len == 0)
191   {
192     pthread_mutex_unlock (&h->lock);
193     return (NULL);
194   }
195   else if (h->list_len == 1)
196   {
197     ret = h->list[0];
198     h->list[0] = NULL;
199     h->list_len = 0;
200   }
201   else /* if (h->list_len > 1) */
202   {
203     ret = h->list[0];
204     h->list[0] = h->list[h->list_len - 1];
205     h->list[h->list_len - 1] = NULL;
206     h->list_len--;
207
208     reheap (h, /* root = */ 0, DIR_DOWN);
209   }
210
211   /* free some memory */
212   if ((h->list_len + 32) < h->list_size)
213   {
214     void **tmp;
215
216     tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
217     if (tmp != NULL)
218     {
219       h->list = tmp;
220       h->list_size = h->list_len + 16;
221     }
222   }
223
224   pthread_mutex_unlock (&h->lock);
225
226   return (ret);
227 } /* void *c_heap_get_root */
228
229 /* vim: set sw=2 sts=2 et fdm=marker : */