Przeglądaj źródła

structure node reference by reference "kind"

Julius Pfrommer 7 lat temu
rodzic
commit
1f7e07d014

+ 41 - 13
src/server/ua_nodes.c

@@ -6,17 +6,23 @@
 #include "ua_nodes.h"
 
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
-    /* delete standard content */
+    /* Delete standard content */
     UA_NodeId_deleteMembers(&node->nodeId);
     UA_QualifiedName_deleteMembers(&node->browseName);
     UA_LocalizedText_deleteMembers(&node->displayName);
     UA_LocalizedText_deleteMembers(&node->description);
-    UA_Array_delete(node->references, node->referencesSize,
-                    &UA_TYPES[UA_TYPES_REFERENCENODE]);
+    for(size_t i = 0; i < node->referencesSize; ++i) {
+        UA_NodeReferenceKind *refs = &node->references[i];
+        for(size_t j = 0; j < refs->targetIdsSize; ++j)
+            UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
+        UA_free(refs->targetIds);
+        UA_NodeId_deleteMembers(&refs->referenceTypeId);
+    }
+    UA_free(node->references);
     node->references = NULL;
     node->referencesSize = 0;
 
-    /* delete unique content of the nodeclass */
+    /* Delete unique content of the nodeclass */
     switch(node->nodeClass) {
     case UA_NODECLASS_OBJECT:
         break;
@@ -138,7 +144,7 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     if(src->nodeClass != dst->nodeClass)
         return UA_STATUSCODE_BADINTERNALERROR;
     
-    /* copy standard content */
+    /* Copy standard content */
     UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId);
     dst->nodeClass = src->nodeClass;
     retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
@@ -149,16 +155,38 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
         UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
     }
-    retval |= UA_Array_copy(src->references, src->referencesSize,
-                            (void**)&dst->references,
-                            &UA_TYPES[UA_TYPES_REFERENCENODE]);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteMembersAnyNodeClass(dst);
-        return retval;
+
+    /* Copy the references */
+    dst->references = NULL;
+    if(src->referencesSize > 0) {
+        dst->references = UA_calloc(src->referencesSize, sizeof(UA_NodeReferenceKind));
+        dst->referencesSize = src->referencesSize;
+        if(!dst->references) {
+            UA_Node_deleteMembersAnyNodeClass(dst);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+
+        for(size_t i = 0; i < src->referencesSize; ++i) {
+            UA_NodeReferenceKind *srefs = &src->references[i];
+            UA_NodeReferenceKind *drefs = &dst->references[i];
+            drefs->isInverse = srefs->isInverse;
+            retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
+            retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize,
+                                    (void**)&drefs->targetIds,
+                                    &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
+            drefs->targetIdsSize = srefs->targetIdsSize;
+        }
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_Node_deleteMembersAnyNodeClass(dst);
+            return retval;
+        }
     }
-    dst->referencesSize = src->referencesSize;
 
