/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*- * vim: ts=4 sw=4 noet ai cindent syntax=c * * 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 * * Initially based on the former implementation of sorted processes in * top.c, Copyright (C) 2005 David Carter * * 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 . * */ #include "conky.h" #include /* INT_MAX */ #include #include 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(void) { struct prio_queue *retval; retval = (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) queue->compare = pqcompare; } void pq_set_free(struct prio_queue *queue, void (*pqfree)(void *a)) { if (pqfree) 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 = (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) return; /* empty queue, insert the first item */ if (!queue->cur_size) { 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; 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 NULL; tmp = queue->head; data = tmp->data; queue->head = queue->head->next; queue->cur_size--; if (queue->head) queue->head->prev = NULL; else /* list is now empty */ queue->tail = NULL; free(tmp); return data; } void free_prio_queue(struct prio_queue *queue) { void *data; while((data = pop_prio_elem(queue))) (*queue->free)(data); free(queue); }