2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 /** @file queue.cpp Implementation of the #BinaryHeap/#Hash. */
10 #include "../../stdafx.h"
11 #include "../../core/alloc_func.hpp"
14 #include "../../safeguards.h"
19 * For information, see: http://www.policyalmanac.org/games/binaryHeaps.htm
22 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE_BITS
= 10; ///< The number of elements that will be malloc'd at a time.
23 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE
= 1 << BinaryHeap::BINARY_HEAP_BLOCKSIZE_BITS
;
24 const int BinaryHeap::BINARY_HEAP_BLOCKSIZE_MASK
= BinaryHeap::BINARY_HEAP_BLOCKSIZE
- 1;
27 * Clears the queue, by removing all values from it. Its state is
28 * effectively reset. If free_items is true, each of the items cleared
29 * in this way are free()'d.
31 void BinaryHeap::Clear(bool free_values
)
33 /* Free all items if needed and free all but the first blocks of memory */
37 for (i
= 0; i
< this->blocks
; i
++) {
38 if (this->elements
[i
] == nullptr) {
39 /* No more allocated blocks */
42 /* For every allocated block */
44 for (j
= 0; j
< (1 << BINARY_HEAP_BLOCKSIZE_BITS
); j
++) {
45 /* For every element in the block */
46 if ((this->size
>> BINARY_HEAP_BLOCKSIZE_BITS
) == i
&&
47 (this->size
& BINARY_HEAP_BLOCKSIZE_MASK
) == j
) {
48 break; // We're past the last element
50 free(this->elements
[i
][j
].item
);
54 /* Leave the first block of memory alone */
55 free(this->elements
[i
]);
56 this->elements
[i
] = nullptr;
64 * Frees the queue, by reclaiming all memory allocated by it. After
65 * this it is no longer usable. If free_items is true, any remaining
66 * items are free()'d too.
68 void BinaryHeap::Free(bool free_values
)
72 this->Clear(free_values
);
73 for (i
= 0; i
< this->blocks
; i
++) {
74 if (this->elements
[i
] == nullptr) break;
75 free(this->elements
[i
]);
81 * Pushes an element into the queue, at the appropriate place for the queue.
82 * Requires the queue pointer to be of an appropriate type, of course.
84 bool BinaryHeap::Push(void *item
, int priority
)
86 if (this->size
== this->max_size
) return false;
87 assert(this->size
< this->max_size
);
89 if (this->elements
[this->size
>> BINARY_HEAP_BLOCKSIZE_BITS
] == nullptr) {
90 /* The currently allocated blocks are full, allocate a new one */
91 assert((this->size
& BINARY_HEAP_BLOCKSIZE_MASK
) == 0);
92 this->elements
[this->size
>> BINARY_HEAP_BLOCKSIZE_BITS
] = MallocT
<BinaryHeapNode
>(BINARY_HEAP_BLOCKSIZE
);
96 /* Add the item at the end of the array */
97 this->GetElement(this->size
+ 1).priority
= priority
;
98 this->GetElement(this->size
+ 1).item
= item
;
101 /* Now we are going to check where it belongs. As long as the parent is
102 * bigger, we switch with the parent */
110 /* Get the parent of this object (divide by 2) */
112 /* Is the parent bigger than the current, switch them */
113 if (this->GetElement(i
).priority
<= this->GetElement(j
).priority
) {
114 temp
= this->GetElement(j
);
115 this->GetElement(j
) = this->GetElement(i
);
116 this->GetElement(i
) = temp
;
119 /* It is not, we're done! */
129 * Deletes the item from the queue. priority should be specified if
130 * known, which speeds up the deleting for some queue's. Should be -1
133 bool BinaryHeap::Delete(void *item
, int priority
)
137 /* First, we try to find the item.. */
139 if (this->GetElement(i
+ 1).item
== item
) break;
141 } while (i
< this->size
);
142 /* We did not find the item, so we return false */
143 if (i
== this->size
) return false;
145 /* Now we put the last item over the current item while decreasing the size of the elements */
147 this->GetElement(i
+ 1) = this->GetElement(this->size
+ 1);
149 /* Now the only thing we have to do, is resort it..
150 * On place i there is the item to be sorted.. let's start there */
154 /* Because of the fact that Binary Heap uses array from 1 to n, we need to
161 /* Check if we have 2 children */
162 if (2 * j
+ 1 <= this->size
) {
163 /* Is this child smaller than the parent? */
164 if (this->GetElement(j
).priority
>= this->GetElement(2 * j
).priority
) i
= 2 * j
;
165 /* Yes, we _need_ to use i here, not j, because we want to have the smallest child
166 * This way we get that straight away! */
167 if (this->GetElement(i
).priority
>= this->GetElement(2 * j
+ 1).priority
) i
= 2 * j
+ 1;
168 /* Do we have one child? */
169 } else if (2 * j
<= this->size
) {
170 if (this->GetElement(j
).priority
>= this->GetElement(2 * j
).priority
) i
= 2 * j
;
173 /* One of our children is smaller than we are, switch */
175 temp
= this->GetElement(j
);
176 this->GetElement(j
) = this->GetElement(i
);
177 this->GetElement(i
) = temp
;
179 /* None of our children is smaller, so we stay here.. stop :) */
189 * Pops the first element from the queue. What exactly is the first element,
190 * is defined by the exact type of queue.
192 void *BinaryHeap::Pop()
196 if (this->size
== 0) return nullptr;
198 /* The best item is always on top, so give that as result */
199 result
= this->GetElement(1).item
;
200 /* And now we should get rid of this item... */
201 this->Delete(this->GetElement(1).item
, this->GetElement(1).priority
);
207 * Initializes a binary heap and allocates internal memory for maximum of
210 void BinaryHeap::Init(uint max_size
)
212 this->max_size
= max_size
;
214 /* We malloc memory in block of BINARY_HEAP_BLOCKSIZE
215 * It autosizes when it runs out of memory */
216 this->elements
= CallocT
<BinaryHeapNode
*>((max_size
- 1) / BINARY_HEAP_BLOCKSIZE
+ 1);
217 this->elements
[0] = MallocT
<BinaryHeapNode
>(BINARY_HEAP_BLOCKSIZE
);
221 /* Because we don't want anyone else to bother with our defines */
229 * Builds a new hash in an existing struct. Make sure that hash() always
230 * returns a hash less than num_buckets! Call delete_hash after use
232 void Hash::Init(Hash_HashProc
*hash
, uint num_buckets
)
234 /* Allocate space for the Hash, the buckets and the bucket flags */
237 /* Ensure the size won't overflow. */
238 CheckAllocationConstraints(sizeof(*this->buckets
) + sizeof(*this->buckets_in_use
), num_buckets
);
242 this->num_buckets
= num_buckets
;
243 this->buckets
= (HashNode
*)MallocT
<byte
>(num_buckets
* (sizeof(*this->buckets
) + sizeof(*this->buckets_in_use
)));
244 this->buckets_in_use
= (bool*)(this->buckets
+ num_buckets
);
245 for (i
= 0; i
< num_buckets
; i
++) this->buckets_in_use
[i
] = false;
249 * Deletes the hash and cleans up. Only cleans up memory allocated by new_Hash
250 * & friends. If free is true, it will call free() on all the values that
251 * are left in the hash.
253 void Hash::Delete(bool free_values
)
257 /* Iterate all buckets */
258 for (i
= 0; i
< this->num_buckets
; i
++) {
259 if (this->buckets_in_use
[i
]) {
262 /* Free the first value */
263 if (free_values
) free(this->buckets
[i
].value
);
264 node
= this->buckets
[i
].next
;
265 while (node
!= nullptr) {
266 HashNode
*prev
= node
;
270 if (free_values
) free(prev
->value
);
277 /* No need to free buckets_in_use, it is always allocated in one
278 * malloc with buckets */
282 void Hash::PrintStatistics() const
284 uint used_buckets
= 0;
285 uint max_collision
= 0;
290 for (i
= 0; i
< lengthof(usage
); i
++) usage
[i
] = 0;
291 for (i
= 0; i
< this->num_buckets
; i
++) {
293 if (this->buckets_in_use
[i
]) {
294 const HashNode
*node
;
297 for (node
= &this->buckets
[i
]; node
!= nullptr; node
= node
->next
) collision
++;
298 if (collision
> max_collision
) max_collision
= collision
;
300 if (collision
>= lengthof(usage
)) collision
= lengthof(usage
) - 1;
302 if (collision
> 0 && usage
[collision
] >= max_usage
) {
303 max_usage
= usage
[collision
];
310 "Non empty buckets: %u\n"
311 "Max collision: %u\n",
312 this->num_buckets
, this->size
, used_buckets
, max_collision
315 for (i
= 0; i
<= max_collision
; i
++) {
317 printf("%u:%u ", i
, usage
[i
]);
322 for (j
= 0; j
< usage
[i
] * 160 / 800; j
++) putchar('#');
333 * Cleans the hash, but keeps the memory allocated
335 void Hash::Clear(bool free_values
)
340 if (this->size
> 2000) this->PrintStatistics();
343 /* Iterate all buckets */
344 for (i
= 0; i
< this->num_buckets
; i
++) {
345 if (this->buckets_in_use
[i
]) {
348 this->buckets_in_use
[i
] = false;
349 /* Free the first value */
350 if (free_values
) free(this->buckets
[i
].value
);
351 node
= this->buckets
[i
].next
;
352 while (node
!= nullptr) {
353 HashNode
*prev
= node
;
356 if (free_values
) free(prev
->value
);
365 * Finds the node that that saves this key pair. If it is not
366 * found, returns nullptr. If it is found, *prev is set to the
367 * node before the one found, or if the node found was the first in the bucket
368 * to nullptr. If it is not found, *prev is set to the last HashNode in the
369 * bucket, or nullptr if it is empty. prev can also be nullptr, in which case it is
370 * not used for output.
372 HashNode
*Hash::FindNode(uint key1
, uint key2
, HashNode
** prev_out
) const
374 uint hash
= this->hash(key1
, key2
);
375 HashNode
*result
= nullptr;
377 /* Check if the bucket is empty */
378 if (!this->buckets_in_use
[hash
]) {
379 if (prev_out
!= nullptr) *prev_out
= nullptr;
381 /* Check the first node specially */
382 } else if (this->buckets
[hash
].key1
== key1
&& this->buckets
[hash
].key2
== key2
) {
384 result
= this->buckets
+ hash
;
385 if (prev_out
!= nullptr) *prev_out
= nullptr;
386 /* Check all other nodes */
388 HashNode
*prev
= this->buckets
+ hash
;
391 for (node
= prev
->next
; node
!= nullptr; node
= node
->next
) {
392 if (node
->key1
== key1
&& node
->key2
== key2
) {
399 if (prev_out
!= nullptr) *prev_out
= prev
;
405 * Deletes the value with the specified key pair from the hash and returns
406 * that value. Returns nullptr when the value was not present. The value returned
409 void *Hash::DeleteValue(uint key1
, uint key2
)
412 HashNode
*prev
; // Used as output var for below function call
413 HashNode
*node
= this->FindNode(key1
, key2
, &prev
);
415 if (node
== nullptr) {
418 } else if (prev
== nullptr) {
419 /* It is in the first node, we can't free that one, so we free
420 * the next one instead (if there is any)*/
422 result
= node
->value
;
423 if (node
->next
!= nullptr) {
424 HashNode
*next
= node
->next
;
425 /* Copy the second to the first */
427 /* Free the second */
430 /* This was the last in this bucket
431 * Mark it as empty */
432 uint hash
= this->hash(key1
, key2
);
433 this->buckets_in_use
[hash
] = false;
436 /* It is in another node
438 result
= node
->value
;
439 /* Link previous and next nodes */
440 prev
->next
= node
->next
;
444 if (result
!= nullptr) this->size
--;
449 * Sets the value associated with the given key pair to the given value.
450 * Returns the old value if the value was replaced, nullptr when it was not yet present.
452 void *Hash::Set(uint key1
, uint key2
, void *value
)
455 HashNode
*node
= this->FindNode(key1
, key2
, &prev
);
457 if (node
!= nullptr) {
459 void *result
= node
->value
;
464 /* It is not yet present, let's add it */
465 if (prev
== nullptr) {
466 /* The bucket is still empty */
467 uint hash
= this->hash(key1
, key2
);
468 this->buckets_in_use
[hash
] = true;
469 node
= this->buckets
+ hash
;
471 /* Add it after prev */
472 node
= MallocT
<HashNode
>(1);
475 node
->next
= nullptr;
484 * Gets the value associated with the given key pair, or nullptr when it is not
487 void *Hash::Get(uint key1
, uint key2
) const
489 HashNode
*node
= this->FindNode(key1
, key2
, nullptr);
491 return (node
!= nullptr) ? node
->value
: nullptr;