-    /* copy unique content of the nodeclass */
+    /* Copy unique content of the nodeclass */
     switch(src->nodeClass) {
     case UA_NODECLASS_OBJECT:
         retval = UA_ObjectNode_copy((const UA_ObjectNode*)src,

+ 9 - 1
src/server/ua_nodes.h

@@ -48,6 +48,14 @@ extern "C" {
  * Internally, open62541 uses ``UA_Node`` in places where the exact node type is
  * not known or not important. The ``nodeClass`` attribute is used to ensure the
  * correctness of casting from ``UA_Node`` to a specific node type. */
+/* List of reference targets with the same reference type and direction */
+typedef struct {
+    UA_NodeId referenceTypeId;
+    UA_Boolean isInverse;
+    size_t targetIdsSize;
+    UA_ExpandedNodeId *targetIds;
+} UA_NodeReferenceKind;
+
 #define UA_NODE_BASEATTRIBUTES                  \
     UA_NodeId nodeId;                           \
     UA_NodeClass nodeClass;                     \
@@ -56,7 +64,7 @@ extern "C" {
     UA_LocalizedText description;               \
     UA_UInt32 writeMask;                        \
     size_t referencesSize;                      \
-    UA_ReferenceNode *references;
+    UA_NodeReferenceKind *references;
 
 typedef struct {
     UA_NODE_BASEATTRIBUTES

+ 59 - 43
src/server/ua_server_utils.c

@@ -105,38 +105,45 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_Node *rootRef, UA_Boolean inverse,
     const UA_NodeId hasSubtypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
     while(true) {
         for(size_t i = 0; i < node->referencesSize; ++i) {
-            /* is the reference relevant? */
-            if(node->references[i].isInverse != inverse ||
-               !UA_NodeId_equal(&hasSubtypeNodeId, &node->references[i].referenceTypeId))
+            UA_NodeReferenceKind *refs = &node->references[i];
+            /* Are the references relevant? */
+            if(refs->isInverse != inverse ||
+               !UA_NodeId_equal(&hasSubtypeNodeId, &refs->referenceTypeId))
                 continue;
 
-            /* is the target already considered? (multi-inheritance) */
-            UA_Boolean duplicate = false;
-            for(size_t j = 0; j <= last; ++j) {
-                if(UA_NodeId_equal(&node->references[i].targetId.nodeId, &results[j])) {
-                    duplicate = true;
-                    break;
+            for(size_t j = 0; j < refs->targetIdsSize; ++j) {
+                UA_NodeId *targetId = &refs->targetIds[j].nodeId;
+
+                /* Is the target already considered? (multi-inheritance) */
+                UA_Boolean duplicate = false;
+                for(size_t k = 0; k <= last; ++k) {
+                    if(UA_NodeId_equal(targetId, &results[k])) {
+                        duplicate = true;
+                        break;
+                    }
+                }
+                if(duplicate)
+                    continue;
+
+                /* Increase array length if necessary */
+                if(last + 1 >= results_size) {
+                    UA_NodeId *new_results =
+                        (UA_NodeId*)UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
+                    if(!new_results) {
+                        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
+                        return UA_STATUSCODE_BADOUTOFMEMORY;
+                    }
+                    results = new_results;
+                    results_size *= 2;
                 }
-            }
-            if(duplicate)
-                continue;
 
-            /* increase array length if necessary */
-            if(last + 1 >= results_size) {
-                UA_NodeId *new_results =
-                    (UA_NodeId*)UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
-                if(!new_results) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
+                /* Copy new nodeid to the end of the list */
+                retval = UA_NodeId_copy(targetId, &results[++last]);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
+                    return retval;
                 }
-                results = new_results;
-                results_size *= 2;
             }
-
-            /* copy new nodeid to the end of the list */
-            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[++last]);
-            if(retval != UA_STATUSCODE_GOOD)
-                break;
         }
 
         /* Get the next node */
@@ -149,11 +156,6 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_Node *rootRef, UA_Boolean inverse,
             goto next;
     }
 
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
-        return retval;
-    }
-
     *typeHierarchy = results;
     *typeHierarchySize = last + 1;
     return UA_STATUSCODE_GOOD;
@@ -162,22 +164,33 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_Node *rootRef, UA_Boolean inverse,
 UA_Boolean
 isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
              const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
-    if(UA_NodeId_equal(leafNode, nodeToFind))
+    if(UA_NodeId_equal(nodeToFind, leafNode))
         return true;
 
-    const UA_Node *node = UA_NodeStore_get(ns,leafNode);
+    const UA_Node *node = UA_NodeStore_get(ns, leafNode);
     if(!node)
         return false;
 
-    /* Search upwards in the tree */
     for(size_t i = 0; i < node->referencesSize; ++i) {
-        if(!node->references[i].isInverse)
+        UA_NodeReferenceKind *refs = &node->references[i];
+        /* Search upwards in the tree */
+        if(!refs->isInverse)
             continue;
 
-        /* Recurse only for valid reference types */
+        /* Consider only the indicated reference types */
+        UA_Boolean match = false;
         for(size_t j = 0; j < referenceTypeIdsSize; ++j) {
-            if(UA_NodeId_equal(&node->references[i].referenceTypeId, &referenceTypeIds[j]) &&
-               isNodeInTree(ns, &node->references[i].targetId.nodeId, nodeToFind,
+            if(UA_NodeId_equal(&refs->referenceTypeId, &referenceTypeIds[j])) {
+                match = true;
+                break;
+            }
+        }
+        if(!match)
+            continue;
+
+        /* Match the targets or recurse */
+        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
+            if(isNodeInTree(ns, &refs->targetIds[j].nodeId, nodeToFind,
                             referenceTypeIds, referenceTypeIdsSize))
                 return true;
         }
@@ -207,11 +220,14 @@ void getNodeType(UA_Server *server, const UA_Node *node, UA_NodeId *typeId) {
 
     /* Stop at the first matching candidate */
     for(size_t i = 0; i < node->referencesSize; ++i) {
-        if(node->references[i].isInverse == inverse &&
-           UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef)) {
-            UA_NodeId_copy(&node->references[i].targetId.nodeId, typeId);
-            break;
-        }
+        if(node->references[i].isInverse != inverse)
+            continue;
+        if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &parentRef))
+            continue;
+        if(node->references[i].targetIdsSize == 0)
+            return;
+        UA_NodeId_copy(&node->references[i].targetIds[0].nodeId, typeId);
+        return;
     }
 }
 

+ 32 - 19
src/server/ua_services_call.c

@@ -12,15 +12,22 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
                          UA_String withBrowseName) {
     UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
     for(size_t i = 0; i < ofMethod->referencesSize; ++i) {
-        if(ofMethod->references[i].isInverse == false &&
-            UA_NodeId_equal(&hasProperty, &ofMethod->references[i].referenceTypeId)) {
+        UA_NodeReferenceKind *rk = &ofMethod->references[i];
+
+        if(rk->isInverse != false)
+            continue;
+        
+        if(!UA_NodeId_equal(&hasProperty, &rk->referenceTypeId))
+            continue;
+
+        for(size_t j = 0; j < rk->targetIdsSize; ++j) {
             const UA_Node *refTarget =
-                UA_NodeStore_get(server->nodestore, &ofMethod->references[i].targetId.nodeId);
+                UA_NodeStore_get(server->nodestore, &rk->targetIds[j].nodeId);
             if(!refTarget)
                 continue;
             if(refTarget->nodeClass == UA_NODECLASS_VARIABLE &&
-                refTarget->browseName.namespaceIndex == 0 &&
-                UA_String_equal(&withBrowseName, &refTarget->browseName.name)) {
+               refTarget->browseName.namespaceIndex == 0 &&
+               UA_String_equal(&withBrowseName, &refTarget->browseName.name)) {
                 return (const UA_VariableNode*) refTarget;
             }
         }
@@ -73,13 +80,13 @@ Service_Call_single(UA_Server *server, UA_Session *session,
     }
 
     /* Get/verify the object node */
-    const UA_ObjectNode *withObject =
+    const UA_ObjectNode *object =
         (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId);
-    if(!withObject) {
+    if(!object) {
         result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
         return;
     }
-    if(withObject->nodeClass != UA_NODECLASS_OBJECT && withObject->nodeClass != UA_NODECLASS_OBJECTTYPE) {
+    if(object->nodeClass != UA_NODECLASS_OBJECT && object->nodeClass != UA_NODECLASS_OBJECTTYPE) {
         result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
         return;
     }
@@ -102,13 +109,18 @@ Service_Call_single(UA_Server *server, UA_Session *session,
     UA_Boolean found = false;
     UA_NodeId hasComponentNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT);
     UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE);
-    for(size_t i = 0; i < methodCalled->referencesSize; ++i) {
-        if(methodCalled->references[i].isInverse &&
-           UA_NodeId_equal(&methodCalled->references[i].targetId.nodeId, &withObject->nodeId)) {
-            found = isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId,
-                                 &hasComponentNodeId, &hasSubTypeNodeId, 1);
-            if(found)
+    for(size_t i = 0; i < object->referencesSize; ++i) {
+        UA_NodeReferenceKind *rk = &object->references[i];
+        if(rk->isInverse)
+            continue;
+        if(!isNodeInTree(server->nodestore, &rk->referenceTypeId,
+                         &hasComponentNodeId, &hasSubTypeNodeId, 1))
+            continue;
+        for(size_t j = 0; j < rk->targetIdsSize; ++j) {
+            if(UA_NodeId_equal(&rk->targetIds[j].nodeId, &request->methodId)) {
+                found = true;
                 break;
+            }
         }
     }
     if(!found) {
@@ -138,8 +150,9 @@ Service_Call_single(UA_Server *server, UA_Session *session,
     const UA_VariableNode *outputArguments =
         getArgumentsVariableNode(server, methodCalled, UA_STRING("OutputArguments"));
     if(outputArguments) {
-        result->outputArguments = (UA_Variant*)UA_Array_new(outputArguments->value.data.value.value.arrayLength,
-                                               &UA_TYPES[UA_TYPES_VARIANT]);
+        result->outputArguments =
+            (UA_Variant*)UA_Array_new(outputArguments->value.data.value.value.arrayLength,
+                                      &UA_TYPES[UA_TYPES_VARIANT]);
         if(!result->outputArguments) {
             result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             return;
@@ -151,7 +164,7 @@ Service_Call_single(UA_Server *server, UA_Session *session,
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
     methodCallSession = session;
 #endif
-    result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, &withObject->nodeId,
+    result->statusCode = methodCalled->attachedMethod(methodCalled->methodHandle, &object->nodeId,
                                                       &session->sessionId, session->sessionHandle,
                                                       request->inputArgumentsSize, request->inputArguments,
                                                       result->outputArgumentsSize, result->outputArguments);
@@ -171,7 +184,8 @@ void Service_Call(UA_Server *server, UA_Session *session,
         return;
     }
 
-    response->results = (UA_CallMethodResult*)UA_Array_new(request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
+    response->results = (UA_CallMethodResult*)UA_Array_new(request->methodsToCallSize,
+                                                           &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
@@ -208,4 +222,3 @@ void Service_Call(UA_Server *server, UA_Session *session,
 }
 
 #endif /* UA_ENABLE_METHODCALLS */
-

+ 102 - 53
src/server/ua_services_nodemanagement.c

@@ -295,14 +295,15 @@ mandatoryChild(UA_Server *server, UA_Session *session, const UA_NodeId *childNod
 
     /* Look for the reference making the child mandatory */
     for(size_t i = 0; i < child->referencesSize; ++i) {
-        UA_ReferenceNode *ref = &child->references[i];
-        if(!UA_NodeId_equal(&hasModellingRuleId, &ref->referenceTypeId))
+        UA_NodeReferenceKind *refs = &child->references[i];
+        if(!UA_NodeId_equal(&hasModellingRuleId, &refs->referenceTypeId))
             continue;
-        if(!UA_NodeId_equal(&mandatoryId, &ref->targetId.nodeId))
+        if(refs->isInverse)
             continue;
-        if(ref->isInverse)
-            continue;
-        return true;
+        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
+            if(UA_NodeId_equal(&mandatoryId, &refs->targetIds[j].nodeId))
+                return true;
+        }
     }
     return false;
 }
@@ -1162,35 +1163,66 @@ static UA_StatusCode
 deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
                       const UA_DeleteReferencesItem *item);
 
-/* Adds a one-way reference to the local nodestore */
 static UA_StatusCode
-addOneWayReference(UA_Server *server, UA_Session *session,
-                   UA_Node *node, const UA_AddReferencesItem *item) {
-    /* Increase the array size */
-    size_t i = node->referencesSize;
-    size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
-    UA_ReferenceNode *new_refs =
-        (UA_ReferenceNode *)UA_realloc(node->references,
-                                       sizeof(UA_ReferenceNode) * refssize);
-    if(!new_refs)
+addOneWayTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target) {
+    UA_ExpandedNodeId *targets = UA_realloc(refs->targetIds,
+                                            sizeof(UA_ExpandedNodeId) * (refs->targetIdsSize+1));
+    if(!targets)
         return UA_STATUSCODE_BADOUTOFMEMORY;
+    refs->targetIds = targets;
 
-    /* Add the new reference */
-    node->references = new_refs;
-    UA_ReferenceNode_init(&new_refs[i]);
-    UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId,
-                                          &new_refs[i].referenceTypeId);
-    retval |= UA_ExpandedNodeId_copy(&item->targetNodeId, &new_refs[i].targetId);
-    new_refs[i].isInverse = !item->isForward;
+    UA_StatusCode retval = UA_ExpandedNodeId_copy(target, &refs->targetIds[refs->targetIdsSize]);
+    if(retval != UA_STATUSCODE_GOOD && refs->targetIds == 0) {
+        UA_free(refs->targetIds);
+        refs->targetIds = NULL;
+        return retval;
+    }
 
-    /* Increase the array size counter or clean up */
-    if(retval == UA_STATUSCODE_GOOD)
-        node->referencesSize = i+1;
-    else
-        UA_ReferenceNode_deleteMembers(&new_refs[i]);
+    refs->targetIdsSize++;
     return retval;
 }
 
+static UA_StatusCode
+addOneWayNodeReferences(UA_Node *node, const UA_AddReferencesItem *item) {
+    UA_NodeReferenceKind *refs = UA_realloc(node->references,
+                                            sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
+    if(!refs)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    node->references = refs;
+    UA_NodeReferenceKind *new = &refs[node->referencesSize];
+    memset(new, 0, sizeof(UA_NodeReferenceKind));
+
+    new->isInverse = !item->isForward;
+    UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &new->referenceTypeId);
+    retval |= addOneWayTarget(new, &item->targetNodeId);
+
+    if(retval == UA_STATUSCODE_GOOD) {
+        node->referencesSize++;
+    } else {
+        UA_NodeId_deleteMembers(&new->referenceTypeId);
+        if(node->referencesSize == 0) {
+            UA_free(node->references);
+            node->references = NULL;
+        }
+    }
+    return retval;
+}
+
+/* Adds a one-way reference to the local nodestore */
+static UA_StatusCode
+addOneWayReference(UA_Server *server, UA_Session *session,
+                   UA_Node *node, const UA_AddReferencesItem *item) {
+    for(size_t i = 0; i < node->referencesSize; ++i) {
+        UA_NodeReferenceKind *refs = &node->references[i];
+        if(refs->isInverse == item->isForward)
+            continue;
+        if(!UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId))
+            continue;
+        return addOneWayTarget(refs, &item->targetNodeId);
+    }
+    return addOneWayNodeReferences(node, item);
+}
+
 static UA_StatusCode
 addReference(UA_Server *server, UA_Session *session,
              const UA_AddReferencesItem *item) {
@@ -1359,15 +1391,19 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
 /****************/
 
 static void
-removeReferences(UA_Server *server, UA_Session *session, const UA_Node *node) {
+removeReferences(UA_Server *server, UA_Session *session,
+                 const UA_Node *node) {
     UA_DeleteReferencesItem item;
     UA_DeleteReferencesItem_init(&item);
     item.targetNodeId.nodeId = node->nodeId;
     for(size_t i = 0; i < node->referencesSize; ++i) {
-        item.isForward = node->references[i].isInverse;
-        item.sourceNodeId = node->references[i].targetId.nodeId;
-        item.referenceTypeId = node->references[i].referenceTypeId;
-        deleteReference(server, session, &item);
+        UA_NodeReferenceKind *refs = &node->references[i];
+        item.isForward = refs->isInverse;
+        item.referenceTypeId = refs->referenceTypeId;
+        for(size_t j = 0; j < refs->targetIdsSize; ++j) {
+            item.sourceNodeId = refs->targetIds[j].nodeId;
+            deleteReference(server, session, &item);
+        }
     }
 }
 
@@ -1443,30 +1479,43 @@ UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
 static UA_StatusCode
 deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
                       const UA_DeleteReferencesItem *item) {
-    UA_Boolean edited = false;
     for(size_t i = node->referencesSize; i > 0; --i) {
-        UA_ReferenceNode *ref = &node->references[i-1];
-        if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &ref->targetId.nodeId))
+        UA_NodeReferenceKind *refs = &node->references[i-1];
+        if(item->isForward == refs->isInverse)
             continue;
-        if(!UA_NodeId_equal(&item->referenceTypeId, &ref->referenceTypeId))
+        if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId))
             continue;
-        if(item->isForward == ref->isInverse)
-            continue;
-        UA_ReferenceNode_deleteMembers(ref);
-        /* move the last entry to override the current position */
-        node->references[i-1] = node->references[node->referencesSize-1];
-        --node->referencesSize;
-        edited = true;
-        break;
-    }
-    if(!edited)
-        return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
-    /* we removed the last reference */
-    if(node->referencesSize == 0 && node->references) {
-        UA_free(node->references);
-        node->references = NULL;
+
+        for(size_t j = refs->targetIdsSize; j > 0; --j) {
+            if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &refs->targetIds[j-1].nodeId))
+                continue;
+
+            /* Ok, delete the reference */
+            UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j-1]);
+            refs->targetIdsSize--;
+
+            /* One matching target remaining */
+            if(refs->targetIdsSize > 0) {
+                refs->targetIds[j-1] = refs->targetIds[refs->targetIdsSize];
+                return UA_STATUSCODE_GOOD;
+            }
+
+            /* Remove refs */
+            UA_free(refs->targetIds);
+            UA_NodeId_deleteMembers(&refs->referenceTypeId);
+            node->referencesSize--;
+            if(node->referencesSize > 0) {
+                node->references[i-1] = node->references[node->referencesSize];
+                return UA_STATUSCODE_GOOD;
+            }
+
+            /* Remove the node references */
+            UA_free(node->references);
+            node->references = NULL;
+            return UA_STATUSCODE_GOOD;
+        }
     }
-    return UA_STATUSCODE_GOOD;;
+    return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
 }
 
 static UA_StatusCode

+ 134 - 122
src/server/ua_services_view.c

@@ -6,8 +6,9 @@
 #include "ua_services.h"
 
 static UA_StatusCode
-fillReferenceDescription(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
-                         UA_UInt32 mask, UA_ReferenceDescription *descr) {
+fillReferenceDescription(UA_Server *server, const UA_Node *curr,
+                         const UA_NodeReferenceKind *ref, UA_UInt32 mask,
+                         UA_ReferenceDescription *descr) {
     UA_ReferenceDescription_init(descr);
     UA_StatusCode retval = UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
     if(mask & UA_BROWSERESULTMASK_REFERENCETYPEID)
@@ -20,15 +21,11 @@ fillReferenceDescription(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode
         retval |= UA_QualifiedName_copy(&curr->browseName, &descr->browseName);
     if(mask & UA_BROWSERESULTMASK_DISPLAYNAME)
         retval |= UA_LocalizedText_copy(&curr->displayName, &descr->displayName);
-    if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION){
+    if(mask & UA_BROWSERESULTMASK_TYPEDEFINITION) {
         if(curr->nodeClass == UA_NODECLASS_OBJECT || curr->nodeClass == UA_NODECLASS_VARIABLE) {
-            for(size_t i = 0; i < curr->referencesSize; ++i) {
-                UA_ReferenceNode *refnode = &curr->references[i];
-                if(refnode->referenceTypeId.identifier.numeric == UA_NS0ID_HASTYPEDEFINITION) {
-                    retval |= UA_ExpandedNodeId_copy(&refnode->targetId, &descr->typeDefinition);
-                    break;
-                }
-            }
+            UA_NodeId type;
+            getNodeType(server, curr , &type);
+            retval |= UA_NodeId_copy(&type, &descr->typeDefinition.nodeId);
         }
     }
     return retval;
@@ -88,48 +85,6 @@ returnRelevantNodeExternal(UA_ExternalNodeStore *ens, const UA_BrowseDescription
 }
 #endif
 
-/* Tests if the node is relevant to the browse request and shall be returned. If
-   so, it is retrieved from the Nodestore. If not, null is returned. */
-static const UA_Node *
-returnRelevantNode(UA_Server *server, const UA_BrowseDescription *descr,
-                   const UA_ReferenceNode *reference, const UA_NodeId *relevant, size_t relevant_count) {
-    /* reference in the right direction? */
-    if(reference->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
-        return NULL;
-    if(!reference->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_INVERSE)
-        return NULL;
-
-    /* Is the reference part of the hierarchy of references we look for? For
-     * this, relevant != NULL.*/
-    if(relevant) {
-        UA_Boolean is_relevant = false;
-        for(size_t i = 0; i < relevant_count; ++i) {
-            if(UA_NodeId_equal(&reference->referenceTypeId, &relevant[i])) {
-                is_relevant = true;
-                break;
-            }
-        }
-        if(!is_relevant)
-            return NULL;
-    }
-
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
-    /* return the node from an external namespace*/
-    for(size_t nsIndex = 0; nsIndex < server->externalNamespacesSize; ++nsIndex) {
-        if(reference->targetId.nodeId.namespaceIndex != server->externalNamespaces[nsIndex].index)
-            continue;
-        return returnRelevantNodeExternal(&server->externalNamespaces[nsIndex].externalNodeStore,
-                                          descr, reference);
-    }
-#endif
-
-    /* return from the internal nodestore */
-    const UA_Node *node = UA_NodeStore_get(server->nodestore, &reference->targetId.nodeId);
-    if(node && descr->nodeClassMask != 0 && (node->nodeClass & descr->nodeClassMask) == 0)
-        return NULL;
-    return node;
-}
-
 static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     LIST_REMOVE(cp, pointers);
     UA_ByteString_deleteMembers(&cp->identifier);
@@ -138,10 +93,13 @@ static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     ++session->availableContinuationPoints;
 }
 
+/* Returns whether the node / continuationpoint is done */
 static UA_Boolean
 browseRelevantReferences(UA_Server *server, UA_BrowseResult *result, const UA_NodeId *relevant_refs,
                          size_t relevant_refs_size, const UA_BrowseDescription *descr,
                          struct ContinuationPointEntry *cp) {
+    UA_assert(cp != NULL);
+
     /* Get the node */
     const UA_Node *node = UA_NodeStore_get(server->nodestore, &descr->nodeId);
     if(!node) {
@@ -158,65 +116,113 @@ browseRelevantReferences(UA_Server *server, UA_BrowseResult *result, const UA_No
     /* How many references can we return at most? */
     size_t maxrefs = cp->maxReferences;
     if(maxrefs == 0)
-        maxrefs = node->referencesSize;
+        maxrefs = UA_INT32_MAX;
     else if(maxrefs > node->referencesSize)
         maxrefs = node->referencesSize;
 
-    UA_assert(node->referencesSize > 0);
-    UA_assert(maxrefs > 0);
-    UA_assert(maxrefs <= node->referencesSize);
-
     /* Allocate the results array */
+    size_t refs_size = 2; /* True size of the array */
     result->references =
-        (UA_ReferenceDescription*)UA_Array_new(maxrefs, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
+        (UA_ReferenceDescription*)UA_Array_new(refs_size, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
     if(!result->references) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return false;
     }
 
+    size_t referenceKindIndex = 0;
+    size_t targetIndex = 0;
+    if(cp) {
+        referenceKindIndex = cp->referenceKindIndex;
+        targetIndex = cp->targetIndex;
+    }
+
     /* Loop over the node's references */
-    size_t skipped = 0;
-    size_t referencesCount = 0; /* How many references did we copy into the results array */
-    size_t i = 0; /* Count the references we looked at */
-    for(; i < node->referencesSize && referencesCount < maxrefs; ++i) {
-        const UA_Node *current = returnRelevantNode(server, descr, &node->references[i],
-                                                    relevant_refs, relevant_refs_size);
-        if(!current)
-            continue;
+    for(; referenceKindIndex < node->referencesSize; ++referenceKindIndex) {
+        UA_NodeReferenceKind *rk = &node->references[referenceKindIndex];
 
-        if(skipped < cp->continuationIndex) {
-            ++skipped;
+        /* Reference in the right direction? */
+        if(rk->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+            continue;
+        if(!rk->isInverse && descr->browseDirection == UA_BROWSEDIRECTION_INVERSE)
             continue;
+
+        /* Is the reference part of the hierarchy of references we look for? */
+        if(relevant_refs) {
+            UA_Boolean is_relevant = false;
+            for(size_t i = 0; i < relevant_refs_size; ++i) {
+                if(UA_NodeId_equal(&rk->referenceTypeId, &relevant_refs[i])) {
+                    is_relevant = true;
+                    break;
+                }
+            }
+            if(!is_relevant)
+                continue;
         }
 
-        result->statusCode = fillReferenceDescription(server->nodestore, current,
-                                                      &node->references[i], descr->resultMask,
-                                                      &result->references[referencesCount]);
-        if(result->statusCode != UA_STATUSCODE_GOOD)
-            break;
+        /* Loop over the targets */
+        for(; targetIndex < rk->targetIdsSize; ++targetIndex) {
+            /* Get the node */
+            const UA_Node *target = UA_NodeStore_get(server->nodestore, &rk->targetIds[targetIndex].nodeId);
 
-        ++referencesCount;
+            /* Test if the node class matches */
+            if(!target || (descr->nodeClassMask != 0 && (target->nodeClass & descr->nodeClassMask) == 0))
+                continue;
+
+            /* A match! Can we return it? */
+            if(result->referencesSize >= maxrefs) {
+                /* There are references we could not return */
+                cp->referenceKindIndex = referenceKindIndex;
+                cp->targetIndex = targetIndex;
+                return false;
+            }
+
+            /* Make enough space in the array */
+            if(result->referencesSize >= refs_size) {
+                refs_size *= 2;
+                UA_ReferenceDescription *rd = UA_realloc(result->references,
+                                                         sizeof(UA_ReferenceDescription) * refs_size);
+                if(!rd) {
+                    result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                    goto error_recovery;
+                }
+                result->references = rd;
+            }
+
+            /* Copy the node description */
+            result->statusCode = fillReferenceDescription(server, target, rk, descr->resultMask,
+                                                          &result->references[result->referencesSize]);
+/* fillReferenceDescription(UA_Server *server, const UA_Node *curr, */
+/*                          const UA_NodeReferenceKind *ref, */
+/*                          const UA_ExpandedNodeId *target, UA_UInt32 mask, */
+/*                          UA_ReferenceDescription *descr) { */
+            if(result->statusCode != UA_STATUSCODE_GOOD)
+                goto error_recovery;
+
+            /* Increase the counter */
+            result->referencesSize++;
+        }
+
+        targetIndex = 0; /* Start at index 0 for the next reference kind */
     }
-    result->referencesSize = referencesCount;
 
     /* No relevant references, return array of length zero */
-    if(referencesCount == 0) {
+    if(result->referencesSize == 0) {
         UA_free(result->references);
         result->references = (UA_ReferenceDescription *)UA_EMPTY_ARRAY_SENTINEL;
-        result->referencesSize = 0;
     }
 
-    /* Clean up if an error occured */
-    if(result->statusCode != UA_STATUSCODE_GOOD) {
+    /* The node is done */
+    return true;
+
+ error_recovery:
+    if(result->referencesSize == 0)
+        UA_free(result->references);
+    else
         UA_Array_delete(result->references, result->referencesSize,
                         &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
-        result->references = NULL;
-        result->referencesSize = 0;
-        return false;
-    }
-
-    /* Are we done with the node? */
-    return (i == node->referencesSize);
+    result->references = NULL;
+    result->referencesSize = 0;
+    return false;
 }
 
 /* Results for a single browsedescription. This is the inner loop for both
@@ -287,10 +293,6 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
-    /* Update the continuationIndex, how many results did we deliver so far for
-     * the BrowseDescription? */
-    internal_cp->continuationIndex += (UA_UInt32)result->referencesSize;
-
     /* A continuation point exists already */
     if(cp) {
         if(done)
@@ -308,7 +310,8 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
             return;
         }
         UA_BrowseDescription_copy(descr, &cp->browseDescription);
-        cp->continuationIndex = internal_cp->continuationIndex;
+        cp->referenceKindIndex = internal_cp->referenceKindIndex;
+        cp->targetIndex = internal_cp->targetIndex;
         cp->maxReferences = internal_cp->maxReferences;
 
         /* Create a random bytestring via a Guid */
@@ -451,16 +454,16 @@ walkBrowsePathElementNodeReference(UA_BrowsePathResult *result, size_t *targetsS
                                    UA_NodeId **next, size_t *nextSize, size_t *nextCount,
                                    UA_UInt32 elemDepth, UA_Boolean inverse, UA_Boolean all_refs,
                                    const UA_NodeId *reftypes, size_t reftypes_count,
-                                   const UA_ReferenceNode *reference) {
+                                   const UA_NodeReferenceKind *rk) {
     /* Does the direction of the reference match? */
-    if(reference->isInverse != inverse)
+    if(rk->isInverse != inverse)
         return;
 
     /* Is the node relevant? */
     if(!all_refs) {
         UA_Boolean match = false;
         for(size_t j = 0; j < reftypes_count; ++j) {
-            if(UA_NodeId_equal(&reference->referenceTypeId, &reftypes[j])) {
+            if(UA_NodeId_equal(&rk->referenceTypeId, &reftypes[j])) {
                 match = true;
                 break;
             }
@@ -469,36 +472,45 @@ walkBrowsePathElementNodeReference(UA_BrowsePathResult *result, size_t *targetsS
             return;
     }
 
-    /* Does the reference point to an external server? Then add to the
-     * targets with the right path "depth" */
-    if(reference->targetId.serverIndex != 0) {
-        UA_BrowsePathTarget *tempTargets =
-            (UA_BrowsePathTarget *)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (*targetsSize) * 2);
-        if(!tempTargets) {
-            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            return;
+    /* Loop over the targets */
+    for(size_t i = 0; i < rk->targetIdsSize; i++) {
+        UA_ExpandedNodeId *targetId = &rk->targetIds[i];
+
+        /* Does the reference point to an external server? Then add to the
+         * targets with the right path "depth" */
+        if(targetId->serverIndex != 0) {
+            UA_BrowsePathTarget *tempTargets =
+                (UA_BrowsePathTarget *)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (*targetsSize) * 2);
+            if(!tempTargets) {
+                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                return;
+            }
+            result->targets = tempTargets;
+            (*targetsSize) *= 2;
+            result->statusCode = UA_ExpandedNodeId_copy(targetId,
+                                                        &result->targets[result->targetsSize].targetId);
+            result->targets[result->targetsSize].remainingPathIndex = elemDepth;
+            continue;
         }
-        result->targets = tempTargets;
-        (*targetsSize) *= 2;
-        result->statusCode = UA_ExpandedNodeId_copy(&reference->targetId,
-                                                    &result->targets[result->targetsSize].targetId);
-        result->targets[result->targetsSize].remainingPathIndex = elemDepth;
-        return;
-    }
 
-    /* Add the node to the next array for the following path element */
-    if(*nextSize <= *nextCount) {
-        UA_NodeId *tempNext = (UA_NodeId *)UA_realloc(*next, sizeof(UA_NodeId) * (*nextSize) * 2);
-        if(!tempNext) {
-            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            return;
+        /* Can we store the node in the array of candidates for deep-search? */
+        if(*nextSize <= *nextCount) {
+            UA_NodeId *tempNext = (UA_NodeId *)UA_realloc(*next, sizeof(UA_NodeId) * (*nextSize) * 2);
+            if(!tempNext) {
+                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                return;
+            }
+            *next = tempNext;
+            (*nextSize) *= 2;
         }
-        *next = tempNext;
-        (*nextSize) *= 2;
+
+        /* Add the node to the next array for the following path element */
+        result->statusCode = UA_NodeId_copy(&targetId->nodeId,
+                                            &(*next)[*nextCount]);
+        if(result->statusCode != UA_STATUSCODE_GOOD)
+            return;
+        ++(*nextCount);
     }
-    result->statusCode = UA_NodeId_copy(&reference->targetId.nodeId,
-                                        &(*next)[*nextCount]);
-    ++(*nextCount);
 }
 
 static void
@@ -547,10 +559,10 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
         /* Loop over the nodes references */
         for(size_t r = 0; r < node->referencesSize &&
                 result->statusCode == UA_STATUSCODE_GOOD; ++r) {
-            UA_ReferenceNode *reference = &node->references[r];
+            UA_NodeReferenceKind *rk = &node->references[r];
             walkBrowsePathElementNodeReference(result, targetsSize, next, nextSize, nextCount,
                                                elemDepth, elem->isInverse, all_refs,
-                                               reftypes, reftypes_count, reference);
+                                               reftypes, reftypes_count, rk);
         }
     }
 

+ 4 - 1
src/ua_session.h

@@ -20,8 +20,11 @@ struct ContinuationPointEntry {
     LIST_ENTRY(ContinuationPointEntry) pointers;
     UA_ByteString        identifier;
     UA_BrowseDescription browseDescription;
-    UA_UInt32            continuationIndex;
     UA_UInt32            maxReferences;
+
+    /* The last point in the node references? */
+    size_t referenceKindIndex;
+    size_t targetIndex;
 };
 
 struct UA_Subscription;