{GPL, other}: Relicense to MIT license.
[collectd.git] / src / 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 = malloc (sizeof (*h));
116   if (h == NULL)
117     return (NULL);
118
119   memset (h, 0, sizeof (*h));
120   pthread_mutex_init (&h->lock, /* attr = */ NULL);
121   h->compare = compare;
122   
123   h->list = NULL;
124   h->list_len = 0;
125   h->list_size = 0;
126
127   return (h);
128 } /* c_heap_t *c_heap_create */
129
130 void c_heap_destroy (c_heap_t *h)
131 {
132   if (h == NULL)
133     return;
134
135   h->list_len = 0;
136   h->list_size = 0;
137   free (h->list);
138   h->list = NULL;
139
140   pthread_mutex_destroy (&h->lock);
141
142   free (h);
143 } /* void c_heap_destroy */
144
145 int c_heap_insert (c_heap_t *h, void *ptr)
146 {
147   size_t index;
148
149   if ((h == NULL) || (ptr == NULL))
150     return (-EINVAL);
151
152   pthread_mutex_lock (&h->lock);
153
154   assert (h->list_len <= h->list_size);
155   if (h->list_len == h->list_size)
156   {
157     void **tmp;
158
159     tmp = realloc (h->list, (h->list_size + 16) * sizeof (*h->list));
160     if (tmp == NULL)
161     {
162       pthread_mutex_unlock (&h->lock);
163       return (-ENOMEM);
164     }
165
166     h->list = tmp;
167     h->list_size += 16;
168   }
169
170   /* Insert the new node as a leaf. */
171   index = h->list_len;
172   h->list[index] = ptr;
173   h->list_len++;
174
175   /* Reorganize the heap from bottom up. */
176   reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
177   
178   pthread_mutex_unlock (&h->lock);
179   return (0);
180 } /* int c_heap_insert */
181
182 void *c_heap_get_root (c_heap_t *h)
183 {
184   void *ret = NULL;
185
186   if (h == NULL)
187     return (NULL);
188
189   pthread_mutex_lock (&h->lock);
190
191   if (h->list_len == 0)
192   {
193     pthread_mutex_unlock (&h->lock);
194     return (NULL);
195   }
196   else if (h->list_len == 1)
197   {
198     ret = h->list[0];
199     h->list[0] = NULL;
200     h->list_len = 0;
201   }
202   else /* if (h->list_len > 1) */
203   {
204     ret = h->list[0];
205     h->list[0] = h->list[h->list_len - 1];
206     h->list[h->list_len - 1] = NULL;
207     h->list_len--;
208
209     reheap (h, /* root = */ 0, DIR_DOWN);
210   }
211
212   /* free some memory */
213   if ((h->list_len + 32) < h->list_size)
214   {
215     void **tmp;
216
217     tmp = realloc (h->list, (h->list_len + 16) * sizeof (*h->list));
218     if (tmp != NULL)
219     {
220       h->list = tmp;
221       h->list_size = h->list_len + 16;
222     }
223   }
224
225   pthread_mutex_unlock (&h->lock);
226
227   return (ret);
228 } /* void *c_heap_get_root */
229
230 /* vim: set sw=2 sts=2 et fdm=marker : */