[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Cvs-cvs] Changes to ccvs/src/hash.c [signed-commits2]
From: |
Derek Robert Price |
Subject: |
[Cvs-cvs] Changes to ccvs/src/hash.c [signed-commits2] |
Date: |
Thu, 20 Oct 2005 17:34:09 -0400 |
Index: ccvs/src/hash.c
diff -u /dev/null ccvs/src/hash.c:1.48.4.1
--- /dev/null Thu Oct 20 21:34:06 2005
+++ ccvs/src/hash.c Thu Oct 20 21:33:10 2005
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
+ *
+ * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
+ * and others.
+ *
+ * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
+ *
+ * You may distribute under the terms of the GNU General Public License as
+ * specified in the README file that comes with the CVS source distribution.
+ *
+ * Polk's hash list manager. So cool.
+ */
+
+#include "cvs.h"
+
+/* Global caches. The idea is that we maintain a linked list of "free"d
+ nodes or lists, and get new items from there. It has been suggested
+ to use an obstack instead, but off the top of my head, I'm not sure
+ that would gain enough to be worth worrying about. */
+static List *listcache = NULL;
+static Node *nodecache = NULL;
+
+static void freenode_mem (Node * p);
+
+/* hash function */
+static int
+hashp (const char *key)
+{
+ unsigned int h = 0;
+ unsigned int g;
+
+ assert(key != NULL);
+
+ while (*key != 0)
+ {
+ unsigned int c = *key++;
+ /* The FOLD_FN_CHAR is so that findnode_fn works. */
+ h = (h << 4) + FOLD_FN_CHAR (c);
+ if ((g = h & 0xf0000000) != 0)
+ h = (h ^ (g >> 24)) ^ g;
+ }
+
+ return h % HASHSIZE;
+}
+
+
+
+/*
+ * create a new list (or get an old one from the cache)
+ */
+List *
+getlist (void)
+{
+ int i;
+ List *list;
+ Node *node;
+
+ if (listcache != NULL)
+ {
+ /* get a list from the cache and clear it */
+ list = listcache;
+ listcache = listcache->next;
+ list->next = NULL;
+ for (i = 0; i < HASHSIZE; i++)
+ list->hasharray[i] = NULL;
+ }
+ else
+ {
+ /* make a new list from scratch */
+ list = xmalloc (sizeof (List));
+ memset (list, 0, sizeof (List));
+ node = getnode ();
+ list->list = node;
+ node->type = HEADER;
+ node->next = node->prev = node;
+ }
+ return list;
+}
+
+
+
+/*
+ * Free up a list. For accessing globals which might be accessed via interrupt
+ * handlers, it can be assumed that the first action of this function will be
+ * to set the *listp to NULL.
+ */
+void
+dellist (List **listp)
+{
+ int i;
+ Node *p;
+ List *tmp;
+
+ if (*listp == NULL)
+ return;
+
+ tmp = *listp;
+ *listp = NULL;
+
+ p = tmp->list;
+
+ /* free each node in the list (except header) */
+ while (p->next != p)
+ delnode (p->next);
+
+ /* free any list-private data, without freeing the actual header */
+ freenode_mem (p);
+
+ /* free up the header nodes for hash lists (if any) */
+ for (i = 0; i < HASHSIZE; i++)
+ {
+ if ((p = tmp->hasharray[i]) != NULL)
+ {
+ /* put the nodes into the cache */
+#ifndef NOCACHE
+ p->type = NT_UNKNOWN;
+ p->next = nodecache;
+ nodecache = p;
+#else
+ /* If NOCACHE is defined we turn off the cache. This can make
+ it easier to tools to determine where items were allocated
+ and freed, for tracking down memory leaks and the like. */
+ free (p);
+#endif
+ }
+ }
+
+ /* put it on the cache */
+#ifndef NOCACHE
+ tmp->next = listcache;
+ listcache = tmp;
+#else
+ free (tmp->list);
+ free (tmp);
+#endif
+}
+
+
+
+/*
+ * remove a node from it's list (maybe hash list too)
+ */
+void
+removenode (Node *p)
+{
+ if (!p) return;
+
+ /* take it out of the list */
+ p->next->prev = p->prev;
+ p->prev->next = p->next;
+
+ /* if it was hashed, remove it from there too */
+ if (p->hashnext)
+ {
+ p->hashnext->hashprev = p->hashprev;
+ p->hashprev->hashnext = p->hashnext;
+ }
+}
+
+
+
+void
+mergelists (List *dest, List **src)
+{
+ Node *head, *p, *n;
+
+ head = (*src)->list;
+ for (p = head->next; p != head; p = n)
+ {
+ n = p->next;
+ removenode (p);
+ addnode (dest, p);
+ }
+ dellist (src);
+}
+
+
+
+/*
+ * get a new list node
+ */
+Node *
+getnode (void)
+{
+ Node *p;
+
+ if (nodecache != NULL)
+ {
+ /* get one from the cache */
+ p = nodecache;
+ nodecache = p->next;
+ }
+ else
+ {
+ /* make a new one */
+ p = xmalloc (sizeof (Node));
+ }
+
+ /* always make it clean */
+ memset (p, 0, sizeof (Node));
+ p->type = NT_UNKNOWN;
+
+ return p;
+}
+
+
+
+/*
+ * remove a node from it's list (maybe hash list too) and free it
+ */
+void
+delnode (Node *p)
+{
+ if (!p) return;
+ /* remove it */
+ removenode (p);
+ /* free up the storage */
+ freenode (p);
+}
+
+
+
+/*
+ * free up the storage associated with a node
+ */
+static void
+freenode_mem (Node *p)
+{
+ if (p->delproc != NULL)
+ p->delproc (p); /* call the specified delproc */
+ else
+ {
+ if (p->data != NULL) /* otherwise free() it if necessary */
+ free (p->data);
+ }
+ if (p->key != NULL) /* free the key if necessary */
+ free (p->key);
+
+ /* to be safe, re-initialize these */
+ p->key = p->data = NULL;
+ p->delproc = NULL;
+}
+
+
+
+/*
+ * free up the storage associated with a node and recycle it
+ */
+void
+freenode (Node *p)
+{
+ /* first free the memory */
+ freenode_mem (p);
+
+ /* then put it in the cache */
+#ifndef NOCACHE
+ p->type = NT_UNKNOWN;
+ p->next = nodecache;
+ nodecache = p;
+#else
+ free (p);
+#endif
+}
+
+
+
+/*
+ * Link item P into list LIST before item MARKER. If P->KEY is non-NULL and
+ * that key is already in the hash table, return -1 without modifying any
+ * parameter.
+ *
+ * return 0 on success
+ */
+int
+insert_before (List *list, Node *marker, Node *p)
+{
+ if (p->key != NULL) /* hash it too? */
+ {
+ int hashval;
+ Node *q;
+
+ hashval = hashp (p->key);
+ if (list->hasharray[hashval] == NULL) /* make a header for list? */
+ {
+ q = getnode ();
+ q->type = HEADER;
+ list->hasharray[hashval] = q->hashnext = q->hashprev = q;
+ }
+
+ /* put it into the hash list if it's not already there */
+ for (q = list->hasharray[hashval]->hashnext;
+ q != list->hasharray[hashval]; q = q->hashnext)
+ {
+ if (strcmp (p->key, q->key) == 0)
+ return -1;
+ }
+ q = list->hasharray[hashval];
+ p->hashprev = q->hashprev;
+ p->hashnext = q;
+ p->hashprev->hashnext = p;
+ q->hashprev = p;
+ }
+
+ p->next = marker;
+ p->prev = marker->prev;
+ marker->prev->next = p;
+ marker->prev = p;
+
+ return 0;
+}
+
+
+
+/*
+ * insert item p at end of list "list" (maybe hash it too) if hashing and it
+ * already exists, return -1 and don't actually put it in the list
+ *
+ * return 0 on success
+ */
+int
+addnode (List *list, Node *p)
+{
+ return insert_before (list, list->list, p);
+}
+
+
+
+/*
+ * Like addnode, but insert p at the front of `list'. This bogosity is
+ * necessary to preserve last-to-first output order for some RCS functions.
+ */
+int
+addnode_at_front (List *list, Node *p)
+{
+ return insert_before (list, list->list->next, p);
+}
+
+
+
+/* Look up an entry in hash list table and return a pointer to the
+ * node. Return NULL if not found or if list is NULL. Abort with a fatal
+ * error for errors.
+ */
+Node *
+findnode (List *list, const char *key)
+{
+ Node *head, *p;
+
+ if ((list == NULL))
+ return NULL;
+
+ assert (key != NULL);
+
+ head = list->hasharray[hashp (key)];
+ if (head == NULL)
+ /* Not found. */
+ return NULL;
+
+ for (p = head->hashnext; p != head; p = p->hashnext)
+ if (strcmp (p->key, key) == 0)
+ return p;
+ return NULL;
+}
+
+
+
+/*
+ * Like findnode, but for a filename.
+ */
+Node *
+findnode_fn (List *list, const char *key)
+{
+ Node *head, *p;
+
+ /* This probably should be "assert (list != NULL)" (or if not we
+ should document the current behavior), but only if we check all
+ the callers to see if any are relying on this behavior. */
+ if (list == NULL)
+ return NULL;
+
+ assert (key != NULL);
+
+ head = list->hasharray[hashp (key)];
+ if (head == NULL)
+ return NULL;
+
+ for (p = head->hashnext; p != head; p = p->hashnext)
+ if (fncmp (p->key, key) == 0)
+ return p;
+ return NULL;
+}
+
+
+
+/*
+ * walk a list with a specific proc
+ */
+int
+walklist (List *list, int (*proc) (Node *, void *), void *closure)
+{
+ Node *head, *p;
+ int err = 0;
+
+#ifdef HAVE_PRINTF_PTR
+ TRACE (TRACE_FLOW, "walklist ( list=%p, proc=%p, closure=%p )",
+ (void *)list, (void *)proc, (void *)closure);
+#else
+ TRACE (TRACE_FLOW, "walklist ( list=%lx, proc=%lx, closure=%lx )",
+ (unsigned long)list,(unsigned long) proc,
+ (unsigned long)closure);
+#endif
+
+ if (list == NULL)
+ return 0;
+
+ head = list->list;
+ for (p = head->next; p != head; p = p->next)
+ err += proc (p, closure);
+ return err;
+}
+
+
+
+int
+list_isempty (List *list)
+{
+ return list == NULL || list->list->next == list->list;
+}
+
+
+
+static int (*client_comp) (const Node *, const Node *);
+
+static int
+qsort_comp (const void *elem1, const void *elem2)
+{
+ Node **node1 = (Node **) elem1;
+ Node **node2 = (Node **) elem2;
+ return client_comp (*node1, *node2);
+}
+
+
+
+/*
+ * sort the elements of a list (in place)
+ */
+void
+sortlist (List *list, int (*comp) (const Node *, const Node *))
+{
+ Node *head, *remain, *p, **array;
+ int i, n;
+
+ if (list == NULL)
+ return;
+
+ /* save the old first element of the list */
+ head = list->list;
+ remain = head->next;
+
+ /* count the number of nodes in the list */
+ n = 0;
+ for (p = remain; p != head; p = p->next)
+ n++;
+
+ /* allocate an array of nodes and populate it */
+ array = xnmalloc (n, sizeof (Node *));
+ i = 0;
+ for (p = remain; p != head; p = p->next)
+ array[i++] = p;
+
+ /* sort the array of nodes */
+ client_comp = comp;
+ qsort (array, n, sizeof(Node *), qsort_comp);
+
+ /* rebuild the list from beginning to end */
+ head->next = head->prev = head;
+ for (i = 0; i < n; i++)
+ {
+ p = array[i];
+ p->next = head;
+ p->prev = head->prev;
+ p->prev->next = p;
+ head->prev = p;
+ }
+
+ /* release the array of nodes */
+ free (array);
+}
+
+
+
+/*
+ * compare two files list node (for sort)
+ */
+int
+fsortcmp (const Node *p, const Node *q)
+{
+ return strcmp (p->key, q->key);
+}
+
+
+
+/* Debugging functions. Quite useful to call from within gdb. */
+
+
+static char *
+nodetypestring (Ntype type)
+{
+ switch (type) {
+ case NT_UNKNOWN: return "UNKNOWN";
+ case HEADER: return "HEADER";
+ case ENTRIES: return "ENTRIES";
+ case FILES: return "FILES";
+ case LIST: return "LIST";
+ case RCSNODE: return "RCSNODE";
+ case RCSVERS: return "RCSVERS";
+ case DIRS: return "DIRS";
+ case UPDATE: return "UPDATE";
+ case LOCK: return "LOCK";
+ case NDBMNODE: return "NDBMNODE";
+ case FILEATTR: return "FILEATTR";
+ case VARIABLE: return "VARIABLE";
+ case RCSFIELD: return "RCSFIELD";
+ case RCSCMPFLD: return "RCSCMPFLD";
+ case RCSSTRING: return "RCSSTRING";
+ }
+
+ return "<trash>";
+}
+
+
+
+static int
+printnode (Node *node, void *closure)
+{
+ if (node == NULL)
+ {
+ (void) printf("NULL node.\n");
+ return 0;
+ }
+
+#ifdef HAVE_PRINTF_PTR
+ (void) printf("Node at %p: type = %s, key = %p = \"%s\", data = %p, next =
%p, prev = %p\n",
+ (void *) node, nodetypestring(node->type),
+ (void *) node->key, node->key, node->data,
+ (void *) node->next, (void *) node->prev);
+#else
+ (void) printf("Node at 0x%lx: type = %s, key = 0x%lx = \"%s\", data =
0x%lx, next = 0x%lx, prev = 0x%lx\n",
+ (unsigned long) node, nodetypestring(node->type),
+ (unsigned long) node->key, node->key, (unsigned long) node->data,
+ (unsigned long) node->next, (unsigned long) node->prev);
+#endif
+
+ return 0;
+}
+
+
+
+/* This is global, not static, so that its name is unique and to avoid
+ compiler warnings about it not being used. But it is not used by CVS;
+ it exists so one can call it from a debugger. */
+
+void
+printlist (List *list)
+{
+ if (list == NULL)
+ {
+ (void) printf("NULL list.\n");
+ return;
+ }
+
+#ifdef HAVE_PRINTF_PTR
+ (void) printf("List at %p: list = %p, HASHSIZE = %d, next = %p\n",
+ (void *) list, (void *) list->list, HASHSIZE, (void *) list->next);
+#else
+ (void) printf("List at 0x%lx: list = 0x%lx, HASHSIZE = %d, next = 0x%lx\n",
+ (unsigned long) list, (unsigned long) list->list, HASHSIZE,
+ (unsigned long) list->next);
+#endif
+
+ (void) walklist(list, printnode, NULL);
+
+ return;
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Cvs-cvs] Changes to ccvs/src/hash.c [signed-commits2],
Derek Robert Price <=