/
prioqueue.cc
192 lines (159 loc) · 5.53 KB
/
prioqueue.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
*
* prioqueue: a simple priority queue implementation
*
* The queue organises it's data internally using a doubly linked
* list, into which elements are inserted at the right position. This
* is definitely not the best algorithm for a priority queue, but it
* fits best for the given purpose, i.e. the top process sorting.
* This means we have a rather little amount of total elements (~200
* on a normal system), which are to be inserted into a queue of only
* the few top-most elements (10 at the current state). Additionally,
* at each update interval, the queue is drained completely and
* refilled from scratch.
*
* Copyright (C) 2009 Phil Sutter <phil@nwl.cc>
*
* Initially based on the former implementation of sorted processes in
* top.c, Copyright (C) 2005 David Carter <boojit@pundo.com>
*
* This program 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <climits> /* INT_MAX */
#include <cstdlib>
#include <cstring>
#include "conky.h"
struct prio_elem {
struct prio_elem *next, *prev;
void *data;
};
struct prio_queue {
/* Compare a and b. Return:
* <0 if a should come before b,
* >0 if b should come before a,
* 0 if don't care */
int (*compare)(void *a, void *b);
/* Free element payload. Called when dropping elements. */
void (*free)(void *a);
/* Maximum size of queue. The first
* elements in the list take precedence. */
int max_size;
/* The pointers to the actual list. */
struct prio_elem *head, *tail;
/* The current number of elements in the list. */
int cur_size;
};
/* nop callback to save us from conditional calling */
static void pq_free_nop(void *a) { (void)a; }
struct prio_queue *init_prio_queue() {
struct prio_queue *retval;
retval = static_cast<struct prio_queue *>(malloc(sizeof(struct prio_queue)));
memset(retval, 0, sizeof(struct prio_queue));
/* use pq_free_nop by default */
retval->free = &pq_free_nop;
/* Default to maximum possible size as restricted
* by the used data type. This also saves us from
* checking if caller has set this field or not. */
retval->max_size = INT_MAX;
return retval;
}
void pq_set_compare(struct prio_queue *queue,
int (*pqcompare)(void *a, void *b)) {
if (pqcompare != nullptr) { queue->compare = pqcompare; }
}
void pq_set_free(struct prio_queue *queue, void (*pqfree)(void *a)) {
if (pqfree != nullptr) { queue->free = pqfree; }
}
void pq_set_max_size(struct prio_queue *queue, int max_size) {
if (max_size >= 0) { queue->max_size = max_size; }
}
int pq_get_cur_size(struct prio_queue *queue) { return queue->cur_size; }
static struct prio_elem *init_prio_elem(void *data) {
struct prio_elem *retval;
retval = static_cast<struct prio_elem *>(malloc(sizeof(struct prio_elem)));
memset(retval, 0, sizeof(struct prio_elem));
retval->data = data;
return retval;
}
void insert_prio_elem(struct prio_queue *queue, void *data) {
struct prio_elem *cur;
/* queue->compare is a must-have */
if (queue->compare == nullptr) { return; }
/* empty queue, insert the first item */
if (queue->cur_size == 0) {
queue->cur_size++;
queue->head = queue->tail = init_prio_elem(data);
return;
}
/* short-cut 1: new item is lower than all others */
if (queue->compare(queue->tail->data, data) <= 0) {
if (queue->cur_size < queue->max_size) {
queue->cur_size++;
queue->tail->next = init_prio_elem(data);
queue->tail->next->prev = queue->tail;
queue->tail = queue->tail->next;
} else { /* list was already full */
(*queue->free)(data);
}
return;
}
/* short-cut 2: we have a new maximum */
if (queue->compare(queue->head->data, data) >= 0) {
queue->cur_size++;
queue->head->prev = init_prio_elem(data);
queue->head->prev->next = queue->head;
queue->head = queue->head->prev;
goto check_cur_size;
}
/* find the actual position if short-cuts failed */
for (cur = queue->head->next; cur != nullptr; cur = cur->next) {
if (queue->compare(cur->data, data) >= 0) {
queue->cur_size++;
cur->prev->next = init_prio_elem(data);
cur->prev->next->prev = cur->prev;
cur->prev->next->next = cur;
cur->prev = cur->prev->next;
break;
}
}
check_cur_size:
/* drop the lowest item if queue overrun */
if (queue->cur_size > queue->max_size) {
queue->cur_size--;
queue->tail = queue->tail->prev;
(*queue->free)(queue->tail->next->data);
free_and_zero(queue->tail->next);
}
}
void *pop_prio_elem(struct prio_queue *queue) {
struct prio_elem *tmp;
void *data;
if (queue->cur_size <= 0) { return nullptr; }
tmp = queue->head;
data = tmp->data;
queue->head = queue->head->next;
queue->cur_size--;
if (queue->head != nullptr) {
queue->head->prev = nullptr;
} else { /* list is now empty */
queue->tail = nullptr;
}
free(tmp);
return data;
}
void free_prio_queue(struct prio_queue *queue) {
void *data;
while ((data = pop_prio_elem(queue)) != nullptr) { (*queue->free)(data); }
free(queue);
}