[Freeciv-Dev] (PR#11990) New genlist code
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[Freeciv-Dev] (PR#11990) New genlist code |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Sat, 22 Jan 2005 11:59:33 -0800 |
Reply-to: |
bugs@xxxxxxxxxxx |
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=11990 >
Now that the API change has been done, the new genlist code itself is a
rather small patch. Here it is.
But before I commit this, I want to make section lookups in registry.c
into a hashtable lookup instead of a genlist search.
- Per
Index: client/helpdata.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/helpdata.c,v
retrieving revision 1.94
diff -u -r1.94 helpdata.c
--- client/helpdata.c 22 Jan 2005 19:45:39 -0000 1.94
+++ client/helpdata.c 22 Jan 2005 19:56:42 -0000
@@ -468,7 +468,7 @@
void help_iter_start(void)
{
check_help_nodes_init();
- help_nodes_iterator = help_nodes->list->head_link;
+ help_nodes_iterator = help_nodes->list->head;
}
/****************************************************************
Index: utility/genlist.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/utility/genlist.c,v
retrieving revision 1.17
diff -u -r1.17 genlist.c
--- utility/genlist.c 22 Jan 2005 19:45:45 -0000 1.17
+++ utility/genlist.c 22 Jan 2005 19:56:42 -0000
@@ -1,265 +1,289 @@
-/**********************************************************************
- Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+/**********************************************************************
+ Freeciv - Copyright (C) 2003 - The Freeciv Team
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 2, or (at your option)
- any later version.
-
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1, 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.
+ GNU Lesser General Public License for more details.
***********************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <assert.h>
#include <stdlib.h>
+#include "log.h"
#include "mem.h"
+#include "shared.h"
+#include "support.h"
#include "genlist.h"
-static struct genlist_link *find_genlist_position(const struct genlist
*pgenlist,
- int pos);
-
-/************************************************************************
- Create a new empty genlist.
-************************************************************************/
-struct genlist *genlist_new(void)
-{
- struct genlist *pgenlist = fc_malloc(sizeof(*pgenlist));
+/* Tracker is a list of pointers to lists that we will prune when
+ * the time is right. */
+static struct genlist *tracker;
- pgenlist->nelements=0;
- pgenlist->head_link = NULL;
- pgenlist->tail_link = NULL;
+static void genlist_prune(struct genlist *plist);
- return pgenlist;
+/**************************************************************************
+ Initialize genlist module.
+**************************************************************************/
+void genlists_init()
+{
+ tracker = genlist_new();
}
-/************************************************************************
- Remove a genlist. The list must be empty first!
-************************************************************************/
-void genlist_free(struct genlist *pgenlist)
+/**************************************************************************
+ Quit genlist module. Returns FALSE if memory was not cleaned up
+ properly.
+**************************************************************************/
+void genlists_free()
{
- free(pgenlist);
+ genlist_free(tracker);
}
-/************************************************************************
- Returns the number of elements stored in the genlist.
-************************************************************************/
-int genlist_size(const struct genlist *pgenlist)
+/**************************************************************************
+ Create a genlist and insert it into the genlist tracker.
+**************************************************************************/
+struct genlist *genlist_new()
{
- return pgenlist->nelements;
-}
+ struct genlist *plist = calloc(sizeof(struct genlist), 1);
+ plist->nelements = 0;
+ plist->ndeleted = 0;
+ plist->head = NULL;
+ plist->tail = NULL;
+ plist->track = NULL;
-/************************************************************************
- Returns the user-data pointer stored in the genlist at the position
- given by 'idx'. For idx out of range (including an empty list),
- returns NULL.
- Recall 'idx' can be -1 meaning the last element.
-************************************************************************/
-void *genlist_get(const struct genlist *pgenlist, int idx)
-{
- struct genlist_link *link=find_genlist_position(pgenlist, idx);
+ if (tracker) {
+ struct genlist_link *plink;
- if (link) {
- return link->dataptr;
- } else {
- return NULL;
- }
-}
+ /* Insert list into tracker, unless we _are_ the tracker. */
+ genlist_prepend(tracker, plist);
+ /* Find our position in the tracker */
+ for (plink = tracker->head; plink->dataptr != plist; plink = plink->next);
-/************************************************************************
- Frees all the internal data used by the genlist (but doesn't touch
- the user-data). At the end the state of the genlist will be the
- same as when genlist_init() is called on a new genlist.
-************************************************************************/
-void genlist_unlink_all(struct genlist *pgenlist)
-{
- if(pgenlist->nelements > 0) {
- struct genlist_link *plink=pgenlist->head_link, *plink2;
+ /* Point plist->track at ourselves in the tracker */
+ plist->track = plink;
+ }
- do {
- plink2=plink->next;
- free(plink);
- } while ((plink = plink2) != NULL);
+ return plist;
+}
- pgenlist->head_link = NULL;
- pgenlist->tail_link = NULL;
+/**************************************************************************
+ Get data from given position. -1 gives us last position.
+**************************************************************************/
+void *genlist_get(const struct genlist *plist, int idx)
+{
+ struct genlist_link *plink = plist->head;
+ int i = 0;
- pgenlist->nelements=0;
+ if (idx == -1) {
+ idx = genlist_size(plist);
}
-}
+ if (!plist->head || genlist_size(plist) == 0) {
+ /* List is empty */
+ return NULL;
+ } else if (idx == genlist_size(plist) && !plist->tail->deleted) {
+ /* Last item */
+ return plist->tail->dataptr;
+ } else if (idx > genlist_size(plist) || idx < -1) {
+ /* Trying to fetch out of bounds item */
+ return NULL;
+ }
-/************************************************************************
- Remove an element of the genlist with the specified user-data pointer
- given by 'punlink'. If there is no such element, does nothing.
- If there are multiple such elements, removes the first one.
-************************************************************************/
-void genlist_unlink(struct genlist *pgenlist, void *punlink)
-{
- if(pgenlist->nelements > 0) {
- struct genlist_link *plink=pgenlist->head_link;
-
- while (plink != NULL && plink->dataptr != punlink) {
- plink = plink->next;
+ /* Ok, we have to search */
+ for (;; plink = plink->next) {
+ if (plink->deleted) {
+ continue;
}
-
- if (plink) {
- if(pgenlist->head_link==plink)
- pgenlist->head_link=plink->next;
- else
- plink->prev->next=plink->next;
-
- if(pgenlist->tail_link==plink)
- pgenlist->tail_link=plink->prev;
- else
- plink->next->prev=plink->prev;
- free(plink);
- pgenlist->nelements--;
+ if (i == idx) {
+ break;
}
+ i++;
}
+ return plink->dataptr;
}
-
-/************************************************************************
- Insert a new element in the list, at position 'pos', with the specified
- user-data pointer 'data'. Existing elements at >= pos are moved one
- space to the "right". Recall 'pos' can be -1 meaning add at the
- end of the list. For an empty list pos has no effect.
- A bad 'pos' value for a non-empty list is treated as -1 (is this
- a good idea?)
-************************************************************************/
-static void genlist_insert(struct genlist *pgenlist, void *data, int pos)
-{
- if(pgenlist->nelements == 0) { /*list is empty, ignore pos */
-
- struct genlist_link *plink = fc_malloc(sizeof(*plink));
-
- plink->dataptr=data;
- plink->next = NULL;
- plink->prev = NULL;
-
- pgenlist->head_link=plink;
- pgenlist->tail_link=plink;
-
- }
- else {
- struct genlist_link *plink = fc_malloc(sizeof(*plink));
- plink->dataptr=data;
-
- if(pos==0) {
- plink->next=pgenlist->head_link;
- plink->prev = NULL;
- pgenlist->head_link->prev=plink;
- pgenlist->head_link=plink;
- }
- else if(pos<=-1 || pos>=pgenlist->nelements) {
- plink->next = NULL;
- plink->prev=pgenlist->tail_link;
- pgenlist->tail_link->next=plink;
- pgenlist->tail_link=plink;
- }
- else {
- struct genlist_link *left, *right; /* left and right of new element
*/
- right = find_genlist_position(pgenlist, pos);
- left = right->prev;
- plink->next = right;
- plink->prev = left;
- right->prev = plink;
- left->next = plink;
- }
+/**************************************************************************
+ Free list. Does not touch data inside the list. Calling this function
+ while such data is still in the list is an error.
+**************************************************************************/
+void genlist_free(struct genlist *plist)
+{
+ /* Remove our entry in the tracker list. */
+ if (plist->track) {
+ plist->track->deleted = TRUE;
+ tracker->nelements--;
}
+ /* assert(plist->nelements == 0); */
- pgenlist->nelements++;
+ free(plist);
}
-
-/************************************************************************
- Insert an item at the start of the list.
-************************************************************************/
-void genlist_prepend(struct genlist *pgenlist, void *data)
+/**************************************************************************
+ Remove first link from the list where the data pointers match.
+**************************************************************************/
+void genlist_unlink(struct genlist *plist, void *punlink)
{
- genlist_insert(pgenlist, data, 0); /* beginning */
+ struct genlist_link *next = plist->head;
+
+ while (next) {
+ if (!next->deleted && next->dataptr == punlink) {
+ next->deleted = TRUE;
+ plist->nelements--;
+ plist->ndeleted++;
+ break;
+ }
+ next = next->next;
+ }
}
+/**************************************************************************
+ Empty the list.
+**************************************************************************/
+void genlist_unlink_all(struct genlist *plist)
+{
+ struct genlist_link *next = plist->head;
+
+ while (next) {
+ if (!next->deleted) {
+ next->deleted = TRUE;
+ plist->nelements--;
+ plist->ndeleted++;
+ }
+ next = next->next;
+ }
+ assert(plist->nelements == 0);
+}
-/************************************************************************
- Insert an item at the end of the list.
-************************************************************************/
-void genlist_append(struct genlist *pgenlist, void *data)
-{
- genlist_insert(pgenlist, data, -1); /* end */
+/**************************************************************************
+ Insert data at beginning of list.
+**************************************************************************/
+void genlist_prepend(struct genlist *plist, void *data)
+{
+ struct genlist_link *new_link = fc_malloc(sizeof(struct genlist_link));
+
+ new_link->dataptr = data;
+ new_link->deleted = FALSE;
+ new_link->next = plist->head;
+ plist->head = new_link;
+ if (plist->tail == NULL) {
+ plist->tail = new_link;
+ }
+ plist->nelements++;
}
-/************************************************************************
- Returns a pointer to the genlist link structure at the specified
- position. Recall 'pos' -1 refers to the last position.
- For pos out of range returns NULL.
- Traverses list either forwards or backwards for best efficiency.
-************************************************************************/
-static struct genlist_link *find_genlist_position(const struct genlist
*pgenlist,
- int pos)
+/**************************************************************************
+ Insert data at end of list.
+**************************************************************************/
+void genlist_append(struct genlist *plist, void *data)
{
- struct genlist_link *plink;
+ struct genlist_link *new_link = fc_malloc(sizeof(struct genlist_link));
- if (pos == 0) {
- return pgenlist->head_link;
- } else if (pos == -1) {
- return pgenlist->tail_link;
- } else if (pos < -1 || pos >= pgenlist->nelements) {
- return NULL;
+ new_link->dataptr = data;
+ new_link->deleted = FALSE;
+ new_link->next = NULL;
+ if (plist->tail) {
+ plist->tail->next = new_link;
+ } else {
+ plist->head = new_link;
}
+ plist->tail = new_link;
+ plist->nelements++;
+}
- if(pos<pgenlist->nelements/2) /* fastest to do forward search */
- for(plink=pgenlist->head_link; pos != 0; pos--)
- plink=plink->next;
-
- else /* fastest to do backward search */
- for(plink=pgenlist->tail_link,pos=pgenlist->nelements-pos-1; pos != 0;
pos--)
- plink=plink->prev;
-
- return plink;
+/**************************************************************************
+ Free all unused memory in all genlists.
+**************************************************************************/
+void genlists_prune()
+{
+ struct genlist_link *plink = tracker->head;
+
+ for (; plink != NULL; plink = plink->next) {
+ if (!plink->deleted
+ && plink->dataptr != NULL
+ && ((struct genlist *)(plink->dataptr))->ndeleted > 0) {
+ genlist_prune(plink->dataptr);
+ }
+ }
+ genlist_prune(tracker);
}
+/**************************************************************************
+ Free unused memory. Call some time when we are certain that no calling
+ code uses the list.
+**************************************************************************/
+static void genlist_prune(struct genlist *plist)
+{
+ struct genlist_link *plink = plist->head;
+ struct genlist_link *plink_prev = NULL;
+
+ while (plink && plist->ndeleted > 0) {
+ if (plink->deleted) {
+ struct genlist_link *remove = plink;
+
+ if (plist->head == plink) {
+ plist->head = plink->next;
+ }
+ if (plist->tail == plink) {
+ plist->tail = plink_prev;
+ }
+ if (plink_prev) {
+ plink_prev->next = plink->next;
+ }
+ plink = plink->next;
+ free(remove);
+ plist->ndeleted--;
+ } else {
+ plink_prev = plink;
+ plink = plink->next;
+ }
+ }
+}
-/************************************************************************
- Sort the elements of a genlist.
-
- The comparison function should be a function usable by qsort; note
- that the const void * arguments to compar should really be "pointers to
- void*", where the void* being pointed to are the genlist dataptrs.
- That is, there are two levels of indirection.
- To do the sort we first construct an array of pointers corresponding
- the the genlist dataptrs, then sort those and put them back into
- the genlist.
-************************************************************************/
-void genlist_sort(struct genlist *pgenlist,
- int (*compar)(const void *, const void *))
-{
- const int n = genlist_size(pgenlist);
- void *sortbuf[n];
- struct genlist_link *myiter;
+/**************************************************************************
+ Generic sort function. Hand over a comparison function that can be
+ used by qsort. The arguments to this helper function are pointers to
+ void*, which is a pointer to the list dataptr. Note, two levels of
+ indirection.
+**************************************************************************/
+void genlist_sort(struct genlist *plist, int (*compar)(const void *,
+ const void *))
+{
+ struct genlist_link *plink = plist->head;
int i;
+ void *buffer[genlist_size(plist)];
+
+ if (compar == NULL || !plink || !plink->next || genlist_size(plist) <= 1) {
+ return; /* Nothing to do */
+ }
- if (n <= 1) {
- return;
+ /* Copy pointers into buffer */
+ for (i = 0; plink; plink = plink->next) {
+ if (!plink->deleted) {
+ buffer[i] = plink->dataptr;
+ i++;
+ }
}
- myiter = find_genlist_position(pgenlist, 0);
- for(i=0; i<n; i++, ITERATOR_NEXT(myiter)) {
- sortbuf[i] = ITERATOR_PTR(myiter);
- }
-
- qsort(sortbuf, n, sizeof(*sortbuf), compar);
-
- myiter = find_genlist_position(pgenlist, 0);
- for(i=0; i<n; i++, ITERATOR_NEXT(myiter)) {
- myiter->dataptr = sortbuf[i];
+ /* Sort the array */
+ qsort(&buffer, genlist_size(plist), sizeof(void*), compar);
+
+ /* Copy pointers back from buffer */
+ plink = plist->head;
+ for (i = 0; plink; plink = plink->next) {
+ if (!plink->deleted) {
+ plink->dataptr = buffer[i];
+ i++;
+ }
}
}
Index: utility/genlist.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/utility/genlist.h,v
retrieving revision 1.13
diff -u -r1.13 genlist.h
--- utility/genlist.h 22 Jan 2005 19:45:45 -0000 1.13
+++ utility/genlist.h 22 Jan 2005 19:56:42 -0000
@@ -1,116 +1,81 @@
-/**********************************************************************
- Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+/**********************************************************************
+ Freeciv - Copyright (C) 2003 - The Freeciv Team
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 2, or (at your option)
- any later version.
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1, 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.
+ GNU Lesser General Public License for more details.
***********************************************************************/
#ifndef FC__GENLIST_H
#define FC__GENLIST_H
-/**********************************************************************
- MODULE: genlist
+#undef DEBUG_LISTS
+#ifdef DEBUG_LISTS
+#define DEBUG_INFO(p) __FILE__, __LINE__
+#else
+#define DEBUG_INFO(p)
+#endif
- A "genlist" is a generic doubly-linked list. That is:
- generic: stores (void*) pointers to arbitrary user data;
- doubly-linked: can be efficiently traversed both "forwards"
- and "backwards".
-
- The list data structures are allocated dynamically, and list
- elements can be added or removed at arbitrary positions.
-
- Positions in the list are specified starting from 0, up to n-1
- for a list with n elements. The position -1 can be used to
- refer to the last element (that is, the same as n-1, or n when
- adding a new element), but other negative numbers are not
- meaningful.
-
- There are two memory management issues:
-
- - The user-data pointed to by the genlist elements; these are
- entirely the user's responsibility, and the genlist code just
- treats these as opaque data, not doing any allocation or freeing.
-
- - The data structures used internally by the genlist to store
- data for the links etc. These are allocated by genlist_insert(),
- and freed by genlist_unlink() and genlist_unlink_all(). That is,
- it is the responsibility of the user to call the unlink functions
- as necessary to avoid memory leaks.
-
- A trap to beware of with iterators is modifying the list while the
- iterator is active, in particular removing the next element pointed
- to by the iterator (see further comments below).
+#include "shared.h"
- See also the speclist module.
-***********************************************************************/
-
-/* A single element of a genlist, storing the pointer to user
- data, and pointers to the next and previous elements:
-*/
+/* A single list link. The last link's next is NULL. */
struct genlist_link {
- struct genlist_link *next, *prev;
+ struct genlist_link *next;
void *dataptr;
+ bool deleted;
};
-
-/* A genlist, storing the number of elements (for quick retrieval and
- testing for empty lists), and pointers to the first and last elements
- of the list.
-*/
+/* The nelements parameter contains the number of links in the list, while
+ * head and tail point to first and last link, respectively. The track
+ * variable points to our entry in the tracker, if we are there. */
struct genlist {
int nelements;
- struct genlist_link *head_link;
- struct genlist_link *tail_link;
+ int ndeleted;
+ struct genlist_link *head;
+ struct genlist_link *tail;
+ struct genlist_link *track;
};
-int genlist_size(const struct genlist *pgenlist);
-void *genlist_get(const struct genlist *pgenlist, int idx);
-struct genlist *genlist_new(void);
-void genlist_unlink_all(struct genlist *pgenlist);
-void genlist_free(struct genlist *pgenlist);
-void genlist_append(struct genlist *pgenlist, void *data);
-void genlist_prepend(struct genlist *pgenlist, void *data);
-void genlist_unlink(struct genlist *pgenlist, void *punlink);
+/* Operations on all lists */
+void genlists_init(void);
+void genlists_free(void);
+void genlists_prune(void);
-void genlist_sort(struct genlist *pgenlist,
- int (*compar)(const void *, const void *));
+/* Operations on a single list */
+struct genlist *genlist_new(void);
+#define genlist_size(plist) ((plist)->nelements)
+void *genlist_get(const struct genlist *plist, int idx);
+void genlist_prepend(struct genlist *plist, void *data);
+void genlist_append(struct genlist *plist, void *data);
+void genlist_unlink(struct genlist *plist, void *punlink);
+void genlist_unlink_all(struct genlist *plist);
+void genlist_free(struct genlist *plist);
+void genlist_sort(struct genlist *plist,
+ int (*compar)(const void *, const void *));
-#define ITERATOR_PTR(iter) ((iter) ? ((iter)->dataptr) : NULL)
+#define ITERATOR_PTR(iter) ((iter)->dataptr)
#define ITERATOR_NEXT(iter) (iter = (iter)->next)
#define ITERATOR_PREV(iter) (iter = (iter)->prev)
-
/* This is to iterate for a type defined like:
struct unit_list { struct genlist list; };
where the pointers in the list are really pointers to "atype".
Eg, see speclist.h, which is what this is really for.
*/
-#define TYPED_LIST_ITERATE(atype, typed_list, var) { \
- struct genlist_link *myiter = (typed_list)->list->head_link;\
- atype *var; \
- for(; ITERATOR_PTR(myiter);) { \
- var = ITERATOR_PTR(myiter); \
- ITERATOR_NEXT(myiter);
-
-/* Balance for above: */
-#define LIST_ITERATE_END }}
-
-
-/* Same, but iterate backwards: */
-#define TYPED_LIST_ITERATE_REV(atype, typed_list, var) { \
- struct genlist_link *myiter = (typed_list)->list->tail_link;\
- atype *var; \
- for(; ITERATOR_PTR(myiter);) { \
- var = ITERATOR_PTR(myiter); \
- ITERATOR_PREV(myiter);
-
-/* Balance for above: */
-#define LIST_ITERATE_REV_END }}
+#define TYPED_LIST_ITERATE(atype, typed_list, var) { \
+ struct genlist_link *link = (typed_list)->list->head; \
+ atype *var; \
+ while (link && ITERATOR_PTR(link)) { \
+ bool deleted = link->deleted; \
+ var = (atype *)ITERATOR_PTR(link); \
+ ITERATOR_NEXT(link); \
+ if (!deleted) {
+/* Balance for above: */
+#define LIST_ITERATE_END }}}
-#endif /* FC__GENLIST_H */
+#endif
Index: utility/registry.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/utility/registry.c,v
retrieving revision 1.69
diff -u -r1.69 registry.c
--- utility/registry.c 22 Jan 2005 19:45:45 -0000 1.69
+++ utility/registry.c 22 Jan 2005 19:56:42 -0000
@@ -210,10 +210,6 @@
TYPED_LIST_ITERATE(struct section, seclist, psection)
#define section_list_iterate_end LIST_ITERATE_END
-#define section_list_iterate_rev(seclist, psection) \
- TYPED_LIST_ITERATE_REV(struct section, seclist, psection)
-#define section_list_iterate_rev_end LIST_ITERATE_REV_END
-
/* The hash table and some extra data: */
struct hash_data {
struct hash_table *htbl;
@@ -360,17 +356,14 @@
const char *name)
{
/*
- * Search backwards since on insertion the requested section is most
- * likely at the end (on lookup it doesn't matter).
- *
- * Nonetheless this is slow if there are lots of sections. We could have
+ * This is slow if there are lots of sections. We could have
* a hash on section names to speed it up.
*/
- section_list_iterate_rev(sf->sections, psection) {
+ section_list_iterate(sf->sections, psection) {
if (strcmp(psection->name, name) == 0) {
return psection;
}
- } section_list_iterate_rev_end;
+ } section_list_iterate_end;
return NULL;
}
@@ -657,7 +650,7 @@
/* Following doesn't use entry_list_iterate() because we want to do
* tricky things with the iterators...
*/
- for(ent_iter = psection->entries->list->head_link;
+ for(ent_iter = psection->entries->list->head;
ent_iter && (pentry = ITERATOR_PTR(ent_iter));
ITERATOR_NEXT(ent_iter)) {
- [Freeciv-Dev] (PR#11990) New genlist code,
Per I. Mathisen <=
|
|