Browse Source

fix a memleak for isNodeInTreeNoCircular; clean up

Julius Pfrommer 7 years ago
parent
commit
c54a3e2643
1 changed files with 33 additions and 40 deletions
  1. 33 40
      src/server/ua_server_utils.c

+ 33 - 40
src/server/ua_server_utils.c

@@ -4,6 +4,8 @@
 
 #include "ua_server_internal.h"
 
+#define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */
+
 /**********************/
 /* Parse NumericRange */
 /**********************/
@@ -85,12 +87,11 @@ UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
 /* Information Model Operations */
 /********************************/
 
-/*
- * Keeps track of already visited nodes to detect circular references
- */
+/* Keeps track of already visited nodes to detect circular references */
 struct ref_history {
-    struct ref_history *parent; // the previous element
-    const UA_NodeId *id; // the id of the node at this depth
+    struct ref_history *parent; /* the previous element */
+    const UA_NodeId *id; /* the id of the node at this depth */
+    UA_UInt16 depth;
 };
 
 static UA_Boolean
@@ -100,6 +101,9 @@ isNodeInTreeNoCircular(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_Nod
     if(UA_NodeId_equal(nodeToFind, leafNode))
         return true;
 
+    if(visitedRefs->depth >= UA_MAX_TREE_RECURSE)
+        return false;
+
     const UA_Node *node = ns->getNode(ns->context, leafNode);
     if(!node)
         return false;
@@ -123,31 +127,31 @@ isNodeInTreeNoCircular(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_Nod
 
         /* Match the targets or recurse */
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
-            /* check if we already have seen the referenced node and skip to avoid endless recursion */
-
-            struct ref_history *tmp = visitedRefs;
-            struct ref_history *last = visitedRefs;
-            UA_Boolean skip = UA_FALSE;
-            while(!skip && tmp) {
-                if(UA_NodeId_equal(tmp->id, &refs->targetIds[j].nodeId))
-                    skip = UA_TRUE;
-                last = tmp;
-                tmp = tmp->parent;
+            /* Check if we already have seen the referenced node and skip to
+             * avoid endless recursion. Do this only at every 5th depth to save
+             * effort. Circular dependencies are rare and forbidden for most
+             * reference types. */
+            if(visitedRefs->depth % 5 == 4) {
+                struct ref_history *last = visitedRefs;
+                UA_Boolean skip = UA_FALSE;
+                while(!skip && last) {
+                    if(UA_NodeId_equal(last->id, &refs->targetIds[j].nodeId))
+                        skip = UA_TRUE;
+                    last = last->parent;
+                }
+                if(skip)
+                    continue;
             }
 
-            if(skip)
-                continue;
+            /* Stack-allocate the visitedRefs structure for the next depth */
+            struct ref_history nextVisitedRefs = {visitedRefs, &refs->targetIds[j].nodeId,
+                                                  (UA_UInt16)(visitedRefs->depth+1)};
 
-            last->parent = (struct ref_history*)UA_malloc(sizeof(struct ref_history));
-            if(!last->parent) {
-                return false;
-            }
-            last = last->parent;
-            last->parent = NULL;
-            last->id = &refs->targetIds[j].nodeId;
-
-            if(isNodeInTreeNoCircular(ns, &refs->targetIds[j].nodeId, nodeToFind, last,
-                            referenceTypeIds, referenceTypeIdsSize)) {
+            /* Recurse */
+            UA_Boolean foundRecursive =
+                isNodeInTreeNoCircular(ns, &refs->targetIds[j].nodeId, nodeToFind, &nextVisitedRefs,
+                                       referenceTypeIds, referenceTypeIdsSize);
+            if(foundRecursive) {
                 ns->releaseNode(ns->context, node);
                 return true;
             }
@@ -161,19 +165,8 @@ isNodeInTreeNoCircular(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_Nod
 UA_Boolean
 isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
              const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
-    struct ref_history visitedRefs = {
-            NULL, leafNode
-    };
-    UA_Boolean retVal = isNodeInTreeNoCircular(ns, leafNode, nodeToFind, &visitedRefs, referenceTypeIds, referenceTypeIdsSize);
-    struct ref_history *tmp = visitedRefs.parent;
-
-    while(tmp) {
-        struct ref_history *deleteMe = tmp;
-        tmp = tmp->parent;
-        UA_free(deleteMe);
-    }
-
-    return retVal;
+    struct ref_history visitedRefs = {NULL, leafNode, 0};
+    return isNodeInTreeNoCircular(ns, leafNode, nodeToFind, &visitedRefs, referenceTypeIds, referenceTypeIdsSize);
 }
 
 const UA_Node *