| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License 2.0 (the "License").  You may not use | 
					
						
							|  |  |  |  * this file except in compliance with the License.  You can obtain a copy | 
					
						
							|  |  |  |  * in the file LICENSE in the source distribution or at | 
					
						
							|  |  |  |  * https://www.openssl.org/source/license.html
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <openssl/crypto.h>
 | 
					
						
							|  |  |  | #include <openssl/err.h>
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include "internal/priority_queue.h"
 | 
					
						
							|  |  |  | #include "internal/safe_math.h"
 | 
					
						
							| 
									
										
										
										
											2023-07-26 20:41:31 +08:00
										 |  |  | #include "internal/numbers.h"
 | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | OSSL_SAFE_MATH_UNSIGNED(size_t, size_t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Fundamental operations: | 
					
						
							|  |  |  |  *                        Binary Heap   Fibonacci Heap | 
					
						
							|  |  |  |  *  Get smallest            O(1)          O(1) | 
					
						
							|  |  |  |  *  Delete any              O(log n)      O(log n) average but worst O(n) | 
					
						
							|  |  |  |  *  Insert                  O(log n)      O(1) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Not supported: | 
					
						
							|  |  |  |  *  Merge two structures    O(log n)      O(1) | 
					
						
							|  |  |  |  *  Decrease key            O(log n)      O(1) | 
					
						
							|  |  |  |  *  Increase key            O(log n)      ? | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The Fibonacci heap is quite a bit more complicated to implement and has | 
					
						
							|  |  |  |  * larger overhead in practice.  We favour the binary heap here.  A multi-way | 
					
						
							|  |  |  |  * (ternary or quaternary) heap might elicit a performance advantage via better | 
					
						
							|  |  |  |  * cache access patterns. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct pq_heap_st { | 
					
						
							|  |  |  |     void *data;     /* User supplied data pointer */ | 
					
						
							|  |  |  |     size_t index;   /* Constant index in elements[] */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct pq_elem_st { | 
					
						
							|  |  |  |     size_t posn;    /* Current index in heap[] or link in free list */ | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     int used;       /* Debug flag indicating that this is in use */ | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ossl_pqueue_st | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_heap_st *heap; | 
					
						
							|  |  |  |     struct pq_elem_st *elements; | 
					
						
							|  |  |  |     int (*compare)(const void *, const void *); | 
					
						
							|  |  |  |     size_t htop;        /* Highest used heap element */ | 
					
						
							|  |  |  |     size_t hmax;        /* Allocated heap & element space */ | 
					
						
							|  |  |  |     size_t freelist;    /* Index into elements[], start of free element list */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * The initial and maximum number of elements in the heap. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static const size_t min_nodes = 8; | 
					
						
							|  |  |  | static const size_t max_nodes = | 
					
						
							|  |  |  |         SIZE_MAX / (sizeof(struct pq_heap_st) > sizeof(struct pq_elem_st) | 
					
						
							|  |  |  |                     ? sizeof(struct pq_heap_st) : sizeof(struct pq_elem_st)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  | /* Some basic sanity checking of the data structure */ | 
					
						
							|  |  |  | # define ASSERT_USED(pq, idx)                                               \
 | 
					
						
							|  |  |  |     assert(pq->elements[pq->heap[idx].index].used);                         \ | 
					
						
							|  |  |  |     assert(pq->elements[pq->heap[idx].index].posn == idx) | 
					
						
							|  |  |  | # define ASSERT_ELEM_USED(pq, elem)                                         \
 | 
					
						
							|  |  |  |     assert(pq->elements[elem].used) | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | # define ASSERT_USED(pq, idx)
 | 
					
						
							|  |  |  | # define ASSERT_ELEM_USED(pq, elem)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Calculate the array growth based on the target size. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The growth factor is a rational number and is defined by a numerator | 
					
						
							|  |  |  |  * and a denominator.  According to Andrew Koenig in his paper "Why Are | 
					
						
							|  |  |  |  * Vectors Efficient?" from JOOP 11(5) 1998, this factor should be less | 
					
						
							|  |  |  |  * than the golden ratio (1.618...). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * We use an expansion factor of 8 / 5 = 1.6 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-16 19:26:20 +08:00
										 |  |  | static ossl_inline size_t compute_pqueue_growth(size_t target, size_t current) | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (current < target) { | 
					
						
							|  |  |  |         if (current >= max_nodes) | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         current = safe_muldiv_size_t(current, 8, 5, &err); | 
					
						
							|  |  |  |         if (err) | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         if (current >= max_nodes) | 
					
						
							|  |  |  |             current = max_nodes; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return current; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ossl_inline void pqueue_swap_elem(OSSL_PQUEUE *pq, size_t i, size_t j) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_heap_st *h = pq->heap, t_h; | 
					
						
							|  |  |  |     struct pq_elem_st *e = pq->elements; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, i); | 
					
						
							|  |  |  |     ASSERT_USED(pq, j); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     t_h = h[i]; | 
					
						
							|  |  |  |     h[i] = h[j]; | 
					
						
							|  |  |  |     h[j] = t_h; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     e[h[i].index].posn = i; | 
					
						
							|  |  |  |     e[h[j].index].posn = j; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ossl_inline void pqueue_move_elem(OSSL_PQUEUE *pq, size_t from, size_t to) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_heap_st *h = pq->heap; | 
					
						
							|  |  |  |     struct pq_elem_st *e = pq->elements; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, from); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     h[to] = h[from]; | 
					
						
							|  |  |  |     e[h[to].index].posn = to; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Force the specified element to the front of the heap.  This breaks | 
					
						
							|  |  |  |  * the heap partial ordering pre-condition. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ossl_inline void pqueue_force_bottom(OSSL_PQUEUE *pq, size_t n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ASSERT_USED(pq, n); | 
					
						
							|  |  |  |     while (n > 0) { | 
					
						
							|  |  |  |         const size_t p = (n - 1) / 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ASSERT_USED(pq, p); | 
					
						
							|  |  |  |         pqueue_swap_elem(pq, n, p); | 
					
						
							|  |  |  |         n = p; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Move an element down to its correct position to restore the partial | 
					
						
							|  |  |  |  * order pre-condition. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ossl_inline void pqueue_move_down(OSSL_PQUEUE *pq, size_t n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_heap_st *h = pq->heap; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, n); | 
					
						
							|  |  |  |     while (n > 0) { | 
					
						
							|  |  |  |         const size_t p = (n - 1) / 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ASSERT_USED(pq, p); | 
					
						
							|  |  |  |         if (pq->compare(h[n].data, h[p].data) >= 0) | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         pqueue_swap_elem(pq, n, p); | 
					
						
							|  |  |  |         n = p; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Move an element up to its correct position to restore the partial | 
					
						
							|  |  |  |  * order pre-condition. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static ossl_inline void pqueue_move_up(OSSL_PQUEUE *pq, size_t n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_heap_st *h = pq->heap; | 
					
						
							|  |  |  |     size_t p = n * 2 + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, n); | 
					
						
							|  |  |  |     if (pq->htop > p + 1) { | 
					
						
							|  |  |  |         ASSERT_USED(pq, p); | 
					
						
							|  |  |  |         ASSERT_USED(pq, p + 1); | 
					
						
							|  |  |  |         if (pq->compare(h[p].data, h[p + 1].data) > 0) | 
					
						
							|  |  |  |             p++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     while (pq->htop > p && pq->compare(h[p].data, h[n].data) < 0) { | 
					
						
							|  |  |  |         ASSERT_USED(pq, p); | 
					
						
							|  |  |  |         pqueue_swap_elem(pq, n, p); | 
					
						
							|  |  |  |         n = p; | 
					
						
							|  |  |  |         p = n * 2 + 1; | 
					
						
							|  |  |  |         if (pq->htop > p + 1) { | 
					
						
							|  |  |  |             ASSERT_USED(pq, p + 1); | 
					
						
							|  |  |  |             if (pq->compare(h[p].data, h[p + 1].data) > 0) | 
					
						
							|  |  |  |                 p++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ossl_pqueue_push(OSSL_PQUEUE *pq, void *data, size_t *elem) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t n, m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ossl_pqueue_reserve(pq, 1)) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     n = pq->htop++; | 
					
						
							|  |  |  |     m = pq->freelist; | 
					
						
							|  |  |  |     pq->freelist = pq->elements[m].posn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pq->heap[n].data = data; | 
					
						
							|  |  |  |     pq->heap[n].index = m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pq->elements[m].posn = n; | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     pq->elements[m].used = 1; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     pqueue_move_down(pq, n); | 
					
						
							|  |  |  |     if (elem != NULL) | 
					
						
							|  |  |  |         *elem = m; | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *ossl_pqueue_peek(const OSSL_PQUEUE *pq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (pq->htop > 0) { | 
					
						
							|  |  |  |         ASSERT_USED(pq, 0); | 
					
						
							|  |  |  |         return pq->heap->data; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *ossl_pqueue_pop(OSSL_PQUEUE *pq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     void *res; | 
					
						
							|  |  |  |     size_t elem; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pq == NULL || pq->htop == 0) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, 0); | 
					
						
							|  |  |  |     res = pq->heap->data; | 
					
						
							|  |  |  |     elem = pq->heap->index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (--pq->htop != 0) { | 
					
						
							|  |  |  |         pqueue_move_elem(pq, pq->htop, 0); | 
					
						
							|  |  |  |         pqueue_move_up(pq, 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pq->elements[elem].posn = pq->freelist; | 
					
						
							|  |  |  |     pq->freelist = elem; | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     pq->elements[elem].used = 0; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *ossl_pqueue_remove(OSSL_PQUEUE *pq, size_t elem) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pq == NULL || elem >= pq->hmax || pq->htop == 0) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_ELEM_USED(pq, elem); | 
					
						
							|  |  |  |     n = pq->elements[elem].posn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ASSERT_USED(pq, n); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (n == pq->htop - 1) | 
					
						
							|  |  |  |         return pq->heap[--pq->htop].data; | 
					
						
							|  |  |  |     if (n > 0) | 
					
						
							|  |  |  |         pqueue_force_bottom(pq, n); | 
					
						
							|  |  |  |     return ossl_pqueue_pop(pq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void pqueue_add_freelist(OSSL_PQUEUE *pq, size_t from) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct pq_elem_st *e = pq->elements; | 
					
						
							|  |  |  |     size_t i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     for (i = from; i < pq->hmax; i++) | 
					
						
							|  |  |  |         e[i].used = 0; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     e[from].posn = pq->freelist; | 
					
						
							|  |  |  |     for (i = from + 1; i < pq->hmax; i++) | 
					
						
							|  |  |  |         e[i].posn = i - 1; | 
					
						
							|  |  |  |     pq->freelist = pq->hmax - 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ossl_pqueue_reserve(OSSL_PQUEUE *pq, size_t n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t new_max, cur_max; | 
					
						
							|  |  |  |     struct pq_heap_st *h; | 
					
						
							|  |  |  |     struct pq_elem_st *e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pq == NULL) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     cur_max = pq->hmax; | 
					
						
							|  |  |  |     if (pq->htop + n < cur_max) | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     new_max = compute_pqueue_growth(n + cur_max, cur_max); | 
					
						
							|  |  |  |     if (new_max == 0) { | 
					
						
							|  |  |  |         ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     h = OPENSSL_realloc(pq->heap, new_max * sizeof(*pq->heap)); | 
					
						
							| 
									
										
										
										
											2022-09-29 19:57:34 +08:00
										 |  |  |     if (h == NULL) | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     pq->heap = h; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     e = OPENSSL_realloc(pq->elements, new_max * sizeof(*pq->elements)); | 
					
						
							| 
									
										
										
										
											2022-09-29 19:57:34 +08:00
										 |  |  |     if (e == NULL) | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     pq->elements = e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pq->hmax = new_max; | 
					
						
							|  |  |  |     pqueue_add_freelist(pq, cur_max); | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OSSL_PQUEUE *ossl_pqueue_new(int (*compare)(const void *, const void *)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OSSL_PQUEUE *pq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (compare == NULL) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pq = OPENSSL_malloc(sizeof(*pq)); | 
					
						
							| 
									
										
										
										
											2022-09-29 19:57:34 +08:00
										 |  |  |     if (pq == NULL) | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     pq->compare = compare; | 
					
						
							|  |  |  |     pq->hmax = min_nodes; | 
					
						
							|  |  |  |     pq->htop = 0; | 
					
						
							|  |  |  |     pq->freelist = 0; | 
					
						
							|  |  |  |     pq->heap = OPENSSL_malloc(sizeof(*pq->heap) * min_nodes); | 
					
						
							|  |  |  |     pq->elements = OPENSSL_malloc(sizeof(*pq->elements) * min_nodes); | 
					
						
							|  |  |  |     if (pq->heap == NULL || pq->elements == NULL) { | 
					
						
							|  |  |  |         ossl_pqueue_free(pq); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pqueue_add_freelist(pq, 0); | 
					
						
							|  |  |  |     return pq; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ossl_pqueue_free(OSSL_PQUEUE *pq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (pq != NULL) { | 
					
						
							|  |  |  |         OPENSSL_free(pq->heap); | 
					
						
							|  |  |  |         OPENSSL_free(pq->elements); | 
					
						
							|  |  |  |         OPENSSL_free(pq); | 
					
						
							| 
									
										
										
										
											2022-06-20 23:11:28 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-12 13:04:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ossl_pqueue_pop_free(OSSL_PQUEUE *pq, void (*freefunc)(void *)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     size_t i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (pq != NULL) { | 
					
						
							|  |  |  |         for (i = 0; i < pq->htop; i++) | 
					
						
							|  |  |  |             (*freefunc)(pq->heap[i].data); | 
					
						
							|  |  |  |         ossl_pqueue_free(pq); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | size_t ossl_pqueue_num(const OSSL_PQUEUE *pq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return pq != NULL ? pq->htop : 0; | 
					
						
							|  |  |  | } |