Browse Source

use an array instead of linked lists for browsing

Julius Pfrommer 10 years ago
parent
commit
2113f8a559

+ 0 - 1
CMakeLists.txt

@@ -33,7 +33,6 @@ set(lib_sources src/ua_types.c
                 src/server/ua_services_securechannel.c
                 src/server/ua_services_nodemanagement.c
                 src/server/ua_services_view.c
-                src/server/ua_services_monitoreditems.c
                 ${headers}
                 ${generated_headers})
 

+ 1 - 1
examples/networklayer_tcp.c

@@ -83,7 +83,7 @@ static UA_StatusCode NetworklayerTCP_remove(NetworklayerTCP *layer, UA_Int32 soc
     layer->connectionsSize--;
 	TCPConnection *newconnections;
     newconnections = malloc(sizeof(TCPConnection) * layer->connectionsSize);
-	memcpy(newconnections, &layer->connections, sizeof(TCPConnection) * index);
+	memcpy(newconnections, layer->connections, sizeof(TCPConnection) * index);
 	memcpy(&newconnections[index], &layer->connections[index+1],
            sizeof(TCPConnection) * (layer->connectionsSize - index));
     free(layer->connections);

src/server/ua_services_monitoreditems.c → src/ongoing/ua_services_monitoreditems.c


+ 6 - 5
src/server/ua_server.c

@@ -473,11 +473,12 @@ void UA_Server_init(UA_Server *server, UA_String *endpointUrl) {
     namespaceArray->value.storage.data.arrayLength = 2;
     UA_String_copycstring("http://opcfoundation.org/UA/", &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[0]);
     UA_String_copycstring("http://localhost:16664/open62541/", &((UA_String *)(namespaceArray->value.storage.data.dataPtr))[1]);
-    namespaceArray->arrayDimensionsSize = 1;
-    UA_UInt32 *dimensions = UA_NULL;
-    dimensions = UA_alloc(sizeof(UA_UInt32));
-    *dimensions = 2;
-    namespaceArray->arrayDimensions = dimensions;
+    UA_UInt32 *dimensions = UA_alloc(sizeof(UA_UInt32));
+    if(dimensions) {
+        *dimensions = 2;
+        namespaceArray->arrayDimensions = dimensions;
+        namespaceArray->arrayDimensionsSize = 1;
+    }
     namespaceArray->dataType = NS0NODEID(UA_STRING_NS0);
     namespaceArray->valueRank       = 1;
     namespaceArray->minimumSamplingInterval = 1.0;

+ 4 - 4
src/server/ua_server_binary.c

@@ -140,8 +140,8 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
         DBG_VERBOSE(printf("Invoke Service: %s\n", # TYPE));                                              \
         Service_##TYPE(server, channel->session, &p, &r);                                                 \
         DBG_VERBOSE(printf("Finished Service: %s\n", # TYPE));                                            \
-        UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r)); \
-        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);     \
+        UA_ByteString_newMembers(message, UA_##TYPE##Response_calcSizeBinary(&r));                        \
+        UA_##TYPE##Response_encodeBinary(&r, message, &sendOffset);                                       \
         UA_##TYPE##Request_deleteMembers(&p);                                                             \
         UA_##TYPE##Response_deleteMembers(&r);                                                            \
         responseType = requestType.nodeId.identifier.numeric + 3;                                         \
@@ -227,8 +227,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_ActivateSessionRequest_deleteMembers(&p);
         UA_ActivateSessionResponse_deleteMembers(&r);
         responseType = requestType.nodeId.identifier.numeric + 3;
-    }
         break;
+    }
 
     case UA_CLOSESESSIONREQUEST_NS0: {
         UA_CloseSessionRequest  p;
@@ -243,8 +243,8 @@ static void processMessage(UA_Connection *connection, UA_Server *server, const U
         UA_CloseSessionRequest_deleteMembers(&p);
         UA_CloseSessionResponse_deleteMembers(&r);
         responseType = requestType.nodeId.identifier.numeric + 3;
+        break;
     }
-    break;
 
     case UA_READREQUEST_NS0:
         INVOKE_SERVICE(Read);

+ 2 - 4
src/server/ua_services_attribute.c

@@ -208,13 +208,11 @@ static UA_DataValue service_read_node(UA_Server *server, const UA_ReadValueId *i
         v.status       = UA_STATUSCODE_BADNOTREADABLE;
     }
 
-
     return v;
 }
-void Service_Read(UA_Server *server, UA_Session *session,
-                  const UA_ReadRequest *request, UA_ReadResponse *response) {
-    UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
 
+void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
+                  UA_ReadResponse *response) {
     if(request->nodesToReadSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;

+ 178 - 160
src/server/ua_services_view.c

@@ -4,199 +4,221 @@
 #include "ua_namespace_0.h"
 #include "ua_util.h"
 
-UA_Int32 Service_Browse_getReferenceDescription(UA_NodeStore *ns, UA_ReferenceNode *reference,
-                                                UA_UInt32 nodeClassMask, UA_UInt32 resultMask,
-                                                UA_ReferenceDescription *referenceDescription) {
-    const UA_Node *foundNode;
-    if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &foundNode) != UA_STATUSCODE_GOOD)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    UA_NodeId_copy(&foundNode->nodeId, &referenceDescription->nodeId.nodeId);
-    //TODO ExpandedNodeId is a mockup
+/* Releases the current node, even if it was supplied as an argument. */
+static UA_StatusCode fillReferenceDescription(UA_NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
+                                              UA_UInt32 resultMask, UA_ReferenceDescription *referenceDescription) {
+    UA_ReferenceDescription_init(referenceDescription);
+    if(!currentNode && resultMask != 0) {
+        if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &currentNode) != UA_STATUSCODE_GOOD)
+            return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
+    //TODO: ExpandedNodeId is mocked up
     referenceDescription->nodeId.serverIndex = 0;
     referenceDescription->nodeId.namespaceUri.length = -1;
 
-    /* UA_UInt32 mask = 0; */
-    /* for(mask = 0x01;mask <= 0x40;mask *= 2) { */
-    /*     switch(mask & (resultMask)) { */
     if(resultMask & UA_BROWSERESULTMASK_REFERENCETYPEID)
-        UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
+        retval |= UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
     if(resultMask & UA_BROWSERESULTMASK_ISFORWARD)
         referenceDescription->isForward = !reference->isInverse;
     if(resultMask & UA_BROWSERESULTMASK_NODECLASS)
-        UA_NodeClass_copy(&foundNode->nodeClass, &referenceDescription->nodeClass);
+        retval |= UA_NodeClass_copy(&currentNode->nodeClass, &referenceDescription->nodeClass);
     if(resultMask & UA_BROWSERESULTMASK_BROWSENAME)
-        UA_QualifiedName_copy(&foundNode->browseName, &referenceDescription->browseName);
+        retval |= UA_QualifiedName_copy(&currentNode->browseName, &referenceDescription->browseName);
     if(resultMask & UA_BROWSERESULTMASK_DISPLAYNAME)
-        UA_LocalizedText_copy(&foundNode->displayName, &referenceDescription->displayName);
-    if(resultMask & UA_BROWSERESULTMASK_TYPEDEFINITION) {
-        if(foundNode->nodeClass != UA_NODECLASS_OBJECT &&
-           foundNode->nodeClass != UA_NODECLASS_VARIABLE)
-            goto end;
-        
-        for(UA_Int32 i = 0;i < foundNode->referencesSize;i++) {
-            UA_ReferenceNode *ref = &foundNode->references[i];
+        retval |= UA_LocalizedText_copy(&currentNode->displayName, &referenceDescription->displayName);
+    if(resultMask & UA_BROWSERESULTMASK_TYPEDEFINITION && currentNode->nodeClass != UA_NODECLASS_OBJECT &&
+       currentNode->nodeClass != UA_NODECLASS_VARIABLE) {
+        for(UA_Int32 i = 0;i < currentNode->referencesSize;i++) {
+            UA_ReferenceNode *ref = &currentNode->references[i];
             if(ref->referenceTypeId.identifier.numeric == 40 /* hastypedefinition */) {
-                UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
-                goto end;
+                retval |= UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
+                break;
             }
         }
     }
- end:
-    UA_NodeStore_releaseManagedNode(foundNode);
-    return UA_STATUSCODE_GOOD;
-}
 
-/* singly-linked list */
-struct SubRefTypeId {
-    UA_NodeId id;
-    SLIST_ENTRY(SubRefTypeId) next;
-};
-SLIST_HEAD(SubRefTypeIdList, SubRefTypeId);
-
-static UA_UInt32 walkReferenceTree(UA_NodeStore *ns, const UA_ReferenceTypeNode *current,
-                                   struct SubRefTypeIdList *list) {
-    // insert the current referencetype
-    struct SubRefTypeId *element = UA_alloc(sizeof(struct SubRefTypeId));
-    element->id = current->nodeId;
-    SLIST_INSERT_HEAD(list, element, next);
-
-    UA_UInt32 count = 1; // the current element
-
-    // walk the tree
-    for(UA_Int32 i = 0;i < current->referencesSize;i++) {
-        if(current->references[i].referenceTypeId.identifier.numeric == 45 /* HasSubtype */ &&
-           current->references[i].isInverse == UA_FALSE) {
-            const UA_Node *node;
-            if(UA_NodeStore_get(ns, &current->references[i].targetId.nodeId, &node) == UA_STATUSCODE_GOOD
-               && node->nodeClass == UA_NODECLASS_REFERENCETYPE) {
-                count += walkReferenceTree(ns, (UA_ReferenceTypeNode *)node, list);
-                UA_NodeStore_releaseManagedNode(node);
-            }
-        }
-    }
-    return count;
+    if(currentNode)
+        UA_NodeStore_releaseManagedNode(currentNode);
+    if(retval)
+        UA_ReferenceDescription_deleteMembers(referenceDescription);
+    return retval;
 }
 
-/* We do not search across namespaces so far. The id of the father-referencetype is returned in the array also. */
-static UA_Int32 findSubReferenceTypes(UA_NodeStore *ns, UA_NodeId *rootReferenceType,
-                                      UA_NodeId **ids, UA_UInt32 *idcount) {
-    struct SubRefTypeIdList list;
-    UA_UInt32 count;
-    SLIST_INIT(&list);
-
-    // walk the tree
-    const UA_ReferenceTypeNode *root;
-    if(UA_NodeStore_get(ns, rootReferenceType, (const UA_Node **)&root) != UA_STATUSCODE_GOOD ||
-       root->nodeClass != UA_NODECLASS_REFERENCETYPE)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    count = walkReferenceTree(ns, root, &list);
-    UA_NodeStore_releaseManagedNode((const UA_Node *)root);
-
-    // copy results into an array
-    *ids = UA_alloc(sizeof(UA_NodeId)*count);
-    for(UA_UInt32 i = 0;i < count;i++) {
-        struct SubRefTypeId *element = SLIST_FIRST(&list);
-        UA_NodeId_copy(&element->id, &(*ids)[i]);
-        SLIST_REMOVE_HEAD(&list, next);
-        UA_free(element);
+/* Tests if the node is relevant an shall be returned. If the targetNode needs
+   to be retrieved from the nodestore to determine this, the targetNode is
+   returned if the node is relevant. */
+static UA_Boolean isRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription, UA_Boolean returnAll,
+                                       UA_ReferenceNode *reference, const UA_Node **currentNode,
+                                       UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
+    // 1) Test Browse direction
+    if(reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+        return UA_FALSE;
+
+    else if(reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
+        return UA_FALSE;
+
+    // 2) Test if the reference type is relevant
+    UA_Boolean isRelevant = returnAll;
+    if(!isRelevant) {
+        for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
+            if(UA_NodeId_equal(&reference->referenceTypeId, &relevantRefTypes[i]) == UA_EQUAL)
+                isRelevant = UA_TRUE;
+        }
+        if(!isRelevant)
+            return UA_FALSE;
     }
-    *idcount = count;
 
-    return UA_STATUSCODE_GOOD;
-}
+    // 3) Test if the target nodeClass is relevant
+    if(browseDescription->nodeClassMask == 0)
+        return UA_TRUE; // the node is relevant, but we didn't need to get it from the nodestore yet.
 
-/* is this a relevant reference? */
-static INLINE UA_Boolean Service_Browse_returnReference(UA_BrowseDescription *browseDescription,
-                                                        UA_ReferenceNode     *reference,
-                                                        UA_NodeId            *relevantRefTypes,
-                                                        UA_UInt32             relevantRefTypesCount) {
-    if(reference->isInverse == UA_TRUE &&
-       browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+    if(UA_NodeStore_get(ns, &reference->targetId.nodeId, currentNode) != UA_STATUSCODE_GOOD)
         return UA_FALSE;
-    else if(reference->isInverse == UA_FALSE &&
-            browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
+
+    if(((*currentNode)->nodeClass & browseDescription->nodeClassMask) == 0) {
+        UA_NodeStore_releaseManagedNode(*currentNode);
         return UA_FALSE;
-    for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
-        if(UA_NodeId_equal(&browseDescription->referenceTypeId, &relevantRefTypes[i]) == UA_EQUAL)
-            return UA_TRUE;
     }
-    return UA_FALSE;
+
+    // the node is relevant and was retrieved from the nodestore, do not release it.
+    return UA_TRUE;
 }
 
-/* Return results to a single browsedescription. */
-static void Service_Browse_getBrowseResult(UA_NodeStore         *ns,
-                                           UA_BrowseDescription *browseDescription,
-                                           UA_UInt32             maxReferences,
-                                           UA_BrowseResult      *browseResult) {
-    const UA_Node *node;
-    UA_NodeId     *relevantReferenceTypes = UA_NULL;
-    UA_UInt32      relevantReferenceTypesCount = 0;
-    if(UA_NodeStore_get(ns, &browseDescription->nodeId, &node) != UA_STATUSCODE_GOOD) {
-        browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
-        return;
+/* We do not search across namespaces so far. The id of the root-referencetype
+   is returned in the array also. */
+static UA_Int32 findRelevantReferenceTypes(UA_NodeStore *ns, const UA_NodeId *rootReferenceType,
+                                           UA_NodeId **referenceTypes, UA_UInt32 *referenceTypesSize) {
+    /* The references form a tree. We walk the tree by adding new nodes to the end of the array. */
+    UA_UInt32 currentIndex = 0;
+    UA_UInt32 currentLastIndex = 0;
+    UA_UInt32 currentArraySize = 20; // should be more than enough. if not, increase the array size.
+    UA_NodeId *typeArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize);
+    if(!typeArray)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    retval |= UA_NodeId_copy(rootReferenceType, &typeArray[0]);
+    if(retval) {
+        UA_free(typeArray);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
     }
+        
+    const UA_ReferenceTypeNode *node;
+    do {
+        retval |= UA_NodeStore_get(ns, &typeArray[currentIndex], (const UA_Node **)&node);
+        if(retval)
+            break;
+        if(node->nodeClass != UA_NODECLASS_REFERENCETYPE) // subtypes of referencestypes are always referencestypes?
+            continue;
 
-    // 0 => unlimited references
-    if(maxReferences == 0)
-        maxReferences = node->referencesSize;
-
-    // discover the relevant subtypes
-    if(!browseDescription->includeSubtypes ||
-       findSubReferenceTypes(ns, &browseDescription->referenceTypeId, &relevantReferenceTypes,
-                             &relevantReferenceTypesCount) != UA_STATUSCODE_GOOD) {
-        if(!(relevantReferenceTypes = UA_alloc(sizeof(UA_NodeId)))) {
-            return;
+        // Find subtypes of the current referencetype
+        for(UA_Int32 i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
+            if(node->references[i].referenceTypeId.identifier.numeric != 45 /* HasSubtype */ ||
+               node->references[i].isInverse == UA_TRUE)
+                continue;
+
+            if(currentLastIndex + 1 >= currentArraySize) {
+                // we need to resize the array
+                UA_NodeId *newArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize * 2);
+                if(newArray) {
+                    memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
+                    currentArraySize *= 2;
+                    UA_free(typeArray);
+                    typeArray = newArray;
+                } else {
+                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                    break;
+                }
+            }
+
+            // ok, we have space to add the new referencetype.
+            retval |= UA_NodeId_copy(&node->references[i].targetId.nodeId, &typeArray[++currentLastIndex]);
+            if(retval)
+                currentLastIndex--; // undo if we need to delete the typeArray
         }
-        UA_NodeId_copy(&browseDescription->referenceTypeId, relevantReferenceTypes);
-        relevantReferenceTypesCount = 1;
-    }
+        UA_NodeStore_releaseManagedNode((UA_Node*)node);
+    } while(++currentIndex <= currentLastIndex && retval == UA_STATUSCODE_GOOD);
 
-    /* We do not use a linked list but traverse the nodes references list twice
-     * (once for counting, once for generating the referencedescriptions). That
-     * is much faster than using a linked list, since the references are
-     * allocated in a continuous blob and RAM access is predictible/does not
-     * miss cache lines so often. TODO: measure with some huge objects! */
-    UA_UInt32 refs = 0;
-    for(UA_Int32 i = 0;i < node->referencesSize && refs <= maxReferences;i++) {
-        if(Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes,
-                                          relevantReferenceTypesCount))
-            refs++;
+    if(retval)
+        UA_Array_delete(typeArray, currentLastIndex, &UA_[UA_NODEID]);
+    else {
+        *referenceTypes = typeArray;
+        *referenceTypesSize = currentLastIndex + 1;
     }
+    
+    return retval;
+}
+
+/* Results for a single browsedescription. */
+static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription,
+                            UA_UInt32 maxReferences, UA_BrowseResult *browseResult) {
+    UA_UInt32  relevantReferenceTypesSize = 0;
+    UA_NodeId *relevantReferenceTypes = UA_NULL;
 
-    // can we return all relevant references at once?
-    UA_Boolean finished = UA_TRUE;
-    if(refs > maxReferences) {
-        refs--;
-        finished = UA_FALSE;
+    // if the referencetype is null, all referencetypes are returned
+    UA_Boolean returnAll = UA_NodeId_isNull(&browseDescription->referenceTypeId);
+    if(!returnAll) {
+        if(browseDescription->includeSubtypes) {
+            browseResult->statusCode = findRelevantReferenceTypes(ns, &browseDescription->referenceTypeId,
+                                                                  &relevantReferenceTypes, &relevantReferenceTypesSize);
+            if(browseResult->statusCode != UA_STATUSCODE_GOOD)
+                return;
+        } else {
+            relevantReferenceTypes = (UA_NodeId*)&browseDescription->referenceTypeId; // is const
+            relevantReferenceTypesSize = 1;
+        }
     }
 
-    browseResult->referencesSize = refs;
-    UA_Array_new((void **)&browseResult->references, refs, &UA_[UA_REFERENCEDESCRIPTION]);
+    const UA_Node *parentNode;
+    if(UA_NodeStore_get(ns, &browseDescription->nodeId, &parentNode) != UA_STATUSCODE_GOOD) {
+        browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+        if(!returnAll)
+            UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_[UA_NODEID]);
+        return;
+    }
 
-    for(UA_UInt32 i = 0, j = 0;j < refs;i++) {
-        if(!Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes,
-                                           relevantReferenceTypesCount))
-            continue;
+    // 0 => unlimited references
+    if(maxReferences == 0 || maxReferences > UA_INT32_MAX || (UA_Int32)maxReferences > parentNode->referencesSize)
+        maxReferences = parentNode->referencesSize;
 
-        if(Service_Browse_getReferenceDescription(ns, &node->references[i], browseDescription->nodeClassMask,
-                                                  browseDescription->resultMask, &browseResult->references[j]) != UA_STATUSCODE_GOOD)
-            browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
-        j++;
-    }
+    /* We allocate an array that is probably too big. But since most systems
+       have more than enough memory, this has zero impact on speed and
+       performance. Call Array_delete with the actual content size! */
+    browseResult->references = UA_alloc(sizeof(UA_ReferenceDescription) * maxReferences);
+    if(!browseResult->references) {
+        browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+    } else {
+        UA_UInt32 currentRefs = 0;
+        for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
+            // 1) Is the node relevant? This might retrieve the node from the nodestore
+            const UA_Node *currentNode = UA_NULL;
+            if(!isRelevantTargetNode(ns, browseDescription, returnAll, &parentNode->references[i], &currentNode,
+                                     relevantReferenceTypes, relevantReferenceTypesSize))
+                continue;
 
-    if(!finished) {
-        // Todo. Set the Statuscode and the continuation point.
+            // 2) Fill the reference description. This also releases the current node.
+            if(fillReferenceDescription(ns, currentNode, &parentNode->references[i], browseDescription->resultMask,
+                                        &browseResult->references[currentRefs]) != UA_STATUSCODE_GOOD) {
+                UA_Array_delete(browseResult->references, currentRefs, &UA_[UA_REFERENCEDESCRIPTION]);
+                currentRefs = 0;
+                browseResult->references = UA_NULL;
+                browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
+                break;
+            }
+            currentRefs++;
+        }
+        browseResult->referencesSize = currentRefs;
     }
 
-    UA_NodeStore_releaseManagedNode(node);
-    UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesCount, &UA_[UA_NODEID]);
+    UA_NodeStore_releaseManagedNode(parentNode);
+    if(!returnAll && browseDescription->includeSubtypes)
+        UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_[UA_NODEID]);
 }
 
-void Service_Browse(UA_Server *server, UA_Session *session,
-                    const UA_BrowseRequest *request, UA_BrowseResponse *response) {
-    UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
-
+void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request, UA_BrowseResponse *response) {
     if(request->nodesToBrowseSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -210,22 +232,18 @@ void Service_Browse(UA_Server *server, UA_Session *session,
         
     response->resultsSize = request->nodesToBrowseSize;
     for(UA_Int32 i = 0;i < request->nodesToBrowseSize;i++)
-        Service_Browse_getBrowseResult(server->nodestore, &request->nodesToBrowse[i],
-                                       request->requestedMaxReferencesPerNode, &response->results[i]);
+        getBrowseResult(server->nodestore, &request->nodesToBrowse[i],
+                        request->requestedMaxReferencesPerNode, &response->results[i]);
 }
 
-
 void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
                                            const UA_TranslateBrowsePathsToNodeIdsRequest *request,
                                            UA_TranslateBrowsePathsToNodeIdsResponse *response) {
-    UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
-
     if(request->browsePathsSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
 
-    // Allocate space for a correct answer
     response->resultsSize = request->browsePathsSize;
     // _init of the elements is done in Array_new
     if(UA_Array_new((void **)&response->results, request->browsePathsSize, &UA_[UA_BROWSEPATHRESULT])

+ 5 - 3
src/ua_types.c

@@ -159,13 +159,15 @@ void UA_String_print(const UA_String *p, FILE *stream) {
 }
 #endif
 
+/* The c-string needs to be null-terminated. */
 UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
-    dst->data   = UA_NULL;
     dst->length = strlen(src);
     if(dst->length > 0) {
-        if(!(dst->data = UA_alloc(dst->length)))
+        if(!(dst->data = UA_alloc(dst->length))) {
+            dst->length = -1;
             return UA_STATUSCODE_BADOUTOFMEMORY;
-        UA_memcpy((void *)dst->data, src, dst->length);
+        }
+        UA_memcpy(dst->data, src, dst->length);
     }
     return UA_STATUSCODE_GOOD;
 }