mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 10:52:51 +00:00
468 lines
9.2 KiB
C
468 lines
9.2 KiB
C
/*
|
|
Lightweight C Container Library
|
|
|
|
Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
|
|
|
|
Original code was written by Chris Schlaeger <cs@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include "ccont.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
struct container_info {
|
|
INDEX count;
|
|
CONTAINER currentNode;
|
|
};
|
|
|
|
typedef struct container_info T_CONTAINER_INFO;
|
|
typedef struct container_info* CONTAINER_INFO;
|
|
|
|
#define rpterr(x) fprintf(stderr, "%s\n", x)
|
|
|
|
CONTAINER new_ctnr(void)
|
|
{
|
|
CONTAINER_INFO info;
|
|
CONTAINER rootNode;
|
|
|
|
rootNode = (CONTAINER)malloc(sizeof(T_CONTAINER));
|
|
|
|
info = (CONTAINER_INFO)malloc(sizeof(T_CONTAINER_INFO));
|
|
info->count = 0;
|
|
info->currentNode = rootNode;
|
|
|
|
rootNode->next = rootNode;
|
|
rootNode->prev = rootNode;
|
|
rootNode->data = info;
|
|
|
|
return rootNode;
|
|
}
|
|
|
|
/* O(n) */
|
|
void zero_destr_ctnr(CONTAINER rootNode, DESTR_FUNC destr_func)
|
|
{
|
|
INDEX counter;
|
|
|
|
if (rootNode == NIL || destr_func == NIL) {
|
|
rpterr("destr_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
for (counter = level_ctnr(rootNode); counter > -1; --counter)
|
|
destr_func(pop_ctnr(rootNode));
|
|
|
|
assert(rootNode->data);
|
|
free(rootNode->data);
|
|
|
|
free(rootNode);
|
|
rootNode = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/* O(n) */
|
|
void empty_ctnr(CONTAINER rootNode)
|
|
{
|
|
INDEX i;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("empty_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
for ( i = level_ctnr( rootNode ); i >= 0; --i )
|
|
free( pop_ctnr( rootNode ) );
|
|
|
|
return;
|
|
}
|
|
|
|
/* O(1) */
|
|
INDEX level_ctnr(CONTAINER rootNode)
|
|
{
|
|
if (rootNode == NIL) {
|
|
rpterr("level_ctnr: NIL argument");
|
|
return -1;
|
|
}
|
|
|
|
return ((CONTAINER_INFO)rootNode->data)->count;
|
|
}
|
|
|
|
/* This function is never used */
|
|
/* O(n) */
|
|
void insert_ctnr(CONTAINER rootNode, void* object, INDEX pos)
|
|
{
|
|
CONTAINER it;
|
|
INDEX counter = 0;
|
|
|
|
if (rootNode == NIL || object == NIL) {
|
|
rpterr("insert_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
for (it = rootNode->next; it != rootNode; it = it->next) {
|
|
if (counter == pos) {
|
|
CONTAINER newNode = (CONTAINER)malloc(sizeof(T_CONTAINER));
|
|
|
|
newNode->prev = it;
|
|
newNode->next = it->next;
|
|
it->next->prev = newNode;
|
|
it->next = newNode;
|
|
|
|
newNode->data = object;
|
|
((CONTAINER_INFO)rootNode->data)->count++;
|
|
return;
|
|
}
|
|
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
/* O(1) */
|
|
void push_ctnr(CONTAINER rootNode, void* object)
|
|
{
|
|
CONTAINER newNode;
|
|
|
|
if (rootNode == NIL || object == NIL) {
|
|
rpterr("push_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
newNode = (CONTAINER)malloc(sizeof(T_CONTAINER));
|
|
newNode->next = rootNode;
|
|
newNode->prev = rootNode->prev;
|
|
rootNode->prev->next = newNode;
|
|
rootNode->prev = newNode;
|
|
|
|
newNode->data = object;
|
|
((CONTAINER_INFO)rootNode->data)->count++;
|
|
}
|
|
|
|
/* This function is never used */
|
|
/* O(n) */
|
|
void* remove_at_ctnr(CONTAINER rootNode, INDEX pos)
|
|
{
|
|
CONTAINER it;
|
|
INDEX counter = 0;
|
|
void* retval;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("remove_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
for (it = rootNode->next; it != rootNode; it = it->next) {
|
|
if (counter == pos) {
|
|
retval = it->data;
|
|
|
|
it->prev->next = it->next;
|
|
it->next->prev = it->prev;
|
|
|
|
free(it);
|
|
|
|
((CONTAINER_INFO)rootNode->data)->count--;
|
|
return retval;
|
|
}
|
|
|
|
counter++;
|
|
}
|
|
|
|
return NIL;
|
|
}
|
|
|
|
/* This function is only used within ccont.c */
|
|
/* O(1) */
|
|
void* pop_ctnr(CONTAINER rootNode)
|
|
{
|
|
CONTAINER ptr;
|
|
void* retval;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("pop_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
if (rootNode->next == rootNode)
|
|
return NIL;
|
|
|
|
ptr = rootNode->next;
|
|
retval = ptr->data;
|
|
|
|
rootNode->next = ptr->next;
|
|
ptr->next->prev = rootNode;
|
|
|
|
((CONTAINER_INFO)rootNode->data)->count--;
|
|
|
|
free(ptr);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* O(n) */
|
|
void* get_ctnr(CONTAINER rootNode, INDEX pos)
|
|
{
|
|
CONTAINER it;
|
|
INDEX counter = 0;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("get_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
for (it = rootNode->next; it != rootNode; it = it->next) {
|
|
if (counter == pos)
|
|
return it->data;
|
|
|
|
counter++;
|
|
}
|
|
|
|
return NIL;
|
|
}
|
|
|
|
/* This should return a reference to the node found instead of its index.
|
|
The index is never used, except as an argument to a O(n) function to
|
|
get a reference to the node. */
|
|
/* O(n) */
|
|
INDEX search_ctnr(CONTAINER rootNode, COMPARE_FUNC compare_func, void* pattern)
|
|
{
|
|
CONTAINER it;
|
|
INDEX counter = 0;
|
|
|
|
if (rootNode == NIL || compare_func == NIL || pattern == NIL) {
|
|
rpterr("search_ctnr: NIL argument");
|
|
return -1;
|
|
}
|
|
|
|
for (it = rootNode->next; it != rootNode; it = it->next) {
|
|
if (compare_func(pattern, it->data) == 0)
|
|
return counter;
|
|
|
|
counter++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* This function is never used */
|
|
/* O(n) */
|
|
void swap_ctnr(CONTAINER rootNode, INDEX pos1, INDEX pos2)
|
|
{
|
|
CONTAINER it, node1 = 0, node2 = 0;
|
|
INDEX counter = 0;
|
|
int found = 0;
|
|
void* tmpData;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("swap_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
if (pos1 == pos2)
|
|
return;
|
|
|
|
for (it = rootNode->next; it != rootNode; it = it->next) {
|
|
if (counter == pos1) {
|
|
node1 = it;
|
|
found++;
|
|
if (found == 2)
|
|
break;
|
|
}
|
|
if (counter == pos2) {
|
|
node2 = it;
|
|
found++;
|
|
if (found == 2)
|
|
break;
|
|
}
|
|
|
|
counter++;
|
|
}
|
|
|
|
if (found == 2) {
|
|
tmpData = node1->data;
|
|
node1->data = node2->data;
|
|
node2->data = tmpData;
|
|
}
|
|
}
|
|
|
|
/* O(n log n) */
|
|
void bsort_ctnr(CONTAINER rootNode, COMPARE_FUNC compare_func)
|
|
{
|
|
/* Use mergesort adapted from http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.c */
|
|
|
|
CONTAINER p, q, e, tail, oldhead;
|
|
INDEX insize, nmerges, psize, qsize, i;
|
|
|
|
if (rootNode == NIL || compare_func == NIL) {
|
|
rpterr("bsort_ctnr: NIL argument");
|
|
return;
|
|
}
|
|
|
|
if (level_ctnr(rootNode) == 0) {
|
|
/* Nothing to sort */
|
|
return;
|
|
}
|
|
|
|
insize = 1;
|
|
|
|
while (1) {
|
|
p = rootNode->next;
|
|
oldhead = rootNode; /* only used for circular linkage */
|
|
rootNode->next = NULL;
|
|
tail = NULL;
|
|
|
|
nmerges = 0; /* count number of merges we do in this pass */
|
|
|
|
while (p) {
|
|
nmerges++; /* there exists a merge to be done */
|
|
/* step `insize' places along from p */
|
|
q = p;
|
|
psize = 0;
|
|
for (i = 0; i < insize; i++) {
|
|
psize++;
|
|
q = (q->next == oldhead ? NULL : q->next);
|
|
if (!q) break;
|
|
}
|
|
|
|
/* if q hasn't fallen off end, we have two lists to merge */
|
|
qsize = insize;
|
|
|
|
/* now we have two lists; merge them */
|
|
while (psize > 0 || (qsize > 0 && q)) {
|
|
/* decide whether next element of merge comes from p or q */
|
|
if (psize == 0) {
|
|
assert(q);
|
|
/* p is empty; e must come from q. */
|
|
e = q; q = q->next; qsize--;
|
|
if (q == oldhead) q = NULL;
|
|
} else if (qsize == 0 || !q) {
|
|
assert(p);
|
|
/* q is empty; e must come from p. */
|
|
e = p; p = p->next; psize--;
|
|
if (p == oldhead) p = NULL;
|
|
} else if (compare_func(p,q) <= 0) {
|
|
/* First element of p is lower (or same);
|
|
* e must come from p. */
|
|
e = p; p = p->next; psize--;
|
|
if (p == oldhead) p = NULL;
|
|
} else {
|
|
/* First element of q is lower; e must come from q. */
|
|
e = q; q = q->next; qsize--;
|
|
if (q == oldhead) q = NULL;
|
|
}
|
|
|
|
/* add the next element to the merged list */
|
|
if (tail) {
|
|
tail->next = e;
|
|
e->prev = tail;
|
|
} else {
|
|
rootNode->next = e;
|
|
e->prev = rootNode;
|
|
}
|
|
|
|
tail = e;
|
|
}
|
|
|
|
/* now p has stepped `insize' places along, and q has too */
|
|
p = q;
|
|
}
|
|
|
|
if (tail)
|
|
tail->next = rootNode;
|
|
rootNode->prev = tail;
|
|
|
|
/* If we have done only one merge, we're finished. */
|
|
if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
|
|
break;
|
|
}
|
|
|
|
/* Otherwise repeat, merging lists twice the size */
|
|
insize *= 2;
|
|
}
|
|
}
|
|
|
|
/* O(1) */
|
|
void* first_ctnr(CONTAINER rootNode)
|
|
{
|
|
if (rootNode == NIL) {
|
|
rpterr("first_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
if (rootNode->next == rootNode)
|
|
return NIL;
|
|
|
|
((CONTAINER_INFO)rootNode->data)->currentNode = rootNode->next;
|
|
|
|
return rootNode->next->data;
|
|
}
|
|
|
|
/* O(1) */
|
|
void* next_ctnr(CONTAINER rootNode)
|
|
{
|
|
CONTAINER_INFO info;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("next_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
info = (CONTAINER_INFO)rootNode->data;
|
|
|
|
if (info->currentNode->next == rootNode)
|
|
return NIL;
|
|
|
|
info->currentNode = info->currentNode->next;
|
|
|
|
return info->currentNode->data;
|
|
}
|
|
|
|
/* O(1) */
|
|
void* remove_ctnr(CONTAINER rootNode)
|
|
{
|
|
CONTAINER currentNode, tmp;
|
|
CONTAINER_INFO info;
|
|
void* retval;
|
|
|
|
if (rootNode == NIL) {
|
|
rpterr("remove_curr_ctnr: NIL argument");
|
|
return NIL;
|
|
}
|
|
|
|
info = (CONTAINER_INFO)rootNode->data;
|
|
currentNode = info->currentNode;
|
|
|
|
if (currentNode == rootNode) { /* should never happen */
|
|
rpterr("remove_curr_ctnr: delete root node");
|
|
return NIL;
|
|
}
|
|
|
|
retval = currentNode->data;
|
|
tmp = currentNode->prev;
|
|
|
|
currentNode->prev->next = currentNode->next;
|
|
currentNode->next->prev = currentNode->prev;
|
|
|
|
free(currentNode);
|
|
|
|
info->count--;
|
|
info->currentNode = tmp;
|
|
|
|
return retval;
|
|
}
|