]>
git.ipfire.org Git - thirdparty/squid.git/blob - lib/heap.c
3 * $Id: heap.c,v 1.7 2001/01/12 00:37:12 wessels Exp $
5 * AUTHOR: John Dilley, Hewlett Packard
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 /****************************************************************************
37 * Copyright (C) 1999 by Hewlett Packard
38 ****************************************************************************/
59 * Hacks for non-synchronized heap implementation.
61 #define mutex_lock(m) (void)0
62 #define mutex_unlock(m) (void)0
63 #define mutex_trylock(m) (void)0
64 #define mutex_init(m) ((m)=123456)
67 * Private function prototypes.
69 static void _heap_ify_up(heap
* hp
, heap_node
* elm
);
70 static void _heap_ify_down(heap
* hp
, heap_node
* elm
);
71 static int _heap_should_grow(heap
* hp
);
72 static void _heap_grow(heap
* hp
);
73 static void _heap_swap_element(heap
* hp
, heap_node
* elm1
, heap_node
* elm2
);
74 static int _heap_node_exist(heap
* hp
, int id
);
77 void _heap_print_tree(heap
* hp
, heap_node
* node
);
78 #endif /* HEAP_DEBUG */
80 #define Left(x) (2 * (x) + 1)
81 #define Right(x) (2 * (x) + 2)
82 #define Parent(x) ((int)((x)-1)/2)
84 #define Threshold 10000
89 /****************************************************************************
91 ****************************************************************************/
94 * Return a newly created heap. INITSIZE is the initial size of the heap.
97 new_heap(int initSize
, heap_key_func gen_key
)
99 heap
*hp
= xmalloc(sizeof(*hp
));
104 hp
->nodes
= xcalloc(initSize
, sizeof(heap_node
*));
105 assert(hp
->nodes
!= NULL
);
109 hp
->gen_key
= gen_key
;
117 * Free memory used by a heap. Does not free the metadata pointed to by the
118 * heap nodes, only the heap's internal memory.
121 delete_heap(heap
* hp
)
125 for (i
= 0; i
< hp
->last
; i
++) {
133 * Insert DAT based on KY into HP maintaining the heap property.
134 * Return the newly inserted heap node. The fields of ELM other
135 * than ID are never changed until ELM is deleted from HP, i.e.
136 * caller can assume that the heap node always exist at the same
137 * place in memory unless heap_delete or heap_extractmin is called
138 * on that node. This function exposes the heap's internal data
139 * structure to the caller. This is required in order to do O(lgN)
143 heap_insert(heap
* hp
, void *dat
)
145 heap_node
*elm
= xmalloc(sizeof(*elm
));
147 elm
->key
= heap_gen_key(hp
, dat
);
150 if (_heap_should_grow(hp
))
153 hp
->nodes
[hp
->last
] = elm
;
157 _heap_ify_up(hp
, elm
);
164 * Delete ELM while maintaining the heap property. ELM may be modified.
165 * Assumes that ELM is not NULL and frees it. Returns the data pointed to
166 * in, which the caller must free if necessary.
169 heap_delete(heap
* hp
, heap_node
* elm
)
172 heap_t data
= elm
->data
;
174 assert(_heap_node_exist(hp
, hp
->last
- 1));
176 lastNode
= hp
->nodes
[hp
->last
- 1];
177 _heap_swap_element(hp
, lastNode
, elm
);
178 heap_extractlast(hp
);
180 if (elm
== lastNode
) {
182 * lastNode just got freed, so don't access it in the next
186 } else if (hp
->last
> 0) {
187 if (lastNode
->key
< hp
->nodes
[Parent(lastNode
->id
)]->key
)
188 _heap_ify_up(hp
, lastNode
); /* COOL! */
189 _heap_ify_down(hp
, lastNode
);
195 * Delete the last element (leaf) out of the heap. Does not require a
201 * Function to generate keys. See macro definition in heap.h.
204 heap_gen_key(heap
* hp
, heap_t dat
)
206 return hp
->gen_key(dat
, hp
->age
);
208 #endif /* heap_gen_key */
212 * Returns the data of the node with the largest KEY value and removes that
213 * node from the heap. Returns NULL if the heap was empty.
216 heap_extractmin(heap
* hp
)
223 mutex_lock(hp
->lock
);
225 data
= hp
->nodes
[0]->data
;
226 heap_delete(hp
, hp
->nodes
[0]); /* Delete the root */
228 mutex_unlock(hp
->lock
);
235 * Remove the last node in HP. Frees the heap internal structure and
236 * returns the data pointes to by the last node.
239 heap_extractlast(heap
* hp
)
242 assert(_heap_node_exist(hp
, hp
->last
- 1));
244 data
= hp
->nodes
[hp
->last
]->data
;
245 xfree(hp
->nodes
[hp
->last
]);
251 * The semantics of this routine is the same as the followings:
252 * heap_delete(hp, elm);
253 * heap_insert(hp, dat);
254 * Returns the old data object from elm (the one being replaced). The
255 * caller must free this as necessary.
258 heap_update(heap
* hp
, heap_node
* elm
, void *dat
)
260 heap_t old
= elm
->data
;
261 heap_key ky
= heap_gen_key(hp
, dat
);
266 if (elm
->key
< hp
->nodes
[Parent(elm
->id
)]->key
)
267 _heap_ify_up(hp
, elm
);
268 _heap_ify_down(hp
, elm
);
275 * A pointer to the root node's DATA.
278 heap_peepmin(heap
* hp
)
280 assert(_heap_node_exist(hp
, 0));
281 return hp
->nodes
[0]->data
;
286 * The KEY of the root node.
289 heap_peepminkey(heap
* hp
)
291 assert(_heap_node_exist(hp
, 0));
292 return hp
->nodes
[0]->key
;
297 * Same as heap_peep except that this return the KEY of the node.
298 * Only meant for iteration.
301 heap_peepkey(heap
* hp
, int n
)
303 assert(_heap_node_exist(hp
, n
));
304 return hp
->nodes
[n
]->key
;
309 * A pointer to Nth node's DATA. The caller can iterate through HP by
310 * calling this routine. eg. Caller can execute the following code:
311 * for(i = 0; i < heap_nodes(hp); i++)
312 * data = heap_peep(hp, i);
315 heap_peep(heap
* hp
, int n
)
318 assert(_heap_node_exist(hp
, n
));
319 data
= hp
->nodes
[n
]->data
;
326 * Current number of nodes in HP.
329 heap_nodes(heap
* hp
)
333 #endif /* heap_nodes */
338 * Determine if the heap is empty. Returns 1 if HP has no elements and 0
342 heap_empty(heap
* hp
)
344 return (hp
->last
<= 0) ? 1 : 0;
346 #endif /* heap_empty */
348 /****************** Private Functions *******************/
351 * Maintain the heap order property (parent is smaller than children) which
352 * may only be violated at ELM downwards. Assumes caller has locked the heap.
355 _heap_ify_down(heap
* hp
, heap_node
* elm
)
358 int left
= 0, right
= 0;
361 left
= Left(elm
->id
);
362 right
= Right(elm
->id
);
363 if (!_heap_node_exist(hp
, left
)) {
364 /* At the bottom of the heap (no child). */
366 assert(!_heap_node_exist(hp
, right
));
368 } else if (!_heap_node_exist(hp
, right
))
369 /* Only left child exists. */
371 kid
= hp
->nodes
[left
];
373 if (hp
->nodes
[right
]->key
< hp
->nodes
[left
]->key
)
374 kid
= hp
->nodes
[right
];
376 kid
= hp
->nodes
[left
];
378 if (elm
->key
<= kid
->key
)
380 _heap_swap_element(hp
, kid
, elm
);
386 * Maintain the heap property above ELM. Caller has locked the heap.
389 _heap_ify_up(heap
* hp
, heap_node
* elm
)
391 heap_node
*parentNode
;
392 while (elm
->id
> 0) {
393 parentNode
= hp
->nodes
[Parent(elm
->id
)];
394 if (parentNode
->key
<= elm
->key
)
396 _heap_swap_element(hp
, parentNode
, elm
); /* Demote the parent. */
402 * Swap the position of ELM1 and ELM2 in heap structure. Their IDs are also
406 _heap_swap_element(heap
* hp
, heap_node
* elm1
, heap_node
* elm2
)
408 int elm1Id
= elm1
->id
;
411 hp
->nodes
[elm1
->id
] = elm1
;
412 hp
->nodes
[elm2
->id
] = elm2
;
419 * Copy KEY and DATA fields of SRC to DEST. ID field is NOT copied.
422 _heap_copy_element(heap_node
* src
, heap_node
* dest
)
424 dest
->key
= src
->key
;
425 dest
->data
= src
->data
;
432 * True if HP needs to be grown in size.
435 _heap_should_grow(heap
* hp
)
437 if (hp
->size
<= hp
->last
)
447 _heap_grow(heap
* hp
)
451 if (hp
->size
> Threshold
)
452 newSize
= hp
->size
* SlowRate
;
454 newSize
= hp
->size
* NormalRate
;
456 hp
->nodes
= xrealloc(hp
->nodes
, newSize
* sizeof(heap_node
*));
458 for (i
= 0; i
< hp
->size
; i
++)
459 newNodes
[i
] = hp
->nodes
[i
];
461 hp
->nodes
= newNodes
;
468 * True if a node with ID exists in HP.
471 _heap_node_exist(heap
* hp
, int id
)
473 if ((id
>= hp
->last
) || (id
< 0) || (hp
->nodes
[id
] == NULL
))
478 /****************************************************************************
479 * Printing and debug functions
480 ****************************************************************************/
483 * Print the heap in element order, id..last.
486 heap_print_inorder(heap
* hp
, int id
)
488 while (id
< hp
->last
) {
489 printf("%d\tKey = %.04f\n", id
, hp
->nodes
[id
]->key
);
495 * Returns 1 if HP maintians the heap property and 0 otherwise.
498 verify_heap_property(heap
* hp
)
502 for (i
= 0; i
< hp
->last
/ 2; i
++) {
504 if (_heap_node_exist(hp
, Left(i
)))
505 if (hp
->nodes
[i
]->key
> hp
->nodes
[Left(i
)]->key
)
507 if (_heap_node_exist(hp
, Right(i
)))
508 if (hp
->nodes
[i
]->key
> hp
->nodes
[Right(i
)]->key
)
511 printf("verifyHeap: violated at %d", i
);
512 heap_print_inorder(hp
, 0);
519 #ifdef MEASURE_HEAP_SKEW
521 /****************************************************************************
522 * Heap skew computation
523 ****************************************************************************/
526 compare_heap_keys(const void *a
, const void *b
)
528 heap_node
**an
= (heap_node
**) a
;
529 heap_node
**bn
= (heap_node
**) b
;
530 float cmp
= (*an
)->key
- (*bn
)->key
;
538 * Compute the heap skew for HEAP, a measure of how out-of-order the
539 * elements in the heap are. The skew of a heap node is the difference
540 * between its current position in the heap and where it would be if the
541 * heap were in sorted order. To compute this we have to sort the heap. At
542 * the end if the flag REPLACE is non-zero the heap will be returned in
543 * sorted order (with skew == 0). Note: using REPLACE does not help the
544 * performance of the heap, so only do this if you really want to have a
545 * sorted heap. It is faster not to replace.
548 calc_heap_skew(heap
* heap
, int replace
)
551 long id
, diff
, skew
= 0;
552 #ifdef HEAP_DEBUG_SKEW
554 #endif /* HEAP_DEBUG_SKEW */
559 * Lock the heap to copy it. If replacing it need to keep the heap locked
560 * until we are all done.
562 mutex_lock(hp
->lock
);
564 max
= heap_nodes(heap
);
567 * Copy the heap nodes to a new storage area for offline sorting.
569 nodes
= xmalloc(max
* sizeof(heap_node
*));
570 memcpy(nodes
, heap
->nodes
, max
* sizeof(heap_node
*));
574 * Unlock the heap to allow updates from other threads before the sort.
575 * This allows other heap operations to proceed concurrently with the
576 * heap skew computation on the heap at the time of the call ...
578 mutex_unlock(hp
->lock
);
580 qsort(nodes
, max
, sizeof(heap_node
*), compare_heap_keys
);
582 for (id
= 0; id
< max
; id
++) {
583 diff
= id
- nodes
[id
]->id
;
586 #ifdef HEAP_DEBUG_SKEW
587 skewsq
+= diff
* diff
;
588 #ifdef HEAP_DEBUG_ALL
589 printf("%d\tKey = %f, diff = %d\n", id
, nodes
[id
]->key
, diff
);
590 #endif /* HEAP_DEBUG */
591 #endif /* HEAP_DEBUG_SKEW */
596 * Replace the original heap with the newly sorted heap and let it
597 * continue. Then compute the skew using the copy of the previous heap
598 * which we maintain as private data.
600 memcpy(heap
->nodes
, nodes
, max
* sizeof(heap_node
*));
602 for (id
= 0; id
< max
; id
++) {
604 * Fix up all the ID values in the copied nodes.
606 heap
->nodes
[id
]->id
= id
;
609 mutex_unlock(hp
->lock
);
612 * The skew value is normalized to a range of [0..1]; the distribution
613 * appears to be a skewed Gaussian distribution. For random insertions
614 * into a heap the normalized skew will be slightly less than 0.5. The
615 * maximum value of skew/N^2 (for any value of N) is about 0.39 and is
618 norm
= skew
* 2.56 / (max
* max
);
621 * Free the nodes array; note this is just an array of pointers, not data!
627 #endif /* MEASURE_HEAP_SKEW */