summaryrefslogtreecommitdiffstats
path: root/runtime/linkedlist.c
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-04-16 10:26:54 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2008-04-16 10:26:54 +0200
commit8f8f65abb66d1a7839c30c2d1b4b4d653a8990cc (patch)
tree8bd92ca19ae3cccd5b160c717960db88bb095e73 /runtime/linkedlist.c
parent3af28bbd2dc4c79b242aad3b9e3f45cffe0f19d0 (diff)
downloadrsyslog-8f8f65abb66d1a7839c30c2d1b4b4d653a8990cc.tar.gz
rsyslog-8f8f65abb66d1a7839c30c2d1b4b4d653a8990cc.tar.xz
rsyslog-8f8f65abb66d1a7839c30c2d1b4b4d653a8990cc.zip
moved files to the runtime
there are still some files left which could go into the runtime, but I think we will delete most of them once we are done with the full modularization.
Diffstat (limited to 'runtime/linkedlist.c')
-rw-r--r--runtime/linkedlist.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/runtime/linkedlist.c b/runtime/linkedlist.c
new file mode 100644
index 00000000..ce20651e
--- /dev/null
+++ b/runtime/linkedlist.c
@@ -0,0 +1,414 @@
+/* linkedlist.c
+ * This file set implements a generic linked list object. It can be used
+ * wherever a linke list is required.
+ *
+ * NOTE: we do not currently provide a constructor and destructor for the
+ * object itself as we assume it will always be part of another strucuture.
+ * Having a pointer to it, I think, does not really make sense but costs
+ * performance. Consequently, there is is llInit() and llDestroy() and they
+ * do what a constructor and destructur do, except for creating the
+ * linkedList_t structure itself.
+ *
+ * File begun on 2007-07-31 by RGerhards
+ *
+ * Copyright (C) 2007, 2008 by Rainer Gerhards and Adiscon GmbH
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "rsyslog.h"
+#include "linkedlist.h"
+
+
+/* Initialize an existing linkedList_t structure
+ * pKey destructor may be zero to take care of non-keyed lists.
+ */
+rsRetVal llInit(linkedList_t *pThis, rsRetVal (*pEltDestructor)(), rsRetVal (*pKeyDestructor)(void*), int (*pCmpOp)())
+{
+ assert(pThis != NULL);
+ assert(pEltDestructor != NULL);
+
+ pThis->pEltDestruct = pEltDestructor;
+ pThis->pKeyDestruct = pKeyDestructor;
+ pThis->cmpOp = pCmpOp;
+ pThis->pKey = NULL;
+ pThis->iNumElts = 0;
+ pThis->pRoot = NULL;
+ pThis->pLast = NULL;
+
+ return RS_RET_OK;
+};
+
+
+/* llDestroyEltData - destroys a list element
+ * It is a separate function as the
+ * functionality is needed in multiple code-pathes.
+ */
+static rsRetVal llDestroyElt(linkedList_t *pList, llElt_t *pElt)
+{
+ DEFiRet;
+
+ assert(pList != NULL);
+ assert(pElt != NULL);
+
+ /* we ignore errors during destruction, as we need to try
+ * free the element in any case.
+ */
+ if(pElt->pData != NULL)
+ pList->pEltDestruct(pElt->pData);
+ if(pElt->pKey != NULL)
+ pList->pKeyDestruct(pElt->pKey);
+ free(pElt);
+ pList->iNumElts--; /* one less */
+
+ RETiRet;
+}
+
+
+/* llDestroy - destroys a COMPLETE linkedList
+ */
+rsRetVal llDestroy(linkedList_t *pThis)
+{
+ DEFiRet;
+ llElt_t *pElt;
+ llElt_t *pEltPrev;
+
+ assert(pThis != NULL);
+
+ pElt = pThis->pRoot;
+ while(pElt != NULL) {
+ pEltPrev = pElt;
+ pElt = pElt->pNext;
+ /* we ignore errors during destruction, as we need to try
+ * finish the linked list in any case.
+ */
+ llDestroyElt(pThis, pEltPrev);
+ }
+ /* now clean up the pointers */
+ pThis->pRoot = NULL;
+ pThis->pLast = NULL;
+
+ RETiRet;
+}
+
+/* llDestroyRootElt - destroy the root element but otherwise
+ * keeps this list intact. -- rgerhards, 2007-08-03
+ */
+rsRetVal llDestroyRootElt(linkedList_t *pThis)
+{
+ DEFiRet;
+ llElt_t *pPrev;
+
+ if(pThis->pRoot == NULL) {
+ ABORT_FINALIZE(RS_RET_EMPTY_LIST);
+ }
+
+ pPrev = pThis->pRoot;
+ if(pPrev->pNext == NULL) {
+ /* it was the only list element */
+ pThis->pLast = NULL;
+ pThis->pRoot = NULL;
+ } else {
+ /* there are other list elements */
+ pThis->pRoot = pPrev->pNext;
+ }
+
+ CHKiRet(llDestroyElt(pThis, pPrev));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* get next user data element of a linked list. The caller must also
+ * provide a "cookie" to the function. On initial call, it must be
+ * NULL. Other than that, the caller is not allowed to to modify the
+ * cookie. In the current implementation, the cookie is an actual
+ * pointer to the current list element, but this is nothing that the
+ * caller should rely on.
+ */
+rsRetVal llGetNextElt(linkedList_t *pThis, linkedListCookie_t *ppElt, void **ppUsr)
+{
+ llElt_t *pElt;
+ DEFiRet;
+
+ assert(pThis != NULL);
+ assert(ppElt != NULL);
+ assert(ppUsr != NULL);
+
+ pElt = *ppElt;
+
+ pElt = (pElt == NULL) ? pThis->pRoot : pElt->pNext;
+
+ if(pElt == NULL) {
+ iRet = RS_RET_END_OF_LINKEDLIST;
+ } else {
+ *ppUsr = pElt->pData;
+ }
+
+ *ppElt = pElt;
+
+ RETiRet;
+}
+
+
+/* return the key of an Elt
+ * rgerhards, 2007-09-11: note that ppDatea is actually a void**,
+ * but I need to make it a void* to avoid lots of compiler warnings.
+ * It will be converted later down in the code.
+ */
+rsRetVal llGetKey(llElt_t *pThis, void *ppData)
+{
+ assert(pThis != NULL);
+ assert(ppData != NULL);
+
+ *(void**) ppData = pThis->pKey;
+
+ return RS_RET_OK;
+}
+
+
+/* construct a new llElt_t
+ */
+static rsRetVal llEltConstruct(llElt_t **ppThis, void *pKey, void *pData)
+{
+ DEFiRet;
+ llElt_t *pThis;
+
+ assert(ppThis != NULL);
+
+ if((pThis = (llElt_t*) calloc(1, sizeof(llElt_t))) == NULL) {
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+ pThis->pKey = pKey;
+ pThis->pData = pData;
+
+finalize_it:
+ *ppThis = pThis;
+ RETiRet;
+}
+
+
+/* append a user element to the end of the linked list. This includes setting a key. If no
+ * key is desired, simply pass in a NULL pointer for it.
+ */
+rsRetVal llAppend(linkedList_t *pThis, void *pKey, void *pData)
+{
+ llElt_t *pElt;
+ DEFiRet;
+
+ CHKiRet(llEltConstruct(&pElt, pKey, pData));
+
+ pThis->iNumElts++; /* one more */
+ if(pThis->pLast == NULL) {
+ pThis->pRoot = pElt;
+ } else {
+ pThis->pLast->pNext = pElt;
+ }
+ pThis->pLast = pElt;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* unlink a requested element. As we have singly-linked lists, the
+ * caller also needs to pass in the previous element (or NULL, if it is the
+ * root element).
+ * rgerhards, 2007-11-21
+ */
+static rsRetVal llUnlinkElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev)
+{
+ assert(pElt != NULL);
+
+ if(pEltPrev == NULL) { /* root element? */
+ pThis->pRoot = pElt->pNext;
+ } else { /* regular element */
+ pEltPrev->pNext = pElt->pNext;
+ }
+
+ if(pElt == pThis->pLast)
+ pThis->pLast = pEltPrev;
+
+ return RS_RET_OK;
+}
+
+
+/* unlinks and immediately deletes an element. Previous element must
+ * be given (or zero if the root element is to be deleted).
+ * rgerhards, 2007-11-21
+ */
+static rsRetVal llUnlinkAndDelteElt(linkedList_t *pThis, llElt_t *pElt, llElt_t *pEltPrev)
+{
+ DEFiRet;
+
+ assert(pElt != NULL);
+
+ CHKiRet(llUnlinkElt(pThis, pElt, pEltPrev));
+ CHKiRet(llDestroyElt(pThis, pElt));
+
+finalize_it:
+ RETiRet;
+}
+
+/* find a user element based on the provided key - this is the
+ * internal variant, which also tracks the last element pointer
+ * before the found element. This is necessary to delete elements.
+ * NULL means there is no element in front of it, aka the found elt
+ * is the root elt.
+ * rgerhards, 2007-11-21
+ */
+static rsRetVal llFindElt(linkedList_t *pThis, void *pKey, llElt_t **ppElt, llElt_t **ppEltPrev)
+{
+ DEFiRet;
+ llElt_t *pElt;
+ llElt_t *pEltPrev = NULL;
+ int bFound = 0;
+
+ assert(pThis != NULL);
+ assert(pKey != NULL);
+ assert(ppElt != NULL);
+ assert(ppEltPrev != NULL);
+
+ pElt = pThis->pRoot;
+ while(pElt != NULL && bFound == 0) {
+ if(pThis->cmpOp(pKey, pElt->pKey) == 0)
+ bFound = 1;
+ else {
+ pEltPrev = pElt;
+ pElt = pElt->pNext;
+ }
+ }
+
+ if(bFound == 1) {
+ *ppElt = pElt;
+ *ppEltPrev = pEltPrev;
+ } else
+ iRet = RS_RET_NOT_FOUND;
+
+ RETiRet;
+}
+
+
+/* find a user element based on the provided key
+ */
+rsRetVal llFind(linkedList_t *pThis, void *pKey, void **ppData)
+{
+ DEFiRet;
+ llElt_t *pElt;
+ llElt_t *pEltPrev;
+
+ CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev));
+
+ /* if we reach this point, we have found the element */
+ *ppData = pElt->pData;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* find a delete an element based on user-provided key. The element is
+ * delete, the caller does not receive anything. If we need to receive
+ * the element before destruction, we may implement an llFindAndUnlink()
+ * at that time.
+ * rgerhards, 2007-11-21
+ */
+rsRetVal llFindAndDelete(linkedList_t *pThis, void *pKey)
+{
+ DEFiRet;
+ llElt_t *pElt;
+ llElt_t *pEltPrev;
+
+ CHKiRet(llFindElt(pThis, pKey, &pElt, &pEltPrev));
+
+ /* if we reach this point, we have found an element */
+ CHKiRet(llUnlinkAndDelteElt(pThis, pElt, pEltPrev));
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* provide the count of linked list elements
+ */
+rsRetVal llGetNumElts(linkedList_t *pThis, int *piCnt)
+{
+ DEFiRet;
+
+ assert(pThis != NULL);
+ assert(piCnt != NULL);
+
+ *piCnt = pThis->iNumElts;
+
+ RETiRet;
+}
+
+
+/* execute a function on all list members. The functions receives a
+ * user-supplied parameter, which may be either a simple value
+ * or a pointer to a structure with more data. If the user-supplied
+ * function does not return RS_RET_OK, this function here terminates.
+ * rgerhards, 2007-08-02
+ * rgerhards, 2007-11-21: added functionality to delete a list element.
+ * If the called user function returns RS_RET_OK_DELETE_LISTENTRY the current element
+ * is deleted.
+ */
+rsRetVal llExecFunc(linkedList_t *pThis, rsRetVal (*pFunc)(void*, void*), void* pParam)
+{
+ DEFiRet;
+ rsRetVal iRetLL;
+ void *pData;
+ linkedListCookie_t llCookie = NULL;
+ linkedListCookie_t llCookiePrev = NULL; /* previous list element (needed for deletion, NULL = at root) */
+
+ assert(pThis != NULL);
+ assert(pFunc != NULL);
+
+ while((iRetLL = llGetNextElt(pThis, &llCookie, (void**)&pData)) == RS_RET_OK) {
+ iRet = pFunc(pData, pParam);
+ if(iRet == RS_RET_OK_DELETE_LISTENTRY) {
+ /* delete element */
+ CHKiRet(llUnlinkAndDelteElt(pThis, llCookie, llCookiePrev));
+ /* we need to revert back, as we have just deleted the current element.
+ * So the actual current element is the one before it, which happens to be
+ * stored in llCookiePrev. -- rgerhards, 2007-11-21
+ */
+ llCookie = llCookiePrev;
+ } else if (iRet != RS_RET_OK) {
+ goto finalize_it;
+ }
+ llCookiePrev = llCookie;
+ }
+
+ if(iRetLL != RS_RET_END_OF_LINKEDLIST)
+ iRet = iRetLL;
+
+finalize_it:
+ RETiRet;
+}
+
+/* vim:set ai:
+ */