OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

queue.cpp
Go to the documentation of this file.
1 /* $Id: queue.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #include "../../stdafx.h"
13 #include "../../core/alloc_func.hpp"
14 #include "queue.h"
15
16 #include "../../safeguards.h"
17
18
19 /*
20 * Binary Heap
21 * For information, see: http://www.policyalmanac.org/games/binaryHeaps.htm
22 */
23
24 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE_BITS = 10;
25 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE = 1 << BinaryHeap::BINARY_HEAP_BLOCKSIZE_BITS;
26 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE_MASK = BinaryHeap::BINARY_HEAP_BLOCKSIZE - 1;
27
33 void BinaryHeap::Clear(bool free_values)
34 {
35 /* Free all items if needed and free all but the first blocks of memory */
36 uint i;
37 uint j;
38
39 for (i = 0; i < this->blocks; i++) {
40 if (this->elements[i] == NULL) {
41 /* No more allocated blocks */
42 break;
43 }
44 /* For every allocated block */
45 if (free_values) {
46 for (j = 0; j < (1 << BINARY_HEAP_BLOCKSIZE_BITS); j++) {
47 /* For every element in the block */
48 if ((this->size >> BINARY_HEAP_BLOCKSIZE_BITS) == i &&
49 (this->size & BINARY_HEAP_BLOCKSIZE_MASK) == j) {
50 break; // We're past the last element
51 }
52 free(this->elements[i][j].item);
53 }
54 }
55 if (i != 0) {
56 /* Leave the first block of memory alone */
57 free(this->elements[i]);
58 this->elements[i] = NULL;
59 }
60 }
61 this->size = 0;
62 this->blocks = 1;
63 }
64
70 void BinaryHeap::Free(bool free_values)
71 {
72 uint i;
73
74 this->Clear(free_values);
75 for (i = 0; i < this->blocks; i++) {
76 if (this->elements[i] == NULL) break;
77 free(this->elements[i]);
78 }
79 free(this->elements);
80 }
81
86 bool BinaryHeap::Push(void *item, int priority)
87 {
88 if (this->size == this->max_size) return false;
89 assert(this->size < this->max_size);
90
91 if (this->elements[this->size >> BINARY_HEAP_BLOCKSIZE_BITS] == NULL) {
92 /* The currently allocated blocks are full, allocate a new one */
93 assert((this->size & BINARY_HEAP_BLOCKSIZE_MASK) == 0);
94 this->elements[this->size >> BINARY_HEAP_BLOCKSIZE_BITS] = MallocT<BinaryHeapNode>(BINARY_HEAP_BLOCKSIZE);
95 this->blocks++;
96 }
97
98 /* Add the item at the end of the array */
99 this->GetElement(this->size + 1).priority = priority;
100 this->GetElement(this->size + 1).item = item;
101 this->size++;
102
103 /* Now we are going to check where it belongs. As long as the parent is
104 * bigger, we switch with the parent */
105 {
106 BinaryHeapNode temp;
107 int i;
108 int j;
109
110 i = this->size;
111 while (i > 1) {
112 /* Get the parent of this object (divide by 2) */
113 j = i / 2;
114 /* Is the parent bigger than the current, switch them */
115 if (this->GetElement(i).priority <= this->GetElement(j).priority) {
116 temp = this->GetElement(j);
117 this->GetElement(j) = this->GetElement(i);
118 this->GetElement(i) = temp;
119 i = j;
120 } else {
121 /* It is not, we're done! */
122 break;
123 }
124 }
125 }
126
127 return true;
128 }
129
135 bool BinaryHeap::Delete(void *item, int priority)
136 {
137 uint i = 0;
138
139 /* First, we try to find the item.. */
140 do {
141 if (this->GetElement(i + 1).item == item) break;
142 i++;
143 } while (i < this->size);
144 /* We did not find the item, so we return false */
145 if (i == this->size) return false;
146
147 /* Now we put the last item over the current item while decreasing the size of the elements */
148 this->size--;
149 this->GetElement(i + 1) = this->GetElement(this->size + 1);
150
151 /* Now the only thing we have to do, is resort it..
152 * On place i there is the item to be sorted.. let's start there */
153 {
154 uint j;
155 BinaryHeapNode temp;
156 /* Because of the fact that Binary Heap uses array from 1 to n, we need to
157 * increase i by 1
158 */
159 i++;
160
161 for (;;) {
162 j = i;
163 /* Check if we have 2 children */
164 if (2 * j + 1 <= this->size) {
165 /* Is this child smaller than the parent? */
166 if (this->GetElement(j).priority >= this->GetElement(2 * j).priority) i = 2 * j;
167 /* Yes, we _need_ to use i here, not j, because we want to have the smallest child
168 * This way we get that straight away! */
169 if (this->GetElement(i).priority >= this->GetElement(2 * j + 1).priority) i = 2 * j + 1;
170 /* Do we have one child? */
171 } else if (2 * j <= this->size) {
172 if (this->GetElement(j).priority >= this->GetElement(2 * j).priority) i = 2 * j;
173 }
174
175 /* One of our children is smaller than we are, switch */
176 if (i != j) {
177 temp = this->GetElement(j);
178 this->GetElement(j) = this->GetElement(i);
179 this->GetElement(i) = temp;
180 } else {
181 /* None of our children is smaller, so we stay here.. stop :) */
182 break;
183 }
184 }
185 }
186
187 return true;
188 }
189
194 void *BinaryHeap::Pop()
195 {
196 void *result;
197
198 if (this->size == 0) return NULL;
199
200 /* The best item is always on top, so give that as result */
201 result = this->GetElement(1).item;
202 /* And now we should get rid of this item... */
203 this->Delete(this->GetElement(1).item, this->GetElement(1).priority);
204
205 return result;
206 }
207
212 void BinaryHeap::Init(uint max_size)
213 {
214 this->max_size = max_size;
215 this->size = 0;
216 /* We malloc memory in block of BINARY_HEAP_BLOCKSIZE
217 * It autosizes when it runs out of memory */
218 this->elements = CallocT<BinaryHeapNode*>((max_size - 1) / BINARY_HEAP_BLOCKSIZE + 1);
219 this->elements[0] = MallocT<BinaryHeapNode>(BINARY_HEAP_BLOCKSIZE);
220 this->blocks = 1;
221 }
222
223 /* Because we don't want anyone else to bother with our defines */
224 #undef BIN_HEAP_ARR
225
226 /*
227 * Hash
228 */
229
234 void Hash::Init(Hash_HashProc *hash, uint num_buckets)
235 {
236 /* Allocate space for the Hash, the buckets and the bucket flags */
237 uint i;
238
239 /* Ensure the size won't overflow. */
240 CheckAllocationConstraints(sizeof(*this->buckets) + sizeof(*this->buckets_in_use), num_buckets);
241
242 this->hash = hash;
243 this->size = 0;
244 this->num_buckets = num_buckets;
245 this->buckets = (HashNode*)MallocT<byte>(num_buckets * (sizeof(*this->buckets) + sizeof(*this->buckets_in_use)));
246 this->buckets_in_use = (bool*)(this->buckets + num_buckets);
247 for (i = 0; i < num_buckets; i++) this->buckets_in_use[i] = false;
248 }
249
255 void Hash::Delete(bool free_values)
256 {
257 uint i;
258
259 /* Iterate all buckets */
260 for (i = 0; i < this->num_buckets; i++) {
261 if (this->buckets_in_use[i]) {
262 HashNode *node;
263
264 /* Free the first value */
265 if (free_values) free(this->buckets[i].value);
266 node = this->buckets[i].next;
267 while (node != NULL) {
268 HashNode *prev = node;
269
270 node = node->next;
271 /* Free the value */
272 if (free_values) free(prev->value);
273 /* Free the node */
274 free(prev);
275 }
276 }
277 }
278 free(this->buckets);
279 /* No need to free buckets_in_use, it is always allocated in one
280 * malloc with buckets */
281 }
282
283 #ifdef HASH_STATS
284 void Hash::PrintStatistics() const
285 {
286 uint used_buckets = 0;
287 uint max_collision = 0;
288 uint max_usage = 0;
289 uint usage[200];
290 uint i;
291
292 for (i = 0; i < lengthof(usage); i++) usage[i] = 0;
293 for (i = 0; i < this->num_buckets; i++) {
294 uint collision = 0;
295 if (this->buckets_in_use[i]) {
296 const HashNode *node;
297
298 used_buckets++;
299 for (node = &this->buckets[i]; node != NULL; node = node->next) collision++;
300 if (collision > max_collision) max_collision = collision;
301 }
302 if (collision >= lengthof(usage)) collision = lengthof(usage) - 1;
303 usage[collision]++;
304 if (collision > 0 && usage[collision] >= max_usage) {
305 max_usage = usage[collision];
306 }
307 }
308 printf(
309 "---\n"
310 "Hash size: %d\n"
311 "Nodes used: %d\n"
312 "Non empty buckets: %d\n"
313 "Max collision: %d\n",
314 this->num_buckets, this->size, used_buckets, max_collision
315 );
316 printf("{ ");
317 for (i = 0; i <= max_collision; i++) {
318 if (usage[i] > 0) {
319 printf("%d:%d ", i, usage[i]);
320 #if 0
321 if (i > 0) {
322 uint j;
323
324 for (j = 0; j < usage[i] * 160 / 800; j++) putchar('#');
325 }
326 printf("\n");
327 #endif
328 }
329 }
330 printf ("}\n");
331 }
332 #endif
333
337 void Hash::Clear(bool free_values)
338 {
339 uint i;
340
341 #ifdef HASH_STATS
342 if (this->size > 2000) this->PrintStatistics();
343 #endif
344
345 /* Iterate all buckets */
346 for (i = 0; i < this->num_buckets; i++) {
347 if (this->buckets_in_use[i]) {
348 HashNode *node;
349
350 this->buckets_in_use[i] = false;
351 /* Free the first value */
352 if (free_values) free(this->buckets[i].value);
353 node = this->buckets[i].next;
354 while (node != NULL) {
355 HashNode *prev = node;
356
357 node = node->next;
358 if (free_values) free(prev->value);
359 free(prev);
360 }
361 }
362 }
363 this->size = 0;
364 }
365
374 HashNode *Hash::FindNode(uint key1, uint key2, HashNode** prev_out) const
375 {
376 uint hash = this->hash(key1, key2);
377 HashNode *result = NULL;
378
379 /* Check if the bucket is empty */
380 if (!this->buckets_in_use[hash]) {
381 if (prev_out != NULL) *prev_out = NULL;
382 result = NULL;
383 /* Check the first node specially */
384 } else if (this->buckets[hash].key1 == key1 && this->buckets[hash].key2 == key2) {
385 /* Save the value */
386 result = this->buckets + hash;
387 if (prev_out != NULL) *prev_out = NULL;
388 /* Check all other nodes */
389 } else {
390 HashNode *prev = this->buckets + hash;
391 HashNode *node;
392
393 for (node = prev->next; node != NULL; node = node->next) {
394 if (node->key1 == key1 && node->key2 == key2) {
395 /* Found it */
396 result = node;
397 break;
398 }
399 prev = node;
400 }
401 if (prev_out != NULL) *prev_out = prev;
402 }
403 return result;
404 }
405
411 void *Hash::DeleteValue(uint key1, uint key2)
412 {
413 void *result;
414 HashNode *prev; // Used as output var for below function call
415 HashNode *node = this->FindNode(key1, key2, &prev);
416
417 if (node == NULL) {
418 /* not found */
419 result = NULL;
420 } else if (prev == NULL) {
421 /* It is in the first node, we can't free that one, so we free
422 * the next one instead (if there is any)*/
423 /* Save the value */
424 result = node->value;
425 if (node->next != NULL) {
426 HashNode *next = node->next;
427 /* Copy the second to the first */
428 *node = *next;
429 /* Free the second */
430 free(next);
431 } else {
432 /* This was the last in this bucket
433 * Mark it as empty */
434 uint hash = this->hash(key1, key2);
435 this->buckets_in_use[hash] = false;
436 }
437 } else {
438 /* It is in another node
439 * Save the value */
440 result = node->value;
441 /* Link previous and next nodes */
442 prev->next = node->next;
443 /* Free the node */
444 free(node);
445 }
446 if (result != NULL) this->size--;
447 return result;
448 }
449
454 void *Hash::Set(uint key1, uint key2, void *value)
455 {
456 HashNode *prev;
457 HashNode *node = this->FindNode(key1, key2, &prev);
458
459 if (node != NULL) {
460 /* Found it */
461 void *result = node->value;
462
463 node->value = value;
464 return result;
465 }
466 /* It is not yet present, let's add it */
467 if (prev == NULL) {
468 /* The bucket is still empty */
469 uint hash = this->hash(key1, key2);
470 this->buckets_in_use[hash] = true;
471 node = this->buckets + hash;
472 } else {
473 /* Add it after prev */
474 node = MallocT<HashNode>(1);
475 prev->next = node;
476 }
477 node->next = NULL;
478 node->key1 = key1;
479 node->key2 = key2;
480 node->value = value;
481 this->size++;
482 return NULL;
483 }
484
489 void *Hash::Get(uint key1, uint key2) const
490 {
491 HashNode *node = this->FindNode(key1, key2, NULL);
492
493 return (node != NULL) ? node->value : NULL;
494 }