Переглянути джерело

simplify handling of external nodestores

Julius Pfrommer 10 роки тому
батько
коміт
c85c4e4f72

+ 5 - 9
CMakeLists.txt

@@ -27,8 +27,6 @@ set(lib_sources src/ua_types.c
                 src/server/ua_server.c
                 src/server/ua_securechannel_manager.c
                 src/server/ua_session_manager.c
-                src/server/ua_namespace.c
-                src/server/ua_namespace_manager.c
 				src/server/ua_server_binary.c
                 src/server/ua_services_attribute.c
                 src/server/ua_services_session.c
@@ -36,11 +34,9 @@ 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/nodestore/open62541_nodestore_view.c
-				src/server/nodestore/open62541_nodestore_attribute.c
-				src/server/nodestore/open62541_nodestore_nodemanagement.c
-				src/server/nodestore/open62541_nodestore_core.c
-				src/server/ua_nodestore_interface.c
+				#src/server/nodestore/open62541_nodestore_view.c
+				#src/server/nodestore/open62541_nodestore_attribute.c
+				#src/server/nodestore/open62541_nodestore_nodemanagement.c
 				${exported_headers}
 				${generated_headers}
                 ${headers})
@@ -115,9 +111,9 @@ endif(ENABLE_JSON_ENCODING)
 option(ENABLE_MULTITHREADING "Enable multithreading" OFF)
 if(ENABLE_MULTITHREADING)
     find_package(Threads REQUIRED)
-    list(APPEND lib_sources src/server/ua_nodestore_concurrent_core.c)
+    list(APPEND lib_sources src/server/ua_nodestore_concurrent.c)
 else()
-    list(APPEND lib_sources src/server/nodestore/open62541_nodestore_core.c)
+    list(APPEND lib_sources src/server/ua_nodestore.c)
 endif()
 
 add_library(open62541-objects OBJECT ${lib_sources}) # static version that exports all symbols

+ 7 - 18
examples/opcuaServer.c

@@ -16,11 +16,6 @@
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
-
-#include "../src/server/ua_nodestore_interface.h"
-#include "../src/server/ua_namespace_manager.h"
-#include "../src/server/nodestore/open62541_nodestore.h"
-
 UA_Boolean running = 1;
 
 void stopHandler(int sign) {
@@ -56,31 +51,25 @@ UA_ByteString loadCertificate() {
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
-	UA_Server *server;
 	UA_String endpointUrl;
-	UA_String_copycstring("no endpoint url",&endpointUrl);
-
+    UA_String_copycstring("opc.tcp://192.168.56.101:16664",&endpointUrl);
 	UA_ByteString certificate = loadCertificate();
 	//create a nodestore which holds all nodes
-	open62541NodeStore *open62541NodeStore;
-	open62541NodeStore_new(&open62541NodeStore);
-	open62541NodeStore_setNodeStore(open62541NodeStore);
+	/* open62541NodeStore *open62541NodeStore; */
+	/* open62541NodeStore_new(&open62541NodeStore); */
+	/* open62541NodeStore_setNodeStore(open62541NodeStore); */
 
 	//create server and use default open62541Nodestore for storing the nodes
-	server = UA_Server_new(&endpointUrl, &certificate, NULL, 1);
+	UA_Server *server = UA_Server_new(&endpointUrl, &certificate);
 
 	//add a node to the adresspace
     UA_Int32 myInteger = 42;
     UA_QualifiedName myIntegerName;
     UA_QualifiedName_copycstring("the answer is",&myIntegerName);
-    UA_ExpandedNodeId parentNodeId;
-    UA_ExpandedNodeId_init(&parentNodeId);
-    parentNodeId.namespaceUri.length = 0;
-    parentNodeId.nodeId = UA_NODEIDS[UA_OBJECTSFOLDER];
     UA_Server_addScalarVariableNode(server, &myIntegerName, (void*)&myInteger, &UA_TYPES[UA_INT32],
-    		&parentNodeId, (UA_NodeId*)&UA_NODEIDS[UA_ORGANIZES]);
+                                    &UA_EXPANDEDNODEIDS[UA_OBJECTSFOLDER], &UA_NODEIDS[UA_ORGANIZES]);
     UA_QualifiedName_deleteMembers(&myIntegerName);
-
+    
 #ifdef BENCHMARK
     UA_UInt32 nodeCount = 500;
     UA_Int32 data = 42;

+ 49 - 45
include/ua_server.h

@@ -27,59 +27,63 @@ extern "C" {
 
 /** @defgroup server Server */
 
-struct UA_NodeStore;
-typedef struct UA_NodeStore UA_NodeStoreInterface;
-
 struct UA_Server;
 typedef struct UA_Server UA_Server;
-struct open62541NodeStore;
-typedef struct open62541NodeStore open62541NodeStore;
-
-
-struct UA_NamespaceManager;
-typedef struct UA_NamespaceManager UA_NamespaceManager;
-
-typedef UA_Int32 (*UA_NodeStore_addNodes)(const UA_RequestHeader *requestHeader, UA_AddNodesItem *nodesToAdd,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos);
-typedef UA_Int32 (*UA_NodeStore_addReferences)(const UA_RequestHeader *requestHeader,UA_AddReferencesItem* referencesToAdd,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_NodeStore_deleteNodes)(const UA_RequestHeader *requestHeader,UA_DeleteNodesItem *nodesToDelete,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *deleteNodesResults, UA_DiagnosticInfo *diagnosticInfos);
-typedef UA_Int32 (*UA_NodeStore_deleteReferences)(const UA_RequestHeader *requestHeader,UA_DeleteReferencesItem *referenceToDelete,UA_UInt32 *indices, UA_UInt32 indicesSize,UA_StatusCode deleteReferencesresults, UA_DiagnosticInfo *diagnosticInfos);
-
-typedef UA_Int32 (*UA_NodeStore_readNodes)(const UA_RequestHeader *requestHeader,UA_ReadValueId *readValueIds,UA_UInt32 *indices,UA_UInt32 indicesSize,UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn, UA_DiagnosticInfo *diagnosticInfos);
-typedef UA_Int32 (*UA_NodeStore_writeNodes)(const UA_RequestHeader *requestHeader,UA_WriteValue *writeValues,UA_UInt32 *indices ,UA_UInt32 indicesSize, UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
-typedef UA_Int32 (*UA_NodeStore_browseNodes)(const UA_RequestHeader *requestHeader,UA_BrowseDescription *browseDescriptions,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode, UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
-
-struct UA_NodeStore {
-	//new, set, get, remove,
-	UA_NodeStore_addNodes addNodes;
-	UA_NodeStore_deleteNodes deleteNodes;
-	UA_NodeStore_writeNodes writeNodes;
-	UA_NodeStore_readNodes readNodes;
-	UA_NodeStore_browseNodes browseNodes;
-	UA_NodeStore_addReferences addReferences;
-	UA_NodeStore_deleteReferences deleteReferences;
-};
-
-
-UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate, UA_NodeStoreInterface *ns0Nodestore,UA_Boolean useOpen62541NodeStore);
 
+UA_Server UA_EXPORT * UA_Server_new(UA_String *endpointUrl, UA_ByteString *serverCertificate);
 void UA_EXPORT UA_Server_delete(UA_Server *server);
 void UA_EXPORT UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
 
 /* Services for local use */
 void UA_EXPORT UA_Server_addScalarVariableNode(UA_Server *server, UA_QualifiedName *browseName, void *value,
-                                                  const UA_VTable_Entry *vt, UA_ExpandedNodeId *parentNodeId,
-                                                  UA_NodeId *referenceTypeId );
-//UA_AddNodesResult UA_EXPORT UA_Server_addNode(UA_Server *server, UA_Node **node, UA_ExpandedNodeId *parentNodeId,
-//		UA_NodeId *referenceTypeId);
-//void UA_EXPORT UA_Server_addReferences(UA_Server *server, const UA_AddReferencesRequest *request,
-//		UA_AddReferencesResponse *response);
-
-UA_Int32 UA_EXPORT UA_Server_addNamespace(UA_Server *server, UA_UInt16 namespaceIndex, UA_NodeStoreInterface *nodeStore);
-
-UA_Int32 UA_EXPORT UA_Server_removeNamespace(UA_Server *server, UA_UInt16 namespaceIndex);
-
-UA_Int32 UA_EXPORT UA_Server_setNodeStore(UA_Server *server, UA_UInt16 namespaceIndex, UA_NodeStoreInterface *nodeStore);
+                                               const UA_VTable_Entry *vt, const UA_ExpandedNodeId *parentNodeId,
+                                               const UA_NodeId *referenceTypeId );
+
+/** @ingroup server
+
+    @defgroup external_nodestore External Nodestore
+
+    To plug in outside data sources, one can use
+
+    - VariableNodes with a data source (functions that are called for read and write access)
+    - An external nodestore that is mapped to specific namespaces
+
+    If no external nodestore is defined for a nodeid, it is always looked up in
+    the "local" nodestore of open62541. Namespace Zero is always in the local nodestore.
+*/
+
+typedef UA_Int32 (*UA_ExternalNodeStore_addNodes)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddNodesItem *nodesToAdd,
+                                                  UA_UInt32 *indices,UA_UInt32 indicesSize, UA_AddNodesResult* addNodesResults,
+                                                  UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_addReferences)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
+                                                       UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
+                                                       UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteNodes)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteNodesItem *nodesToDelete, UA_UInt32 *indices,
+                                                     UA_UInt32 indicesSize, UA_StatusCode *deleteNodesResults, UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_deleteReferences)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteReferencesItem *referenceToDelete,
+                                                          UA_UInt32 *indices, UA_UInt32 indicesSize, UA_StatusCode deleteReferencesresults,
+                                                          UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_readNodes)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_ReadValueId *readValueIds, UA_UInt32 *indices,
+                                                   UA_UInt32 indicesSize,UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn, UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_writeNodes)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_WriteValue *writeValues, UA_UInt32 *indices,
+                                                    UA_UInt32 indicesSize, UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
+typedef UA_Int32 (*UA_ExternalNodeStore_browseNodes)(void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowseDescription *browseDescriptions, UA_UInt32 *indices,
+                                                     UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode, UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
+typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle);
+
+typedef struct UA_ExternalNodeStore {
+    void *ensHandle;
+	UA_ExternalNodeStore_addNodes addNodes;
+	UA_ExternalNodeStore_deleteNodes deleteNodes;
+	UA_ExternalNodeStore_writeNodes writeNodes;
+	UA_ExternalNodeStore_readNodes readNodes;
+	UA_ExternalNodeStore_browseNodes browseNodes;
+	UA_ExternalNodeStore_addReferences addReferences;
+	UA_ExternalNodeStore_deleteReferences deleteReferences;
+	UA_ExternalNodeStore_delete delete;
+} UA_ExternalNodeStore;
+
+UA_StatusCode UA_EXPORT UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, UA_ExternalNodeStore *nodeStore);
 
 #ifdef __cplusplus
 } // extern "C"

+ 0 - 94
src/server/nodestore/open62541_nodestore.h

@@ -1,94 +0,0 @@
-#ifndef UA_OPEN62541_NODESTORE_H_
-#define UA_OPEN62541_NODESTORE_H_
-
-#include "ua_server.h"
-#include "ua_config.h"
-/**
- @ingroup server
-
- @defgroup nodestore NodeStore
-
- @brief The nodestore is the central storage for nodes in the UA address
- space. Internally, the nodestore is realised as hash-map where nodes are
- stored and retrieved with their nodeid.
-
- The nodes in the nodestore are immutable. To change the content of a node, it
- needs to be replaced as a whole. When a node is inserted into the namespace,
- it gets replaced with a pointer to a managed node. Managed nodes shall never
- be freed by the user. This is done by the namespace when the node is removed
- and no readers (in other threads) access the node.
-
- @{
- */
-
-void UA_EXPORT open62541NodeStore_setNodeStore(open62541NodeStore *nodestore);
-open62541NodeStore UA_EXPORT *open62541NodeStore_getNodeStore();
-
-/** @brief Create a new namespace */
-UA_StatusCode UA_EXPORT open62541NodeStore_new(open62541NodeStore **result);
-
-/** @brief Delete the namespace and all nodes in it */
-void open62541NodeStore_delete(open62541NodeStore *ns);
-
-#define UA_NODESTORE_INSERT_UNIQUE 1
-#define UA_NODESTORE_INSERT_GETMANAGED 2
-/** @brief Insert a new node into the namespace
-
- With the UNIQUE flag, the node is only inserted if the nodeid does not
- already exist. With the GETMANAGED flag, the node pointer is replaced with
- the managed pointer. Otherwise, it is set to UA_NULL. */
-UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node,
-		UA_Byte flags);
-
-/** @brief Remove a node from the namespace. Always succeeds, even if the node
- was not found. */
-UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns,
-		const UA_NodeId *nodeid);
-
-/** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
- They can only be replaced. After the Node is no longer used, the locked
- entry needs to be released. */
-UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns,
-		const UA_NodeId *nodeid, const UA_Node **managedNode);
-
-/** @brief Release a managed node. Do never insert a node that isn't stored in a
- namespace. */
-void open62541NodeStore_release(const UA_Node *managed);
-
-/** @brief A function that can be evaluated on all entries in a namespace via
- UA_NodeStore_iterate. Note that the visitor is read-only on the nodes. */
-typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
-
-/** @brief Iterate over all nodes in a namespace. */
-void open62541NodeStore_iterate(const open62541NodeStore *ns,
-		UA_NodeStore_nodeVisitor visitor);
-
-/// @} /* end of group */
-
-//service implementations
-UA_Int32 UA_EXPORT open62541NodeStore_ReadNodes(const UA_RequestHeader *requestHeader,
-		UA_ReadValueId *readValueIds,
-		UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn,
-		UA_DiagnosticInfo *diagnosticInfos);
-
-UA_Int32 UA_EXPORT open62541NodeStore_WriteNodes(const UA_RequestHeader *requestHeader,
-		UA_WriteValue *writeValues,
-		UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo);
-
-UA_Int32 UA_EXPORT open62541NodeStore_BrowseNodes(const UA_RequestHeader *requestHeader,
-		UA_BrowseDescription *browseDescriptions, UA_UInt32 *indices,
-		UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
-		UA_BrowseResult *browseResults, UA_DiagnosticInfo *diagnosticInfos);
-
-UA_Int32 UA_EXPORT open62541NodeStore_AddNodes(const UA_RequestHeader *requestHeader,
-		UA_AddNodesItem *nodesToAdd,
-		UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos);
-
-UA_Int32 UA_EXPORT open62541NodeStore_AddReferences(const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
-		UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
-		UA_DiagnosticInfo *diagnosticInfos);
-
-#endif /* UA_OPEN62541_NODESTORE_H_ */

+ 0 - 376
src/server/nodestore/open62541_nodestore_attribute.c

@@ -1,376 +0,0 @@
-/*
- * nodestore_attribute.c
- *
- *  Created on: Oct 27, 2014
- *      Author: opcua
- */
-
-#include "../ua_services.h"
-#include "open62541_nodestore.h"
-#include "ua_namespace_0.h"
-#include "ua_util.h"
-#define CHECK_NODECLASS(CLASS)                                 \
-    if(!(node->nodeClass & (CLASS))) {                         \
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; \
-        v.status       = UA_STATUSCODE_BADNOTREADABLE;         \
-        break;                                                 \
-    }                                                          \
-
-static UA_DataValue service_read_node(UA_Server *server,
-		const UA_ReadValueId *id) {
-	UA_DataValue v;
-	UA_DataValue_init(&v);
-
-	UA_Node const *node = UA_NULL;
-	open62541NodeStore *ns = open62541NodeStore_getNodeStore();
-	UA_Int32 result = open62541NodeStore_get(ns, &(id->nodeId), &node);
-	if (result != UA_STATUSCODE_GOOD || node == UA_NULL) {
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADNODEIDUNKNOWN;
-		return v;
-	}
-	UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
-	switch (id->attributeId) {
-	case UA_ATTRIBUTEID_NODEID:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID],
-				&node->nodeId);
-		break;
-
-	case UA_ATTRIBUTEID_NODECLASS:
-
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32],
-				&node->nodeClass);
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		break;
-
-	case UA_ATTRIBUTEID_BROWSENAME:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_QUALIFIEDNAME],
-				&node->browseName);
-		break;
-
-	case UA_ATTRIBUTEID_DISPLAYNAME:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&node->displayName);
-		break;
-
-	case UA_ATTRIBUTEID_DESCRIPTION:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&node->description);
-		break;
-
-	case UA_ATTRIBUTEID_WRITEMASK:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32],
-				&node->writeMask);
-		break;
-
-	case UA_ATTRIBUTEID_USERWRITEMASK:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32],
-				&node->userWriteMask);
-		break;
-
-	case UA_ATTRIBUTEID_ISABSTRACT:
-		CHECK_NODECLASS(
-				UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE
-						| UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ReferenceTypeNode *) node)->isAbstract);
-		break;
-
-	case UA_ATTRIBUTEID_SYMMETRIC:
-		CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ReferenceTypeNode *) node)->symmetric);
-		break;
-
-	case UA_ATTRIBUTEID_INVERSENAME:
-		CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&((UA_ReferenceTypeNode *) node)->inverseName);
-		break;
-
-	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-		CHECK_NODECLASS(UA_NODECLASS_VIEW)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ViewNode *) node)->containsNoLoops);
-		break;
-
-	case UA_ATTRIBUTEID_EVENTNOTIFIER:
-		CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_ViewNode *) node)->eventNotifier);
-		break;
-
-	case UA_ATTRIBUTEID_VALUE:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copy(&((UA_VariableNode *) node)->value, &v.value); // todo: zero-copy
-		break;
-
-	case UA_ATTRIBUTEID_DATATYPE:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID],
-				&((UA_VariableTypeNode *) node)->dataType);
-		break;
-
-	case UA_ATTRIBUTEID_VALUERANK:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32],
-				&((UA_VariableTypeNode *) node)->valueRank);
-		break;
-
-	case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		UA_Variant_copySetArray(&v.value, &UA_TYPES[UA_UINT32],
-				((UA_VariableTypeNode *) node)->arrayDimensionsSize,
-				&((UA_VariableTypeNode *) node)->arrayDimensions);
-		break;
-
-	case UA_ATTRIBUTEID_ACCESSLEVEL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_VariableNode *) node)->accessLevel);
-		break;
-
-	case UA_ATTRIBUTEID_USERACCESSLEVEL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_VariableNode *) node)->userAccessLevel);
-		break;
-
-	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_DOUBLE],
-				&((UA_VariableNode *) node)->minimumSamplingInterval);
-		break;
-
-	case UA_ATTRIBUTEID_HISTORIZING:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_VariableNode *) node)->historizing);
-		break;
-
-	case UA_ATTRIBUTEID_EXECUTABLE:
-		CHECK_NODECLASS(UA_NODECLASS_METHOD)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_MethodNode *) node)->executable);
-		break;
-
-	case UA_ATTRIBUTEID_USEREXECUTABLE:
-		CHECK_NODECLASS(UA_NODECLASS_METHOD)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_MethodNode *) node)->userExecutable);
-		break;
-
-	default:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-		break;
-	}
-
-	open62541NodeStore_release(node);
-
-	if (retval != UA_STATUSCODE_GOOD) {
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADNOTREADABLE;
-	}
-
-	return v;
-}
-
-UA_Int32 open62541NodeStore_ReadNodes(const UA_RequestHeader *requestHeader,
-		UA_ReadValueId *readValueIds,
-		UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_DataValue *readNodesResults, UA_Boolean timeStampToReturn,
-		UA_DiagnosticInfo *diagnosticInfos) {
-	for (UA_UInt32 i = 0; i < indicesSize; i++) {
-		readNodesResults[indices[i]] = service_read_node(UA_NULL,
-				&readValueIds[indices[i]]);
-	}
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode Service_Write_writeNode(open62541NodeStore *nodestore,
-		UA_WriteValue *writeValue) {
-	UA_StatusCode retval = UA_STATUSCODE_GOOD;
-	const UA_Node *node;
-	retval = open62541NodeStore_get(nodestore, &writeValue->nodeId, &node);
-	if (retval)
-		return retval;
-
-	switch (writeValue->attributeId) {
-	case UA_ATTRIBUTEID_NODEID:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_NODECLASS:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_BROWSENAME:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_DISPLAYNAME:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_DESCRIPTION:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_WRITEMASK:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_USERWRITEMASK:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_ISABSTRACT:
-
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_SYMMETRIC:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_INVERSENAME:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_EVENTNOTIFIER:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_VALUE:
-		if (writeValue->value.encodingMask
-				== UA_DATAVALUE_ENCODINGMASK_VARIANT) {
-			retval |= UA_Variant_copy(&writeValue->value.value,
-					&((UA_VariableNode *) node)->value); // todo: zero-copy
-		}
-		break;
-
-	case UA_ATTRIBUTEID_DATATYPE:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_VALUERANK:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_ACCESSLEVEL:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_USERACCESSLEVEL:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_HISTORIZING:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_EXECUTABLE:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	case UA_ATTRIBUTEID_USEREXECUTABLE:
-		/* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-		break;
-
-	default:
-		retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-		break;
-	}
-
-	open62541NodeStore_release(node);
-	return retval;
-
-}
-UA_Int32 UA_EXPORT open62541NodeStore_WriteNodes(
-		const UA_RequestHeader *requestHeader, UA_WriteValue *writeValues,
-		UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_StatusCode *writeNodesResults, UA_DiagnosticInfo *diagnosticInfo) {
-	open62541NodeStore *nodestore = UA_NULL;
-	nodestore = open62541NodeStore_getNodeStore();
-	if (nodestore == UA_NULL) {
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-
-	for (UA_UInt32 i = 0; i < indicesSize; i++) {
-		writeNodesResults[indices[i]] = Service_Write_writeNode(
-				open62541NodeStore_getNodeStore(), &writeValues[indices[i]]);
-	}
-	return UA_STATUSCODE_GOOD; //
-}
-

+ 0 - 357
src/server/nodestore/open62541_nodestore_concurrent_core.c

@@ -1,357 +0,0 @@
-#include "open62541_nodestore.h"
-#include "ua_util.h"
-
-#include <urcu.h>
-#include <urcu/compiler.h> // for caa_container_of
-#include <urcu/uatomic.h>
-#include <urcu/rculfhash.h>
-
-#define ALIVE_BIT (1 << 15) /* Alive bit in the readcount */
-
-typedef struct open62541NodeStore_Entry {
-    struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
-    struct rcu_head      rcu_head; /* For call-rcu */
-    UA_UInt16 readcount;           /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
-    UA_Node   node;                /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
-} open62541NodeStore_Entry;
-
-struct open62541NodeStore {
-    struct cds_lfht *ht; /* Hash table */
-};
-
-/********/
-/* Hash */
-/********/
-
-typedef UA_UInt32 hash_t;
-
-/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
-    static const uint32_t c1 = 0xcc9e2d51;
-    static const uint32_t c2 = 0x1b873593;
-    static const uint32_t r1 = 15;
-    static const uint32_t r2 = 13;
-    static const uint32_t m  = 5;
-    static const uint32_t n  = 0xe6546b64;
-    hash_t hash = seed;
-
-    if(data == UA_NULL) return 0;
-
-    const int32_t   nblocks = len / 4;
-    const uint32_t *blocks  = (const uint32_t *)data;
-    for(int32_t i = 0;i < nblocks;i++) {
-        uint32_t k = blocks[i];
-        k    *= c1;
-        k     = (k << r1) | (k >> (32 - r1));
-        k    *= c2;
-        hash ^= k;
-        hash  = ((hash << r2) | (hash >> (32 - r2))) * m + n;
-    }
-
-    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
-    uint32_t       k1   = 0;
-
-    switch(len & 3) {
-    case 3:
-        k1 ^= tail[2] << 16;
-
-    case 2:
-        k1 ^= tail[1] << 8;
-
-    case 1:
-        k1   ^= tail[0];
-        k1   *= c1;
-        k1    = (k1 << r1) | (k1 >> (32 - r1));
-        k1   *= c2;
-        hash ^= k1;
-    }
-
-    hash ^= len;
-    hash ^= (hash >> 16);
-    hash *= 0x85ebca6b;
-    hash ^= (hash >> 13);
-    hash *= 0xc2b2ae35;
-    hash ^= (hash >> 16);
-
-    return hash;
-}
-
-static INLINE hash_t hash(const UA_NodeId *n) {
-    switch(n->identifierType) {
-    case UA_NODEIDTYPE_NUMERIC:
-        /*  Knuth's multiplicative hashing */
-        return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
-
-    case UA_NODEIDTYPE_STRING:
-        return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
-
-    case UA_NODEIDTYPE_GUID:
-        return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
-
-    case UA_NODEIDTYPE_BYTESTRING:
-        return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
-
-    default:
-        UA_assert(UA_FALSE);
-        return 0;
-    }
-}
-
-/****************/
-/* open62541NodeStore */
-/****************/
-
-static inline void node_deleteMembers(const UA_Node *node) {
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_deleteMembers((UA_ObjectNode *)node);
-        break;
-
-    case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_deleteMembers((UA_VariableNode *)node);
-        break;
-
-    case UA_NODECLASS_METHOD:
-        UA_MethodNode_deleteMembers((UA_MethodNode *)node);
-        break;
-
-    case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_deleteMembers((UA_DataTypeNode *)node);
-        break;
-
-    case UA_NODECLASS_VIEW:
-        UA_ViewNode_deleteMembers((UA_ViewNode *)node);
-        break;
-
-    default:
-        UA_assert(UA_FALSE);
-        break;
-    }
-}
-
-/* We are in a rcu_read lock. So the node will not be freed under our feet. */
-static int compare(struct cds_lfht_node *htn, const void *orig) {
-    UA_NodeId *origid = (UA_NodeId *)orig;
-    UA_NodeId *newid  = &((open62541NodeStore_Entry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
-
-    return UA_NodeId_equal(newid, origid);
-}
-
-/* The entry was removed from the hashtable. No more readers can get it. Since
-   all readers using the node for a longer time (outside the rcu critical
-   section) increased the readcount, we only need to wait for the readcount
-   to reach zero. */
-static void markDead(struct rcu_head *head) {
-    open62541NodeStore_Entry *entry = caa_container_of(head, open62541NodeStore_Entry, rcu_head);
-    if(uatomic_sub_return(&entry->readcount, ALIVE_BIT) > 0)
-        return;
-
-    node_deleteMembers(&entry->node);
-    UA_free(entry);
-    return;
-}
-
-/* Free the entry if it is dead and nobody uses it anymore */
-void open62541NodeStore_release(const UA_Node *managed) {
-    open62541NodeStore_Entry *entry = caa_container_of(managed, open62541NodeStore_Entry, node); // pointer to the first entry
-    if(uatomic_sub_return(&entry->readcount, 1) > 0)
-        return;
-
-    node_deleteMembers(managed);
-    UA_free(entry);
-    return;
-}
-
-UA_StatusCode open62541NodeStore_new(open62541NodeStore **result) {
-    open62541NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(open62541NodeStore))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    /* 32 is the minimum size for the hashtable. */
-    ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, UA_NULL);
-    if(!ns->ht) {
-        UA_free(ns);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-
-    *result = ns;
-    return UA_STATUSCODE_GOOD;
-}
-
-void open62541NodeStore_delete(open62541NodeStore *ns) {
-    struct cds_lfht      *ht = ns->ht;
-    struct cds_lfht_iter  iter;
-    struct cds_lfht_node *found_htn;
-
-    rcu_read_lock();
-    cds_lfht_first(ht, &iter);
-    while(iter.node != UA_NULL) {
-        found_htn = cds_lfht_iter_get_node(&iter);
-        if(!cds_lfht_del(ht, found_htn)) {
-            open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
-            call_rcu(&entry->rcu_head, markDead);
-        }
-        cds_lfht_next(ht, &iter);
-    }
-    rcu_read_unlock();
-
-    cds_lfht_destroy(ht, UA_NULL);
-    UA_free(ns);
-}
-
-UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node, UA_Byte flags) {
-    UA_UInt32 nodesize;
-    /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
-    switch((*node)->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        nodesize = sizeof(UA_ObjectNode);
-        break;
-
-    case UA_NODECLASS_VARIABLE:
-        nodesize = sizeof(UA_VariableNode);
-        break;
-
-    case UA_NODECLASS_METHOD:
-        nodesize = sizeof(UA_MethodNode);
-        break;
-
-    case UA_NODECLASS_OBJECTTYPE:
-        nodesize = sizeof(UA_ObjectTypeNode);
-        break;
-
-    case UA_NODECLASS_VARIABLETYPE:
-        nodesize = sizeof(UA_VariableTypeNode);
-        break;
-
-    case UA_NODECLASS_REFERENCETYPE:
-        nodesize = sizeof(UA_ReferenceTypeNode);
-        break;
-
-    case UA_NODECLASS_DATATYPE:
-        nodesize = sizeof(UA_DataTypeNode);
-        break;
-
-    case UA_NODECLASS_VIEW:
-        nodesize = sizeof(UA_ViewNode);
-        break;
-
-    default:
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    open62541NodeStore_Entry *entry;
-    if(!(entry = UA_alloc(sizeof(open62541NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    memcpy(&entry->node, *node, nodesize);
-
-    cds_lfht_node_init(&entry->htn);
-    entry->readcount = ALIVE_BIT;
-    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
-        entry->readcount++;
-
-    hash_t nhash = hash(&(*node)->nodeId);
-    struct cds_lfht_node *result;
-    if(flags & UA_NODESTORE_INSERT_UNIQUE) {
-        rcu_read_lock();
-        result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
-        rcu_read_unlock();
-
-        /* If the nodeid exists already */
-        if(result != &entry->htn) {
-            UA_free(entry);
-            return UA_STATUSCODE_BADNODEIDEXISTS;
-        }
-    } else {
-        rcu_read_lock();
-        result = cds_lfht_add_replace(ns->ht, nhash, compare, &(*node)->nodeId, &entry->htn);
-        /* If an entry got replaced, mark it as dead. */
-        if(result) {
-            open62541NodeStore_Entry *entry = caa_container_of(result, open62541NodeStore_Entry, htn);
-            call_rcu(&entry->rcu_head, markDead);      /* Queue this for the next time when no readers are on the entry.*/
-        }
-        rcu_read_unlock();
-    }
-
-    UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
-    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
-        *node = &entry->node;
-    else
-        *node = UA_NULL;
-
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns, const UA_NodeId *nodeid) {
-    hash_t nhash = hash(nodeid);
-    struct cds_lfht_iter iter;
-
-    rcu_read_lock();
-    cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
-    struct cds_lfht_node *found_htn = cds_lfht_iter_get_node(&iter);
-
-    /* If this fails, then the node has already been removed. */
-    if(!found_htn || cds_lfht_del(ns->ht, found_htn) != 0) {
-        rcu_read_unlock();
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    }
-
-    open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
-    call_rcu(&entry->rcu_head, markDead);
-    rcu_read_unlock();
-
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
-    hash_t nhash = hash(nodeid);
-    struct cds_lfht_iter iter;
-
-    rcu_read_lock();
-    cds_lfht_lookup(ns->ht, nhash, compare, nodeid, &iter);
-    open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
-
-    if(!found_entry) {
-        rcu_read_unlock();
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    }
-
-    /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
-    uatomic_inc(&found_entry->readcount);
-    rcu_read_unlock();
-
-    *managedNode = &found_entry->node;
-    return UA_STATUSCODE_GOOD;
-}
-
-void open62541NodeStore_iterate(const open62541NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    struct cds_lfht     *ht = ns->ht;
-    struct cds_lfht_iter iter;
-
-    rcu_read_lock();
-    cds_lfht_first(ht, &iter);
-    while(iter.node != UA_NULL) {
-        open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
-        uatomic_inc(&found_entry->readcount);
-        const UA_Node      *node = &found_entry->node;
-        rcu_read_unlock();
-        visitor(node);
-        open62541NodeStore_release((UA_Node *)node);
-        rcu_read_lock();
-        cds_lfht_next(ht, &iter);
-    }
-    rcu_read_unlock();
-}
-
-

+ 0 - 366
src/server/nodestore/open62541_nodestore_core.c

@@ -1,366 +0,0 @@
-#include "open62541_nodestore.h"
-#include "ua_util.h"
-#include "ua_statuscodes.h"
-
-struct open62541NodeStore {
-	const UA_Node **entries;
-	UA_UInt32 size;
-	UA_UInt32 count;
-	UA_UInt32 sizePrimeIndex;
-};
-
-
-
-static open62541NodeStore *open62541_nodestore;
-
-void open62541NodeStore_setNodeStore(open62541NodeStore *nodestore){
-	open62541_nodestore = nodestore;
-}
-
-open62541NodeStore *open62541NodeStore_getNodeStore(){
-	return open62541_nodestore;
-}
-
-
-
-typedef UA_UInt32 hash_t;
-/* The size of the hash-map is always a prime number. They are chosen to be
- close to the next power of 2. So the size ca. doubles with each prime. */
-static hash_t const primes[] = { 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093,
-		8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143,
-		4194301, 8388593, 16777213, 33554393, 67108859, 134217689, 268435399,
-		536870909, 1073741789, 2147483647, 4294967291 };
-
-static INLINE hash_t mod(hash_t h, hash_t size) {
-	return h % size;
-}
-static INLINE hash_t mod2(hash_t h, hash_t size) {
-	return 1 + (h % (size - 2));
-}
-
-static INLINE UA_Int16 higher_prime_index(hash_t n) {
-	UA_UInt16 low = 0;
-	UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
-	while (low != high) {
-		UA_UInt16 mid = low + (high - low) / 2;
-		if (n > primes[mid])
-			low = mid + 1;
-		else
-			high = mid;
-	}
-	return low;
-}
-
-/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len,
-		UA_UInt32 seed) {
-	const int32_t nblocks = len / 4;
-	const uint32_t *blocks;
-
-	static const uint32_t c1 = 0xcc9e2d51;
-	static const uint32_t c2 = 0x1b873593;
-	static const uint32_t r1 = 15;
-	static const uint32_t r2 = 13;
-	static const uint32_t m = 5;
-	static const uint32_t n = 0xe6546b64;
-	hash_t hash = seed;
-
-	if (data == UA_NULL)
-		return 0;
-
-	blocks = (const uint32_t *) data;
-	for (int32_t i = 0; i < nblocks; i++) {
-		uint32_t k = blocks[i];
-		k *= c1;
-		k = (k << r1) | (k >> (32 - r1));
-		k *= c2;
-		hash ^= k;
-		hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
-	}
-
-	const uint8_t *tail = (const uint8_t *) (data + nblocks * 4);
-	uint32_t k1 = 0;
-
-	switch (len & 3) {
-	case 3:
-		k1 ^= tail[2] << 16;
-
-	case 2:
-		k1 ^= tail[1] << 8;
-
-	case 1:
-		k1 ^= tail[0];
-		k1 *= c1;
-		k1 = (k1 << r1) | (k1 >> (32 - r1));
-		k1 *= c2;
-		hash ^= k1;
-	}
-
-	hash ^= len;
-	hash ^= (hash >> 16);
-	hash *= 0x85ebca6b;
-	hash ^= (hash >> 13);
-	hash *= 0xc2b2ae35;
-	hash ^= (hash >> 16);
-
-	return hash;
-}
-
-static INLINE hash_t hash(const UA_NodeId *n) {
-	switch (n->identifierType) {
-	case UA_NODEIDTYPE_NUMERIC:
-		/*  Knuth's multiplicative hashing */
-		return (n->identifier.numeric + n->namespaceIndex) * 2654435761; // mod(2^32) is implicit
-
-	case UA_NODEIDTYPE_STRING:
-		return hash_array(n->identifier.string.data,
-				n->identifier.string.length, n->namespaceIndex);
-
-	case UA_NODEIDTYPE_GUID:
-		return hash_array((UA_Byte *) &(n->identifier.guid), sizeof(UA_Guid),
-				n->namespaceIndex);
-
-	case UA_NODEIDTYPE_BYTESTRING:
-		return hash_array((UA_Byte *) n->identifier.byteString.data,
-				n->identifier.byteString.length, n->namespaceIndex);
-
-	default:
-		UA_assert(UA_FALSE);
-		return 0;
-	}
-}
-
-static INLINE void clear_entry(open62541NodeStore *ns, const UA_Node **entry) {
-	const UA_Node *node;
-	if (entry == UA_NULL || *entry == UA_NULL)
-		return;
-
-	node = *entry;
-	switch (node->nodeClass) {
-	case UA_NODECLASS_OBJECT:
-		UA_ObjectNode_delete((UA_ObjectNode *) node);
-		break;
-
-	case UA_NODECLASS_VARIABLE:
-		UA_VariableNode_delete((UA_VariableNode *) node);
-		break;
-
-	case UA_NODECLASS_METHOD:
-		UA_MethodNode_delete((UA_MethodNode *) node);
-		break;
-
-	case UA_NODECLASS_OBJECTTYPE:
-		UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) node);
-		break;
-
-	case UA_NODECLASS_VARIABLETYPE:
-		UA_VariableTypeNode_delete((UA_VariableTypeNode *) node);
-		break;
-
-	case UA_NODECLASS_REFERENCETYPE:
-		UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) node);
-		break;
-
-	case UA_NODECLASS_DATATYPE:
-		UA_DataTypeNode_delete((UA_DataTypeNode *) node);
-		break;
-
-	case UA_NODECLASS_VIEW:
-		UA_ViewNode_delete((UA_ViewNode *) node);
-		break;
-
-	default:
-		UA_assert(UA_FALSE);
-		break;
-	}
-	entry = UA_NULL;
-	ns->count--;
-}
-
-/* Returns UA_SUCCESS if an entry was found. Otherwise, UA_ERROR is returned and the "entry"
- argument points to the first free entry under the NodeId. */
-static INLINE UA_StatusCode find_entry(const open62541NodeStore *ns,
-		const UA_NodeId *nodeid, const UA_Node ***entry) {
-	hash_t h = hash(nodeid);
-	UA_UInt32 size = ns->size;
-	hash_t index = mod(h, size);
-	const UA_Node **e = &ns->entries[index];
-
-	if (*e == UA_NULL) {
-		*entry = e;
-		return UA_STATUSCODE_BADINTERNALERROR;
-	}
-
-	if (UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
-		*entry = e;
-		return UA_STATUSCODE_GOOD;
-	}
-
-	hash_t hash2 = mod2(h, size);
-	for (;;) {
-		index += hash2;
-		if (index >= size)
-			index -= size;
-
-		e = &ns->entries[index];
-
-		if (*e == UA_NULL) {
-			*entry = e;
-			return UA_STATUSCODE_BADINTERNALERROR;
-		}
-
-		if (UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
-			*entry = e;
-			return UA_STATUSCODE_GOOD;
-		}
-	}
-
-	/* NOTREACHED */
-	return UA_STATUSCODE_GOOD;
-}
-
-/* The following function changes size of memory allocated for the entries and
- repeatedly inserts the table elements. The occupancy of the table after the
- call will be about 50%. If memory allocation failures occur, this function
- will return UA_ERROR. */
-static UA_StatusCode expand(open62541NodeStore *ns) {
-	const UA_Node **nentries;
-	int32_t nsize;
-	UA_UInt32 nindex;
-
-	const UA_Node **oentries = ns->entries;
-	int32_t osize = ns->size;
-	const UA_Node **olimit = &oentries[osize];
-	int32_t count = ns->count;
-
-	/* Resize only when table after removal of unused elements is either too full or too empty.  */
-	if (count * 2 < osize && (count * 8 > osize || osize <= 32))
-		return UA_STATUSCODE_GOOD;
-
-	nindex = higher_prime_index(count * 2);
-	nsize = primes[nindex];
-
-	if (!(nentries = UA_alloc(sizeof(UA_Node *) * nsize)))
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-
-	memset(nentries, 0, nsize * sizeof(UA_Node *));
-	ns->entries = nentries;
-	ns->size = nsize;
-	ns->sizePrimeIndex = nindex;
-
-	const UA_Node **p = oentries;
-	do {
-		if (*p != UA_NULL) {
-			const UA_Node **e;
-			find_entry(ns, &(*p)->nodeId, &e); /* We know this returns an empty entry here */
-			*e = *p;
-		}
-		p++;
-	} while (p < olimit);
-
-	UA_free(oentries);
-	return UA_STATUSCODE_GOOD;
-}
-
-/**********************/
-/* Exported functions */
-/**********************/
-
-UA_StatusCode open62541NodeStore_new(open62541NodeStore **result) {
-	open62541NodeStore *ns;
-	UA_UInt32 sizePrimeIndex, size;
-	if (!(ns = UA_alloc(sizeof(UA_NodeStoreInterface))))
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-
-	sizePrimeIndex = higher_prime_index(32);
-	size = primes[sizePrimeIndex];
-	if (!(ns->entries = UA_alloc(sizeof(UA_Node *) * size))) {
-		UA_free(ns);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-
-	/* set entries to zero */
-	memset(ns->entries, 0, size * sizeof(UA_Node *));
-
-	*ns = (open62541NodeStore ) { ns->entries, size, 0, sizePrimeIndex };
-	*result = ns;
-	return UA_STATUSCODE_GOOD;
-}
-
-void open62541NodeStore_delete(open62541NodeStore *ns) {
-	UA_UInt32 size = ns->size;
-	const UA_Node **entries = ns->entries;
-
-	for (UA_UInt32 i = 0; i < size; i++)
-		clear_entry(ns, &entries[i]);
-
-	UA_free(ns->entries);
-	UA_free(ns);
-}
-
-UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node,
-		UA_Byte flags) {
-	if (ns->size * 3 <= ns->count * 4) {
-		if (expand(ns) != UA_STATUSCODE_GOOD)
-			return UA_STATUSCODE_BADINTERNALERROR;
-	}
-
-	const UA_Node **entry;
-	UA_Int32 found = find_entry(ns, &(*node)->nodeId, &entry);
-
-	if (flags & UA_NODESTORE_INSERT_UNIQUE) {
-		if (found == UA_STATUSCODE_GOOD)
-			return UA_STATUSCODE_BADNODEIDEXISTS;
-		else
-			*entry = *node;
-	} else {
-		if (found == UA_STATUSCODE_GOOD)
-			clear_entry(ns, entry);
-		*entry = *node;
-	}
-
-	if (!(flags & UA_NODESTORE_INSERT_GETMANAGED))
-		*node = UA_NULL;
-
-	ns->count++;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns,
-		const UA_NodeId *nodeid, const UA_Node **managedNode) {
-	const UA_Node **entry;
-	if (find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-	*managedNode = *entry;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns,
-		const UA_NodeId *nodeid) {
-	const UA_Node **entry;
-	if (find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-	// Check before if deleting the node makes the UA_NodeStore inconsistent.
-	clear_entry(ns, entry);
-
-	/* Downsize the hashmap if it is very empty */
-	if (ns->count * 8 < ns->size && ns->size > 32)
-		expand(ns); // this can fail. we just continue with the bigger hashmap.
-
-	return UA_STATUSCODE_GOOD;
-}
-
-void open62541NodeStore_iterate(const open62541NodeStore *ns,
-		UA_NodeStore_nodeVisitor visitor) {
-	for (UA_UInt32 i = 0; i < ns->size; i++) {
-		const UA_Node *node = ns->entries[i];
-		if (node != UA_NULL)
-			visitor(node);
-	}
-}
-
-void open62541NodeStore_release(const UA_Node *managed) {
-	;
-}

+ 0 - 408
src/server/nodestore/open62541_nodestore_nodemanagement.c

@@ -1,408 +0,0 @@
-/*
- * open62541_nodestore_nodemanagement.c
- *
- *  Created on: Oct 27, 2014
- *      Author: opcua
- */
-
-#include "../ua_services.h"
-#include "open62541_nodestore.h"
-#include "ua_namespace_0.h"
-#include "ua_util.h"
-
-static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
-	// TODO: Check if reference already exists
-	UA_Int32 count = node->referencesSize;
-	UA_ReferenceNode *old_refs = node->references;
-	UA_ReferenceNode *new_refs;
-
-	if (count < 0)
-		count = 0;
-
-	if (!(new_refs = UA_alloc(sizeof(UA_ReferenceNode) * (count + 1))))
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-
-	UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode) * count);
-	if (UA_ReferenceNode_copy(reference, &new_refs[count])
-			!= UA_STATUSCODE_GOOD) {
-		UA_free(new_refs);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-
-	node->references = new_refs;
-	node->referencesSize = count + 1;
-	UA_free(old_refs);
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_Int32 AddReference(open62541NodeStore *nodestore, UA_Node *node,
-		UA_ReferenceNode *reference) {
-	UA_Int32 retval = AddSingleReference(node, reference);
-	UA_Node *targetnode;
-	UA_ReferenceNode inversereference;
-	if (retval != UA_STATUSCODE_GOOD || nodestore == UA_NULL)
-		return retval;
-
-	// Do a copy every time?
-	if (open62541NodeStore_get(nodestore, &reference->targetId.nodeId,
-			(const UA_Node **) &targetnode) != UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADINTERNALERROR;
-
-	inversereference.referenceTypeId = reference->referenceTypeId;
-	inversereference.isInverse = !reference->isInverse;
-	inversereference.targetId.nodeId = node->nodeId;
-	inversereference.targetId.namespaceUri = UA_STRING_NULL;
-	inversereference.targetId.serverIndex = 0;
-	retval = AddSingleReference(targetnode, &inversereference);
-	open62541NodeStore_release(targetnode);
-
-	return retval;
-}
-
-#define COPY_STANDARDATTRIBUTES do { \
-if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) { \
-vnode->displayName = attr.displayName; \
-UA_LocalizedText_init(&attr.displayName); \
-} \
-if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DESCRIPTION) { \
-vnode->description = attr.description; \
-UA_LocalizedText_init(&attr.description); \
-} \
-if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_WRITEMASK) \
-vnode->writeMask = attr.writeMask; \
-if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERWRITEMASK) \
-vnode->userWriteMask = attr.userWriteMask; \
-} while(0)
-
-static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes,
-		UA_Node **new_node, const UA_VTable_Entry **vt) {
-	if (attributes->typeId.identifier.numeric != 357) // VariableAttributes_Encoding_DefaultBinary,357,Object
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_VariableAttributes attr;
-	UA_UInt32 pos = 0;
-// todo return more informative error codes from decodeBinary
-	if (UA_VariableAttributes_decodeBinary(&attributes->body, &pos, &attr)
-			!= UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_VariableNode *vnode = UA_VariableNode_new();
-	if (!vnode) {
-		UA_VariableAttributes_deleteMembers(&attr);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-// now copy all the attributes. This potentially removes them from the decoded attributes.
-	COPY_STANDARDATTRIBUTES;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ACCESSLEVEL)
-		vnode->accessLevel = attr.accessLevel;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERACCESSLEVEL)
-		vnode->userAccessLevel = attr.userAccessLevel;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_HISTORIZING)
-		vnode->historizing = attr.historizing;
-	if (attr.specifiedAttributes
-			& UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL)
-		vnode->minimumSamplingInterval = attr.minimumSamplingInterval;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUERANK)
-		vnode->valueRank = attr.valueRank;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ARRAYDIMENSIONS) {
-		vnode->arrayDimensionsSize = attr.arrayDimensionsSize;
-		vnode->arrayDimensions = attr.arrayDimensions;
-		attr.arrayDimensionsSize = -1;
-		attr.arrayDimensions = UA_NULL;
-	}
-	if ((attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DATATYPE)
-			|| (attr.specifiedAttributes
-					& UA_NODEATTRIBUTESMASK_OBJECTTYPEORDATATYPE)) {
-		vnode->dataType = attr.dataType;
-		UA_NodeId_init(&attr.dataType);
-	}
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUE) {
-		vnode->value = attr.value;
-		UA_Variant_init(&attr.value);
-	}
-	UA_VariableAttributes_deleteMembers(&attr);
-	*new_node = (UA_Node*) vnode;
-	*vt = &UA_TYPES[UA_VARIABLENODE];
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode parseObjectNode(UA_ExtensionObject *attributes,
-		UA_Node **new_node, const UA_VTable_Entry **vt) {
-	if (attributes->typeId.identifier.numeric != 354) // VariableAttributes_Encoding_DefaultBinary,357,Object
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_ObjectAttributes attr;
-	UA_UInt32 pos = 0;
-// todo return more informative error codes from decodeBinary
-	if (UA_ObjectAttributes_decodeBinary(&attributes->body, &pos, &attr)
-			!= UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_ObjectNode *vnode = UA_ObjectNode_new();
-	if (!vnode) {
-		UA_ObjectAttributes_deleteMembers(&attr);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-// now copy all the attributes. This potentially removes them from the decoded attributes.
-	COPY_STANDARDATTRIBUTES;
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_EVENTNOTIFIER) {
-		vnode->eventNotifier = attr.eventNotifier;
-	}
-	UA_ObjectAttributes_deleteMembers(&attr);
-	*new_node = (UA_Node*) vnode;
-	*vt = &UA_TYPES[UA_OBJECTNODE];
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode parseReferenceTypeNode(UA_ExtensionObject *attributes,
-		UA_Node **new_node, const UA_VTable_Entry **vt) {
-
-	UA_ReferenceTypeAttributes attr;
-	UA_UInt32 pos = 0;
-// todo return more informative error codes from decodeBinary
-	if (UA_ReferenceTypeAttributes_decodeBinary(&attributes->body, &pos, &attr)
-			!= UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_ReferenceTypeNode *vnode = UA_ReferenceTypeNode_new();
-	if (!vnode) {
-		UA_ReferenceTypeAttributes_deleteMembers(&attr);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-// now copy all the attributes. This potentially removes them from the decoded attributes.
-	COPY_STANDARDATTRIBUTES;
-
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ISABSTRACT) {
-		vnode->isAbstract = attr.isAbstract;
-	}
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_SYMMETRIC) {
-		vnode->symmetric = attr.symmetric;
-	}
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_INVERSENAME) {
-		vnode->inverseName = attr.inverseName;
-
-		attr.inverseName.text.length = -1;
-		attr.inverseName.text.data = UA_NULL;
-
-		attr.inverseName.locale.length = -1;
-		attr.inverseName.locale.data = UA_NULL;
-	}
-
-	UA_ReferenceTypeAttributes_deleteMembers(&attr);
-	*new_node = (UA_Node*) vnode;
-	*vt = &UA_TYPES[UA_REFERENCETYPENODE];
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode parseObjectTypeNode(UA_ExtensionObject *attributes,
-		UA_Node **new_node, const UA_VTable_Entry **vt) {
-
-	UA_ObjectTypeAttributes attr;
-	UA_UInt32 pos = 0;
-// todo return more informative error codes from decodeBinary
-	if (UA_ObjectTypeAttributes_decodeBinary(&attributes->body, &pos, &attr)
-			!= UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_ObjectTypeNode *vnode = UA_ObjectTypeNode_new();
-	if (!vnode) {
-		UA_ObjectTypeAttributes_deleteMembers(&attr);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-// now copy all the attributes. This potentially removes them from the decoded attributes.
-	COPY_STANDARDATTRIBUTES;
-
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ISABSTRACT) {
-		vnode->isAbstract = attr.isAbstract;
-	}
-	UA_ObjectTypeAttributes_deleteMembers(&attr);
-	*new_node = (UA_Node*) vnode;
-	*vt = &UA_TYPES[UA_OBJECTTYPENODE];
-	return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode parseViewNode(UA_ExtensionObject *attributes,
-		UA_Node **new_node, const UA_VTable_Entry **vt) {
-
-	UA_ViewAttributes attr;
-	UA_UInt32 pos = 0;
-// todo return more informative error codes from decodeBinary
-	if (UA_ViewAttributes_decodeBinary(&attributes->body, &pos, &attr)
-			!= UA_STATUSCODE_GOOD)
-		return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
-	UA_ViewNode *vnode = UA_ViewNode_new();
-	if (!vnode) {
-		UA_ViewAttributes_deleteMembers(&attr);
-		return UA_STATUSCODE_BADOUTOFMEMORY;
-	}
-// now copy all the attributes. This potentially removes them from the decoded attributes.
-	COPY_STANDARDATTRIBUTES;
-
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_CONTAINSNOLOOPS) {
-		vnode->containsNoLoops = attr.containsNoLoops;
-	}
-	if (attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_EVENTNOTIFIER) {
-		vnode->eventNotifier = attr.eventNotifier;
-	}
-	UA_ViewAttributes_deleteMembers(&attr);
-	*new_node = (UA_Node*) vnode;
-	*vt = &UA_TYPES[UA_VIEWNODE];
-	return UA_STATUSCODE_GOOD;
-}
-
-void open62541Nodestore_getNewNodeId(UA_ExpandedNodeId *requestedNodeId) {
-	//check nodeId here
-	return;
-}
-
-UA_Int32 open62541NodeStore_AddReferences(const UA_RequestHeader *requestHeader,
-		UA_AddReferencesItem* referencesToAdd, UA_UInt32 *indices,
-		UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
-		UA_DiagnosticInfo *diagnosticInfos) {
-	for (UA_UInt32 i = 0; i < indicesSize; i++) {
-		UA_Node *node = UA_NULL;
-		open62541NodeStore *ns = open62541NodeStore_getNodeStore();
-		open62541NodeStore_get((const open62541NodeStore*) ns,
-				(const UA_NodeId*) &referencesToAdd[indices[i]].sourceNodeId,
-				(const UA_Node**) &node);
-		if (node == UA_NULL) {
-			addReferencesResults[indices[i]] =
-					UA_STATUSCODE_BADSOURCENODEIDINVALID;
-			continue;
-		}
-		// TODO: Check if reference already exists
-		UA_Int32 count = node->referencesSize;
-		UA_ReferenceNode *old_refs = node->references;
-		UA_ReferenceNode *new_refs;
-		if (count < 0)
-			count = 0;
-
-		if (!(new_refs = UA_alloc(sizeof(UA_ReferenceNode) * (count + 1))))
-			return UA_STATUSCODE_BADOUTOFMEMORY;
-
-		UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode) * count);
-
-		//UA_ReferenceNode *reference = UA_ReferenceNode_new();
-		UA_ReferenceNode reference;
-		UA_ReferenceNode_init(&reference);
-		reference.isInverse = !referencesToAdd[indices[i]].isForward;
-		UA_NodeId_copy(&referencesToAdd[indices[i]].referenceTypeId,
-				&reference.referenceTypeId);
-		UA_ExpandedNodeId_copy(&referencesToAdd[indices[i]].targetNodeId,
-				&reference.targetId);
-
-		addReferencesResults[indices[i]] = AddReference(ns, node, &reference);
-		//TODO fill diagnostic info if needed
-		UA_free(new_refs);
-	}
-
-	return UA_STATUSCODE_GOOD;
-}
-UA_Boolean isRootNode(UA_NodeId *nodeId) {
-	return nodeId->identifierType == UA_NODEIDTYPE_NUMERIC
-			&& nodeId->namespaceIndex == 0 && nodeId->identifier.numeric == 84;
-}
-UA_Int32 open62541NodeStore_AddNodes(const UA_RequestHeader *requestHeader,
-		UA_AddNodesItem *nodesToAdd, UA_UInt32 *indices, UA_UInt32 indicesSize,
-		UA_AddNodesResult* addNodesResults, UA_DiagnosticInfo *diagnosticInfos) {
-
-	UA_Node *node = UA_NULL;
-	for (UA_UInt32 i = 0; i < indicesSize; i++) {
-
-		const UA_Node *parent = UA_NULL;
-		//todo what if node is in another namespace, readrequest to test, if it exists?
-		open62541NodeStore *ns = open62541NodeStore_getNodeStore();
-		if (open62541NodeStore_get(ns, &nodesToAdd->parentNodeId.nodeId,
-				&parent) != UA_STATUSCODE_GOOD
-				&& !isRootNode(&nodesToAdd->requestedNewNodeId.nodeId)) {
-			addNodesResults[indices[i]].statusCode =
-					UA_STATUSCODE_BADPARENTNODEIDINVALID;
-			continue;
-		}
-
-		open62541NodeStore_get((const open62541NodeStore*) ns,
-				(const UA_NodeId*) &nodesToAdd[indices[i]].requestedNewNodeId.nodeId,
-				(const UA_Node**) &node);
-
-		if (node != UA_NULL) {
-			//todo or overwrite existing node?
-			continue;
-		}
-		UA_Node *newNode = UA_NULL;
-		const UA_VTable_Entry *newNodeVT = UA_NULL;
-
-		switch (nodesToAdd[indices[i]].nodeClass) {
-		case UA_NODECLASS_DATATYPE: {
-			addNodesResults[indices[i]].statusCode =
-					UA_STATUSCODE_BADNOTIMPLEMENTED;
-			continue;
-			break;
-		}
-		case UA_NODECLASS_METHOD: {
-			addNodesResults[indices[i]].statusCode =
-					UA_STATUSCODE_BADNOTIMPLEMENTED;
-			continue;
-			break;
-		}
-		case UA_NODECLASS_OBJECT: {
-			parseObjectNode(&nodesToAdd[indices[i]].nodeAttributes, &newNode,
-					&newNodeVT);
-			newNode->nodeClass = UA_NODECLASS_OBJECT;
-			break;
-		}
-		case UA_NODECLASS_OBJECTTYPE: {
-			parseObjectTypeNode(&nodesToAdd[indices[i]].nodeAttributes,
-					&newNode, &newNodeVT);
-			newNode->nodeClass = UA_NODECLASS_OBJECTTYPE;
-			break;
-		}
-		case UA_NODECLASS_REFERENCETYPE: {
-			parseReferenceTypeNode(&nodesToAdd[indices[i]].nodeAttributes,
-					&newNode, &newNodeVT);
-			newNode->nodeClass = UA_NODECLASS_REFERENCETYPE;
-			break;
-		}
-		case UA_NODECLASS_VARIABLE: {
-			parseVariableNode(&nodesToAdd[indices[i]].nodeAttributes, &newNode,
-					&newNodeVT);
-			newNode->nodeClass = UA_NODECLASS_VARIABLE;
-			break;
-		}
-		case UA_NODECLASS_VARIABLETYPE: {
-			addNodesResults[indices[i]].statusCode =
-					UA_STATUSCODE_BADNOTIMPLEMENTED;
-			continue;
-			break;
-		}
-		default: {
-			addNodesResults[indices[i]].statusCode =
-					UA_STATUSCODE_BADNOTIMPLEMENTED;
-			continue;
-			break;
-		}
-
-		}
-		open62541Nodestore_getNewNodeId(
-				&nodesToAdd[indices[i]].requestedNewNodeId);
-		newNode->nodeId = nodesToAdd[indices[i]].requestedNewNodeId.nodeId;
-		UA_QualifiedName_copy(&nodesToAdd[indices[i]].browseName,
-				&newNode->browseName);
-
-		UA_AddReferencesItem addRefItem;
-		UA_AddReferencesItem_init(&addRefItem);
-		addRefItem.isForward = UA_TRUE;
-		addRefItem.referenceTypeId = nodesToAdd[indices[i]].referenceTypeId;
-		addRefItem.sourceNodeId = nodesToAdd[indices[i]].parentNodeId.nodeId;
-		addRefItem.targetNodeId.nodeId = newNode->nodeId;
-		addRefItem.targetNodeId.namespaceUri.length = 0;
-		addRefItem.targetServerUri.length = 0;
-		addRefItem.targetNodeClass = newNode->nodeClass;
-
-		open62541NodeStore_insert(ns, (UA_Node**) &newNode,
-		UA_NODESTORE_INSERT_UNIQUE);
-		if (!isRootNode(&nodesToAdd[indices[i]].requestedNewNodeId.nodeId)) {
-			UA_UInt32 ind = 0;
-			UA_UInt32 indSize = 1;
-			UA_StatusCode result;
-			UA_DiagnosticInfo diagnosticInfo;
-			open62541NodeStore_AddReferences(requestHeader, &addRefItem, &ind,
-					indSize, &result, &diagnosticInfo);
-		}
-		UA_AddReferencesItem_deleteMembers(&addRefItem);
-	}
-	return UA_STATUSCODE_GOOD;
-}

+ 0 - 242
src/server/nodestore/open62541_nodestore_view.c

@@ -1,242 +0,0 @@
-/*
- * service_view_implementation.c
- *
- *  Created on: Oct 27, 2014
- *      Author: opcua
- */
-#include "open62541_nodestore.h"
-#include "../ua_services.h"
-#include "ua_namespace_0.h"
-#include "ua_util.h"
-
-
-
-/* Releases the current node, even if it was supplied as an argument. */
-static UA_StatusCode fillReferenceDescription(open62541NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
-                                              UA_UInt32 resultMask, UA_ReferenceDescription *referenceDescription) {
-    UA_ReferenceDescription_init(referenceDescription);
-    if(!currentNode && resultMask != 0) {
-        if(open62541NodeStore_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;
-
-    if(resultMask & UA_BROWSERESULTMASK_REFERENCETYPEID)
-        retval |= UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
-    if(resultMask & UA_BROWSERESULTMASK_ISFORWARD)
-        referenceDescription->isForward = !reference->isInverse;
-    if(resultMask & UA_BROWSERESULTMASK_NODECLASS)
-        retval |= UA_NodeClass_copy(&currentNode->nodeClass, &referenceDescription->nodeClass);
-    if(resultMask & UA_BROWSERESULTMASK_BROWSENAME)
-        retval |= UA_QualifiedName_copy(&currentNode->browseName, &referenceDescription->browseName);
-    if(resultMask & UA_BROWSERESULTMASK_DISPLAYNAME)
-        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 */) {
-                retval |= UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
-                break;
-            }
-        }
-    }
-
-    if(currentNode)
-    	open62541NodeStore_release(currentNode);
-    if(retval)
-        UA_ReferenceDescription_deleteMembers(referenceDescription);
-    return retval;
-}
-
-/* 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(open62541NodeStore *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]))
-                isRelevant = UA_TRUE;
-        }
-        if(!isRelevant)
-            return UA_FALSE;
-    }
-
-    // 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.
-
-    if(open62541NodeStore_get(ns, &reference->targetId.nodeId, currentNode) != UA_STATUSCODE_GOOD)
-        return UA_FALSE;
-
-    if(((*currentNode)->nodeClass & browseDescription->nodeClassMask) == 0) {
-    	open62541NodeStore_release(*currentNode);
-        return UA_FALSE;
-    }
-
-    // the node is relevant and was retrieved from the nodestore, do not release it.
-    return UA_TRUE;
-}
-
-/* We do not search across namespaces so far. The id of the root-referencetype
-   is returned in the array also. */
-static UA_StatusCode findRelevantReferenceTypes(open62541NodeStore *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 |= open62541NodeStore_get(ns, &typeArray[currentIndex], (const UA_Node **)&node);
-        if(retval)
-            break;
-        if(node->nodeClass != UA_NODECLASS_REFERENCETYPE) // subtypes of referencestypes are always referencestypes?
-            continue;
-
-        // 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
-        }
-        open62541NodeStore_release((UA_Node*)node);
-    } while(++currentIndex <= currentLastIndex && retval == UA_STATUSCODE_GOOD);
-
-    if(retval)
-        UA_Array_delete(typeArray, currentLastIndex, &UA_TYPES[UA_NODEID]);
-    else {
-        *referenceTypes = typeArray;
-        *referenceTypesSize = currentLastIndex + 1;
-    }
-
-    return retval;
-}
-
-/* Results for a single browsedescription. */
-static void getBrowseResult(open62541NodeStore *ns, const UA_BrowseDescription *browseDescription,
-                            UA_UInt32 maxReferences, UA_BrowseResult *browseResult) {
-    UA_UInt32  relevantReferenceTypesSize = 0;
-    UA_NodeId *relevantReferenceTypes = UA_NULL;
-
-    // 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;
-        }
-    }
-
-    const UA_Node *parentNode;
-    if(open62541NodeStore_get(ns, &browseDescription->nodeId, &parentNode) != UA_STATUSCODE_GOOD) {
-        browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
-        if(!returnAll && browseDescription->includeSubtypes)
-            UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
-        return;
-    }
-
-    // 0 => unlimited references
-    if(maxReferences == 0 || maxReferences > UA_INT32_MAX || (UA_Int32)maxReferences > parentNode->referencesSize)
-        maxReferences = parentNode->referencesSize;
-
-    /* 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;
-
-            // 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_TYPES[UA_REFERENCEDESCRIPTION]);
-                currentRefs = 0;
-                browseResult->references = UA_NULL;
-                browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
-                break;
-            }
-            currentRefs++;
-        }
-        if(currentRefs != 0)
-        	browseResult->referencesSize = currentRefs;
-        else {
-        	UA_free(browseResult->references);
-        	browseResult->references = UA_NULL;
-        }
-    }
-
-    open62541NodeStore_release(parentNode);
-    if(!returnAll && browseDescription->includeSubtypes)
-        UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
-}
-
-UA_Int32 open62541NodeStore_BrowseNodes(const UA_RequestHeader *requestHeader,UA_BrowseDescription *browseDescriptions,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
-		UA_BrowseResult *browseResults,
-		UA_DiagnosticInfo *diagnosticInfos){
-
-
-	for(UA_UInt32 i = 0; i < indicesSize; i++){
-		getBrowseResult(open62541NodeStore_getNodeStore(),&browseDescriptions[indices[i]],requestedMaxReferencesPerNode, &browseResults[indices[i]]);
-	}
-	return UA_STATUSCODE_GOOD;
-}

+ 0 - 31
src/server/ua_namespace.c

@@ -1,31 +0,0 @@
-/*
- * ua_namespace.c
- *
- *  Created on: Nov 6, 2014
- *      Author: opcua
- */
-#include "ua_namespace.h"
-#include "ua_nodestore_interface.h"
-
-
-UA_Namespace *UA_Namespace_new()
-{
-	UA_Namespace *n = UA_alloc(sizeof(UA_Namespace));
-    if(n) UA_Namespace_init(n);
-    return n;
-}
-
-void UA_Namespace_init(UA_Namespace *namespace)
-{
-	namespace->index = 0;
-	namespace->nodeStore = UA_NULL;
-	UA_String_init(&namespace->url);
-}
-void UA_Namespace_delete(UA_Namespace *namespace){
-	UA_Namespace_deleteMembers(namespace);
-	UA_free(namespace);
-}
-void UA_Namespace_deleteMembers(UA_Namespace *namespace){
-	UA_String_deleteMembers(&namespace->url);
-	UA_NodeStoreInterface_delete(namespace->nodeStore);
-}

+ 0 - 26
src/server/ua_namespace.h

@@ -1,26 +0,0 @@
-/*
- * ua_namespace.h
- *
- *  Created on: Nov 6, 2014
- *      Author: opcua
- */
-
-#ifndef UA_NAMESPACE_H_
-#define UA_NAMESPACE_H_
-#include "ua_server.h"
-#include "ua_util.h"
-#include "ua_statuscodes.h"
-
-typedef struct UA_Namespace
-{
-	UA_UInt16 index;
-	UA_String url;
-	UA_NodeStoreInterface *nodeStore;
-}UA_Namespace;
-
-UA_Namespace *UA_Namespace_new();
-void UA_Namespace_init(UA_Namespace *namespace);
-void UA_Namespace_delete(UA_Namespace *namespace);
-void UA_Namespace_deleteMembers(UA_Namespace *namespace);
-
-#endif /* UA_NAMESPACE_H_ */

+ 0 - 113
src/server/ua_namespace_manager.c

@@ -1,113 +0,0 @@
-/*
- * ua_namespace_manager.c
- *
- *  Created on: Oct 14, 2014
- *      Author: opcua
- */
-#include "ua_namespace_manager.h"
-#include "ua_util.h"
-
-
-
-
-struct namespace_list_entry {
-    UA_Namespace namespace;
-    LIST_ENTRY(namespace_list_entry) pointers;
-};
-
-
-
-UA_StatusCode UA_NamespaceManager_init( UA_NamespaceManager* namespaceManager)
-{
-	LIST_INIT(&namespaceManager->namespaces);
-	namespaceManager->currentNamespaceCount = 0;
-	return UA_STATUSCODE_GOOD;
-}
-
-void UA_NamespaceManager_deleteMembers(UA_NamespaceManager *namespaceManager)
-{
-	struct namespace_list_entry *current = LIST_FIRST(&namespaceManager->namespaces);
-    while(current) {
-        LIST_REMOVE(current, pointers);
-        UA_Namespace_deleteMembers(&current->namespace);
-        UA_free(current);
-        current = LIST_FIRST(&namespaceManager->namespaces);
-    }
-}
-
-UA_StatusCode UA_NamespaceManager_createNamespace(UA_NamespaceManager *namespaceManager,UA_UInt16 index, UA_NodeStoreInterface *nodeStore)
-{
-	if(namespaceManager->currentNamespaceCount<UA_UINT16_MAX){
-
-		struct namespace_list_entry *newentry = UA_alloc(sizeof(struct namespace_list_entry));
-		UA_Namespace_init(&newentry->namespace);
-		newentry->namespace.index = index;
-		if(nodeStore != UA_NULL ){
-
-			newentry->namespace.nodeStore = UA_NodeStoreInterface_new();
-			UA_NodeStoreInterface_copy(nodeStore,newentry->namespace.nodeStore);
-		}
-		namespaceManager->currentNamespaceCount++;
-	    LIST_INSERT_HEAD(&namespaceManager->namespaces, newentry, pointers);
-
-	    return UA_STATUSCODE_GOOD;
-
-	}
-	return UA_STATUSCODE_BADNOTFOUND;
-
-
-
-}
-
-UA_StatusCode UA_NamespaceManager_removeNamespace(UA_NamespaceManager *namespaceManager,UA_UInt16 index)
-{
-	UA_Namespace *namespace;
-	UA_NamespaceManager_getNamespace(namespaceManager,index,&namespace);
-	if(namespace == UA_NULL)
-		return UA_STATUSCODE_BADNOTFOUND;
-
-    struct namespace_list_entry *current = UA_NULL;
-    LIST_FOREACH(current, &namespaceManager->namespaces, pointers) {
-        if(current->namespace.index  == index)
-            break;
-    }
-
-    if(!current)
-        return UA_STATUSCODE_BADNOTFOUND;
-	LIST_REMOVE(current, pointers);
-
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_Int32 UA_NamespaceManager_getNamespace(UA_NamespaceManager *namespaceManager, UA_UInt16 index, UA_Namespace **ns)
-{
-
-    struct namespace_list_entry *current = UA_NULL;
-    LIST_FOREACH(current, &namespaceManager->namespaces, pointers) {
-        if(current->namespace.index == index)
-            break;
-    }
-    if(!current) {
-        *ns = UA_NULL;
-        return UA_STATUSCODE_BADNOTFOUND;
-    }
-    *ns = &current->namespace;
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_Int32 UA_NamespaceManager_setNodeStore(UA_NamespaceManager *namespaceManager,UA_UInt16 index, UA_NodeStoreInterface *nodeStore)
-{
-	UA_Namespace *namespace = UA_NULL;
-	UA_NamespaceManager_getNamespace(namespaceManager, index, &namespace);
-	if(namespace == UA_NULL)
-	{
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	if(nodeStore != UA_NULL ){
-		if(namespace->nodeStore == UA_NULL){
-			namespace->nodeStore = UA_NodeStoreInterface_new();
-		}
-	}
-	namespace->nodeStore = nodeStore;
-	return UA_STATUSCODE_GOOD;
-}

+ 0 - 34
src/server/ua_namespace_manager.h

@@ -1,34 +0,0 @@
-/*
- * ua_namespace_manager.h
- *
- *  Created on: Oct 14, 2014
- *      Author: opcua
- */
-
-#ifndef UA_NAMESPACE_MANAGER_H_
-#define UA_NAMESPACE_MANAGER_H_
-#include "ua_server.h"
-#include "ua_nodestore_interface.h"
-#include "ua_namespace.h"
-#include "ua_util.h"
-
-struct UA_NamespaceManager {
-    LIST_HEAD(namespace_list, namespace_list_entry) namespaces;
-    UA_UInt32    currentNamespaceCount;
-};
-
-UA_StatusCode UA_NamespaceManager_init( UA_NamespaceManager* namespaceManager);
-
-void UA_NamespaceManager_deleteMembers(UA_NamespaceManager *namespaceManager);
-
-UA_Int32  UA_NamespaceManager_createNamespace(UA_NamespaceManager *namespaceManager, UA_UInt16 index, UA_NodeStoreInterface *nodeStore);
-
-UA_Int32  UA_NamespaceManager_removeNamespace(UA_NamespaceManager *namespaceManager,UA_UInt16 index);
-
-UA_Int32  UA_NamespaceManager_getNamespace(UA_NamespaceManager *namespaceManager, UA_UInt16 index, UA_Namespace **ns);
-
-UA_Int32  UA_NamespaceManager_setNodeStore(UA_NamespaceManager *namespaceManager,UA_UInt16 index, UA_NodeStoreInterface *nodeStore);
-
-
-
-#endif /* UA_NAMESPACE_MANAGER_H_ */

+ 346 - 0
src/server/ua_nodestore.c

@@ -0,0 +1,346 @@
+#include "ua_nodestore.h"
+#include "ua_util.h"
+#include "ua_statuscodes.h"
+
+struct UA_NodeStore {
+    const UA_Node **entries;
+    UA_UInt32       size;
+    UA_UInt32       count;
+    UA_UInt32       sizePrimeIndex;
+};
+
+typedef UA_UInt32 hash_t;
+/* The size of the hash-map is always a prime number. They are chosen to be
+   close to the next power of 2. So the size ca. doubles with each prime. */
+static hash_t const primes[] = {
+    7,         13,         31,         61,         127,         251,
+    509,       1021,       2039,       4093,       8191,        16381,
+    32749,     65521,      131071,     262139,     524287,      1048573,
+    2097143,   4194301,    8388593,    16777213,   33554393,    67108859,
+    134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
+};
+
+static INLINE hash_t mod(hash_t h, hash_t size) {
+    return h % size;
+}
+static INLINE hash_t mod2(hash_t h, hash_t size) {
+    return 1 + (h % (size - 2));
+}
+
+static INLINE UA_Int16 higher_prime_index(hash_t n) {
+    UA_UInt16 low  = 0;
+    UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
+    while(low != high) {
+        UA_UInt16 mid = low + (high - low) / 2;
+        if(n > primes[mid])
+            low = mid + 1;
+        else
+            high = mid;
+    }
+    return low;
+}
+
+/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
+static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
+    const int32_t   nblocks = len / 4;
+    const uint32_t *blocks;
+
+    static const uint32_t c1 = 0xcc9e2d51;
+    static const uint32_t c2 = 0x1b873593;
+    static const uint32_t r1 = 15;
+    static const uint32_t r2 = 13;
+    static const uint32_t m  = 5;
+    static const uint32_t n  = 0xe6546b64;
+    hash_t hash = seed;
+
+    if(data == UA_NULL)
+        return 0;
+
+    blocks = (const uint32_t *)data;
+    for(int32_t i = 0;i < nblocks;i++) {
+        uint32_t k = blocks[i];
+        k    *= c1;
+        k     = (k << r1) | (k >> (32 - r1));
+        k    *= c2;
+        hash ^= k;
+        hash  = ((hash << r2) | (hash >> (32 - r2))) * m + n;
+    }
+
+    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
+    uint32_t       k1   = 0;
+
+    switch(len & 3) {
+    case 3:
+        k1 ^= tail[2] << 16;
+
+    case 2:
+        k1 ^= tail[1] << 8;
+
+    case 1:
+        k1   ^= tail[0];
+        k1   *= c1;
+        k1    = (k1 << r1) | (k1 >> (32 - r1));
+        k1   *= c2;
+        hash ^= k1;
+    }
+
+    hash ^= len;
+    hash ^= (hash >> 16);
+    hash *= 0x85ebca6b;
+    hash ^= (hash >> 13);
+    hash *= 0xc2b2ae35;
+    hash ^= (hash >> 16);
+
+    return hash;
+}
+
+static INLINE hash_t hash(const UA_NodeId *n) {
+    switch(n->identifierType) {
+    case UA_NODEIDTYPE_NUMERIC:
+        /*  Knuth's multiplicative hashing */
+        return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
+
+    case UA_NODEIDTYPE_STRING:
+        return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
+
+    case UA_NODEIDTYPE_GUID:
+        return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
+
+    case UA_NODEIDTYPE_BYTESTRING:
+        return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
+
+    default:
+        UA_assert(UA_FALSE);
+        return 0;
+    }
+}
+
+static INLINE void clear_entry(UA_NodeStore *ns, const UA_Node **entry) {
+    const UA_Node *node;
+    if(entry == UA_NULL || *entry == UA_NULL)
+        return;
+
+    node = *entry;
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        UA_ObjectNode_delete((UA_ObjectNode *)node);
+        break;
+
+    case UA_NODECLASS_VARIABLE:
+        UA_VariableNode_delete((UA_VariableNode *)node);
+        break;
+
+    case UA_NODECLASS_METHOD:
+        UA_MethodNode_delete((UA_MethodNode *)node);
+        break;
+
+    case UA_NODECLASS_OBJECTTYPE:
+        UA_ObjectTypeNode_delete((UA_ObjectTypeNode *)node);
+        break;
+
+    case UA_NODECLASS_VARIABLETYPE:
+        UA_VariableTypeNode_delete((UA_VariableTypeNode *)node);
+        break;
+
+    case UA_NODECLASS_REFERENCETYPE:
+        UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *)node);
+        break;
+
+    case UA_NODECLASS_DATATYPE:
+        UA_DataTypeNode_delete((UA_DataTypeNode *)node);
+        break;
+
+    case UA_NODECLASS_VIEW:
+        UA_ViewNode_delete((UA_ViewNode *)node);
+        break;
+
+    default:
+        UA_assert(UA_FALSE);
+        break;
+    }
+    entry = UA_NULL;
+    ns->count--;
+}
+
+/* Returns UA_STATUSCODE_GOOD if an entry was found. Otherwise, An error code is
+   returned and the "entry" argument points to the first free entry under the
+   NodeId. */
+static INLINE UA_StatusCode find_entry(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node ***entry) {
+    hash_t          h     = hash(nodeid);
+    UA_UInt32       size  = ns->size;
+    hash_t          index = mod(h, size);
+    const UA_Node **e     = &ns->entries[index];
+
+    if(*e == UA_NULL) {
+        *entry = e;
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    if(UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
+        *entry = e;
+        return UA_STATUSCODE_GOOD;
+    }
+
+    hash_t hash2 = mod2(h, size);
+    for(;;) {
+        index += hash2;
+        if(index >= size)
+            index -= size;
+
+        e = &ns->entries[index];
+
+        if(*e == UA_NULL) {
+            *entry = e;
+            return UA_STATUSCODE_BADINTERNALERROR;
+        }
+
+        if(UA_NodeId_equal(&(*e)->nodeId, nodeid)) {
+            *entry = e;
+            return UA_STATUSCODE_GOOD;
+        }
+    }
+
+    /* NOTREACHED */
+    return UA_STATUSCODE_GOOD;
+}
+
+/* The following function changes size of memory allocated for the entries and
+   repeatedly inserts the table elements. The occupancy of the table after the
+   call will be about 50%. */
+static UA_StatusCode expand(UA_NodeStore *ns) {
+    const UA_Node **nentries;
+    int32_t nsize;
+    UA_UInt32 nindex;
+
+    const UA_Node **oentries = ns->entries;
+    int32_t osize = ns->size;
+    const UA_Node **olimit   = &oentries[osize];
+    int32_t count = ns->count;
+
+    /* Resize only when table after removal of unused elements is either too full or too empty.  */
+    if(count * 2 < osize && (count * 8 > osize || osize <= 32))
+        return UA_STATUSCODE_GOOD;
+
+    nindex = higher_prime_index(count * 2);
+    nsize  = primes[nindex];
+
+    if(!(nentries = UA_alloc(sizeof(UA_Node *) * nsize)))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    memset(nentries, 0, nsize * sizeof(UA_Node *));
+    ns->entries = nentries;
+    ns->size    = nsize;
+    ns->sizePrimeIndex = nindex;
+
+    const UA_Node **p = oentries;
+    do {
+        if(*p != UA_NULL) {
+            const UA_Node **e;
+            find_entry(ns, &(*p)->nodeId, &e);  /* We know this returns an empty entry here */
+            *e = *p;
+        }
+        p++;
+    } while(p < olimit);
+
+    UA_free(oentries);
+    return UA_STATUSCODE_GOOD;
+}
+
+/**********************/
+/* Exported functions */
+/**********************/
+
+UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
+    UA_NodeStore *ns;
+    UA_UInt32     sizePrimeIndex, size;
+    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    sizePrimeIndex = higher_prime_index(32);
+    size = primes[sizePrimeIndex];
+    if(!(ns->entries = UA_alloc(sizeof(UA_Node *) * size))) {
+        UA_free(ns);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    /* set entries to zero */
+    memset(ns->entries, 0, size * sizeof(UA_Node *));
+
+    *ns     = (UA_NodeStore) {ns->entries, size, 0, sizePrimeIndex };
+    *result = ns;
+    return UA_STATUSCODE_GOOD;
+}
+
+void UA_NodeStore_delete(UA_NodeStore *ns) {
+    UA_UInt32       size    = ns->size;
+    const UA_Node **entries = ns->entries;
+
+    for(UA_UInt32 i = 0;i < size;i++)
+        clear_entry(ns, &entries[i]);
+
+    UA_free(ns->entries);
+    UA_free(ns);
+}
+
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags) {
+    if(ns->size * 3 <= ns->count * 4) {
+        if(expand(ns) != UA_STATUSCODE_GOOD)
+            return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    const UA_Node **entry;
+    UA_Int32 found = find_entry(ns, &(*node)->nodeId, &entry);
+
+    if(flags & UA_NODESTORE_INSERT_UNIQUE) {
+        if(found == UA_STATUSCODE_GOOD)
+            return UA_STATUSCODE_BADNODEIDEXISTS;
+        else
+            *entry = *node;
+    } else {
+        if(found == UA_STATUSCODE_GOOD)
+            clear_entry(ns, entry);
+        *entry = *node;
+    }
+
+    if(!(flags & UA_NODESTORE_INSERT_GETMANAGED))
+        *node = UA_NULL;
+
+    ns->count++;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
+    const UA_Node **entry;
+    if(find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    *managedNode = *entry;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    const UA_Node **entry;
+    if(find_entry(ns, nodeid, &entry) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    // Check before if deleting the node makes the UA_NodeStore inconsistent.
+    clear_entry(ns, entry);
+
+    /* Downsize the hashmap if it is very empty */
+    if(ns->count * 8 < ns->size && ns->size > 32)
+        expand(ns); // this can fail. we just continue with the bigger hashmap.
+
+    return UA_STATUSCODE_GOOD;
+}
+
+void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
+    for(UA_UInt32 i = 0;i < ns->size;i++) {
+        const UA_Node *node = ns->entries[i];
+        if(node != UA_NULL)
+            visitor(node);
+    }
+}
+
+void UA_NodeStore_release(const UA_Node *managed) {
+    ;
+}

+ 62 - 0
src/server/ua_nodestore.h

@@ -0,0 +1,62 @@
+#ifndef UA_NODESTORE_H_
+#define UA_NODESTORE_H_
+
+#include "ua_types_generated.h"
+#include "ua_util.h"
+
+/**
+   @ingroup server
+
+   @defgroup nodestore NodeStore
+
+   @brief The nodestore is the central storage for nodes in the UA address
+   space. Internally, the nodestore is realised as hash-map where nodes are
+   stored and retrieved with their nodeid.
+
+   The nodes in the nodestore are immutable. To change the content of a node, it
+   needs to be replaced as a whole. When a node is inserted into the namespace,
+   it gets replaced with a pointer to a managed node. Managed nodes shall never
+   be freed by the user. This is done by the namespace when the node is removed
+   and no readers (in other threads) access the node.
+ */
+
+struct UA_NodeStore;
+typedef struct UA_NodeStore UA_NodeStore;
+
+/** @brief Create a new namespace */
+UA_StatusCode UA_NodeStore_new(UA_NodeStore **result);
+
+/** @brief Delete the namespace and all nodes in it */
+void UA_NodeStore_delete(UA_NodeStore *ns);
+
+#define UA_NODESTORE_INSERT_UNIQUE 1
+#define UA_NODESTORE_INSERT_GETMANAGED 2
+/** @brief Insert a new node into the namespace
+
+    With the UNIQUE flag, the node is only inserted if the nodeid does not
+    already exist. With the GETMANAGED flag, the node pointer is replaced with
+    the managed pointer. Otherwise, it is set to UA_NULL. */
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags);
+
+/** @brief Remove a node from the namespace. Always succeeds, even if the node
+    was not found. */
+UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
+
+/** @brief Retrieve a node (read-only) from the namespace. Nodes are immutable.
+    They can only be replaced. After the Node is no longer used, the locked
+    entry needs to be released. */
+UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid,
+                               const UA_Node **managedNode);
+
+/** @brief Release a managed node. Do never insert a node that isn't stored in a
+    namespace. */
+void UA_NodeStore_release(const UA_Node *managed);
+
+/** @brief A function that can be evaluated on all entries in a namespace via
+    UA_NodeStore_iterate. Note that the visitor is read-only on the nodes. */
+typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
+
+/** @brief Iterate over all nodes in a namespace. */
+void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
+
+#endif /* UA_NODESTORE_H_ */

+ 29 - 29
examples/ua_nodestore_concurrent.c

@@ -1,4 +1,4 @@
-#include "open62541NodeStore.h"
+#include "ua_nodestore.h"
 #include "ua_util.h"
 
 #include <urcu.h>
@@ -8,14 +8,14 @@
 
 #define ALIVE_BIT (1 << 15) /* Alive bit in the readcount */
 
-typedef struct open62541NodeStore_Entry {
+typedef struct UA_NodeStore_Entry {
     struct cds_lfht_node htn;      /* contains next-ptr for urcu-hashmap */
     struct rcu_head      rcu_head; /* For call-rcu */
     UA_UInt16 readcount;           /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
     UA_Node   node;                /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
-} open62541NodeStore_Entry;
+} UA_NodeStore_Entry;
 
-struct open62541NodeStore {
+struct UA_NodeStore {
     struct cds_lfht *ht; /* Hash table */
 };
 
@@ -98,7 +98,7 @@ static INLINE hash_t hash(const UA_NodeId *n) {
 }
 
 /****************/
-/* open62541NodeStore */
+/* UA_NodeStore */
 /****************/
 
 static inline void node_deleteMembers(const UA_Node *node) {
@@ -144,7 +144,7 @@ static inline void node_deleteMembers(const UA_Node *node) {
 /* We are in a rcu_read lock. So the node will not be freed under our feet. */
 static int compare(struct cds_lfht_node *htn, const void *orig) {
     UA_NodeId *origid = (UA_NodeId *)orig;
-    UA_NodeId *newid  = &((open62541NodeStore_Entry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
+    UA_NodeId *newid  = &((UA_NodeStore_Entry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
 
     return UA_NodeId_equal(newid, origid);
 }
@@ -154,7 +154,7 @@ static int compare(struct cds_lfht_node *htn, const void *orig) {
    section) increased the readcount, we only need to wait for the readcount
    to reach zero. */
 static void markDead(struct rcu_head *head) {
-    open62541NodeStore_Entry *entry = caa_container_of(head, open62541NodeStore_Entry, rcu_head);
+    UA_NodeStore_Entry *entry = caa_container_of(head, UA_NodeStore_Entry, rcu_head);
     if(uatomic_sub_return(&entry->readcount, ALIVE_BIT) > 0)
         return;
 
@@ -164,8 +164,8 @@ static void markDead(struct rcu_head *head) {
 }
 
 /* Free the entry if it is dead and nobody uses it anymore */
-void open62541NodeStore_release(const UA_Node *managed) {
-    open62541NodeStore_Entry *entry = caa_container_of(managed, open62541NodeStore_Entry, node); // pointer to the first entry
+void UA_NodeStore_release(const UA_Node *managed) {
+    UA_NodeStore_Entry *entry = caa_container_of(managed, UA_NodeStore_Entry, node); // pointer to the first entry
     if(uatomic_sub_return(&entry->readcount, 1) > 0)
         return;
 
@@ -174,13 +174,13 @@ void open62541NodeStore_release(const UA_Node *managed) {
     return;
 }
 
-UA_StatusCode open62541NodeStore_new(open62541NodeStore **result) {
-    open62541NodeStore *ns;
-    if(!(ns = UA_alloc(sizeof(open62541NodeStore))))
+UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
+    UA_NodeStore *ns;
+    if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     /* 32 is the minimum size for the hashtable. */
-    ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, UA_NULL);
+    ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, NULL);
     if(!ns->ht) {
         UA_free(ns);
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -190,7 +190,7 @@ UA_StatusCode open62541NodeStore_new(open62541NodeStore **result) {
     return UA_STATUSCODE_GOOD;
 }
 
-void open62541NodeStore_delete(open62541NodeStore *ns) {
+void UA_NodeStore_delete(UA_NodeStore *ns) {
     struct cds_lfht      *ht = ns->ht;
     struct cds_lfht_iter  iter;
     struct cds_lfht_node *found_htn;
@@ -200,7 +200,7 @@ void open62541NodeStore_delete(open62541NodeStore *ns) {
     while(iter.node != UA_NULL) {
         found_htn = cds_lfht_iter_get_node(&iter);
         if(!cds_lfht_del(ht, found_htn)) {
-            open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
+            UA_NodeStore_Entry *entry = caa_container_of(found_htn, UA_NodeStore_Entry, htn);
             call_rcu(&entry->rcu_head, markDead);
         }
         cds_lfht_next(ht, &iter);
@@ -211,7 +211,7 @@ void open62541NodeStore_delete(open62541NodeStore *ns) {
     UA_free(ns);
 }
 
-UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node, UA_Byte flags) {
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags) {
     UA_UInt32 nodesize;
     /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
     switch((*node)->nodeClass) {
@@ -251,19 +251,19 @@ UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node,
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
-    open62541NodeStore_Entry *entry;
-    if(!(entry = UA_alloc(sizeof(open62541NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
+    UA_NodeStore_Entry *entry;
+    if(!(entry = UA_alloc(sizeof(UA_NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     memcpy(&entry->node, *node, nodesize);
 
     cds_lfht_node_init(&entry->htn);
     entry->readcount = ALIVE_BIT;
-    if(flags & open62541NodeStore_INSERT_GETMANAGED)
+    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
         entry->readcount++;
 
     hash_t nhash = hash(&(*node)->nodeId);
     struct cds_lfht_node *result;
-    if(flags & open62541NodeStore_INSERT_UNIQUE) {
+    if(flags & UA_NODESTORE_INSERT_UNIQUE) {
         rcu_read_lock();
         result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
         rcu_read_unlock();
@@ -278,14 +278,14 @@ UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node,
         result = cds_lfht_add_replace(ns->ht, nhash, compare, &(*node)->nodeId, &entry->htn);
         /* If an entry got replaced, mark it as dead. */
         if(result) {
-            open62541NodeStore_Entry *entry = caa_container_of(result, open62541NodeStore_Entry, htn);
+            UA_NodeStore_Entry *entry = caa_container_of(result, UA_NodeStore_Entry, htn);
             call_rcu(&entry->rcu_head, markDead);      /* Queue this for the next time when no readers are on the entry.*/
         }
         rcu_read_unlock();
     }
 
     UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
-    if(flags & open62541NodeStore_INSERT_GETMANAGED)
+    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
         *node = &entry->node;
     else
         *node = UA_NULL;
@@ -293,7 +293,7 @@ UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns, const UA_NodeId *nodeid) {
+UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     hash_t nhash = hash(nodeid);
     struct cds_lfht_iter iter;
 
@@ -307,20 +307,20 @@ UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns, const UA_NodeId
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     }
 
-    open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
+    UA_NodeStore_Entry *entry = caa_container_of(found_htn, UA_NodeStore_Entry, htn);
     call_rcu(&entry->rcu_head, markDead);
     rcu_read_unlock();
 
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
+UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
     hash_t nhash = hash(nodeid);
     struct cds_lfht_iter iter;
 
     rcu_read_lock();
     cds_lfht_lookup(ns->ht, nhash, compare, nodeid, &iter);
-    open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
+    UA_NodeStore_Entry *found_entry = (UA_NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
 
     if(!found_entry) {
         rcu_read_unlock();
@@ -335,19 +335,19 @@ UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns, const UA_Node
     return UA_STATUSCODE_GOOD;
 }
 
-void open62541NodeStore_iterate(const open62541NodeStore *ns, open62541NodeStore_nodeVisitor visitor) {
+void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
     struct cds_lfht     *ht = ns->ht;
     struct cds_lfht_iter iter;
 
     rcu_read_lock();
     cds_lfht_first(ht, &iter);
     while(iter.node != UA_NULL) {
-        open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
+        UA_NodeStore_Entry *found_entry = (UA_NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
         uatomic_inc(&found_entry->readcount);
         const UA_Node      *node = &found_entry->node;
         rcu_read_unlock();
         visitor(node);
-        open62541NodeStore_release((UA_Node *)node);
+        UA_NodeStore_release((UA_Node *)node);
         rcu_read_lock();
         cds_lfht_next(ht, &iter);
     }

+ 0 - 99
src/server/ua_nodestore_interface.c

@@ -1,99 +0,0 @@
-#include "ua_nodestore_interface.h"
-#include "ua_util.h"
-#include "ua_statuscodes.h"
-
-
-
-UA_StatusCode UA_NodeStoreInterface_registerAddNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_addNodes addNodes)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->addNodes = addNodes;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStore_registerAddReferencesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_addReferences addReferences)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->addReferences = addReferences;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStoreInterface_registerDeleteNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_deleteNodes deleteNodes)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->deleteNodes = deleteNodes;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStoreInterface_registerDeleteReferencesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_deleteReferences deleteReferences)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->deleteReferences = deleteReferences;
-	return UA_STATUSCODE_GOOD;
-}
-
-
-UA_StatusCode UA_NodeStoreInterface_registerReadNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_readNodes readNodes)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->readNodes = readNodes;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStoreInterface_registerWriteNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_writeNodes writeNodes)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->writeNodes = writeNodes;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_NodeStoreInterface_registerBrowseNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_browseNodes browseNodes)
-{
-	if(nodeStore==UA_NULL){
-		return UA_STATUSCODE_BADNOTFOUND;
-	}
-	nodeStore->browseNodes = browseNodes;
-	return UA_STATUSCODE_GOOD;
-}
-
-UA_NodeStoreInterface* UA_NodeStoreInterface_new(){
-	return UA_alloc(sizeof(UA_NodeStoreInterface));
-}
-
-UA_StatusCode UA_NodeStoreInterface_copy(const UA_NodeStoreInterface *src,UA_NodeStoreInterface *dst){
-	if(src!=UA_NULL){
-		if(dst!=UA_NULL){
-			//just copy the function pointers
-			dst->addNodes = src->addNodes;
-			dst->addReferences = src->addReferences;
-			dst->browseNodes = src->browseNodes;
-			dst->deleteNodes = src->deleteNodes;
-			dst->deleteReferences = src->deleteReferences;
-			dst->readNodes = src->readNodes;
-			dst->writeNodes = src->writeNodes;
-		return UA_STATUSCODE_GOOD;
-		}
-	}
-	return UA_STATUSCODE_BADINTERNALERROR;
-}
-void UA_NodeStoreInterface_delete(UA_NodeStoreInterface *nodestoreInterface){
-	UA_free(nodestoreInterface);
-}
-//add method to add a 'delete nodestore'
-
-
-
-
-

+ 0 - 29
src/server/ua_nodestore_interface.h

@@ -1,29 +0,0 @@
-#ifndef UA_NODESTORE_H_
-#define UA_NODESTORE_H_
-
-
-
-#include "ua_server.h"
-
-
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerAddNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_addNodes addNodes);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerAddReferenceOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_addReferences addReference);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerDeleteNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_deleteNodes deleteNode);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerDeleteReferencesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_deleteReferences deleteReference);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerReadNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_readNodes readNode);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerWriteNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_writeNodes writeNode);
-UA_StatusCode UA_EXPORT UA_NodeStoreInterface_registerBrowseNodesOperation(UA_NodeStoreInterface *nodeStore, UA_NodeStore_browseNodes browseNode);
-
-#define UA_NODESTORE_INSERT_UNIQUE 1
-#define UA_NODESTORE_INSERT_GETMANAGED 2
-
-
-
-UA_NodeStoreInterface* UA_NodeStoreInterface_new();
-UA_StatusCode UA_NodeStoreInterface_copy(const UA_NodeStoreInterface *src,UA_NodeStoreInterface *dst);
-void UA_NodeStoreInterface_delete(UA_NodeStoreInterface *nodestore);
-
-UA_Boolean UA_NodeStore_nodeExists(UA_NodeId nodeId);
-
-
-#endif /* UA_NODESTORE_H_ */

+ 2 - 0
src/server/ua_securechannel_manager.c

@@ -96,6 +96,8 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager           *c
     channel->securityToken.revisedLifetime = request->requestedLifetime > cm->maxChannelLifetime ?
                                              cm->maxChannelLifetime : request->requestedLifetime;
 
+    if(channel->serverNonce.data != UA_NULL)
+        UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_SecureChannel_generateNonce(&channel->serverNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);

Різницю між файлами не показано, бо вона завелика
+ 440 - 576
src/server/ua_server.c


+ 17 - 3
src/server/ua_server_internal.h

@@ -2,20 +2,34 @@
 #define UA_SERVER_INTERNAL_H_
 
 #include "ua_server.h"
+#include "ua_nodestore.h"
 #include "ua_session_manager.h"
-#include "ua_namespace_manager.h"
 #include "ua_securechannel_manager.h"
 
+/** Mapping of namespace-id and url to an external nodestore. For namespaces
+    that have no mapping defined, the internal nodestore is used by default. */
+typedef struct UA_ExternalNamespace {
+	UA_UInt16 index;
+	UA_String url;
+	UA_ExternalNodeStore externalNodeStore;
+} UA_ExternalNamespace;
+
+void UA_ExternalNamespace_init(UA_ExternalNamespace *ens);
+void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens);
+
 struct UA_Server {
     UA_ApplicationDescription description;
     UA_Int32 endpointDescriptionsSize;
     UA_EndpointDescription *endpointDescriptions;
+
     UA_ByteString serverCertificate;
-    UA_NamespaceManager namespaceManager;
     UA_SecureChannelManager secureChannelManager;
     UA_SessionManager sessionManager;
-    UA_NodeStoreInterface *nodestore;
     UA_Logger logger;
+
+    UA_NodeStore *nodestore;
+    UA_Int32 externalNamespacesSize;
+    UA_ExternalNamespace *externalNamespaces;
 };
 
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 4 - 28
src/server/ua_services.h

@@ -6,8 +6,8 @@
 #include "ua_server.h"
 #include "ua_session.h"
 
-
-enum UA_AttributeId {
+/** The (nodes) AttributeIds are defined in part 6, table A1 of the standard */
+typedef enum UA_AttributeId {
     UA_ATTRIBUTEID_NODEID                  = 1,
     UA_ATTRIBUTEID_NODECLASS               = 2,
     UA_ATTRIBUTEID_BROWSENAME              = 3,
@@ -30,32 +30,8 @@ enum UA_AttributeId {
     UA_ATTRIBUTEID_HISTORIZING             = 20,
     UA_ATTRIBUTEID_EXECUTABLE              = 21,
     UA_ATTRIBUTEID_USEREXECUTABLE          = 22
-};
-/*
-UA_ExpandedNodeId RefTypeId_References; NS0EXPANDEDNODEID(RefTypeId_References, 31);
-UA_ExpandedNodeId RefTypeId_NonHierarchicalReferences; NS0EXPANDEDNODEID(RefTypeId_NonHierarchicalReferences, 32);
-UA_ExpandedNodeId RefTypeId_HierarchicalReferences; NS0EXPANDEDNODEID(RefTypeId_HierarchicalReferences, 33);
-UA_ExpandedNodeId RefTypeId_HasChild; NS0EXPANDEDNODEID(RefTypeId_HasChild, 34);
-UA_ExpandedNodeId RefTypeId_Organizes; NS0EXPANDEDNODEID(RefTypeId_Organizes, 35);
-UA_ExpandedNodeId RefTypeId_HasEventSource; NS0EXPANDEDNODEID(RefTypeId_HasEventSource, 36);
-UA_ExpandedNodeId RefTypeId_HasModellingRule; NS0EXPANDEDNODEID(RefTypeId_HasModellingRule, 37);
-UA_ExpandedNodeId RefTypeId_HasEncoding; NS0EXPANDEDNODEID(RefTypeId_HasEncoding, 38);
-UA_ExpandedNodeId RefTypeId_HasDescription; NS0EXPANDEDNODEID(RefTypeId_HasDescription, 39);
-UA_ExpandedNodeId RefTypeId_HasTypeDefinition; NS0EXPANDEDNODEID(RefTypeId_HasTypeDefinition, 40);
-UA_ExpandedNodeId RefTypeId_GeneratesEvent; NS0EXPANDEDNODEID(RefTypeId_GeneratesEvent, 41);
-UA_ExpandedNodeId RefTypeId_Aggregates; NS0EXPANDEDNODEID(RefTypeId_Aggregates, 44);
-UA_ExpandedNodeId RefTypeId_HasSubtype; NS0EXPANDEDNODEID(RefTypeId_HasSubtype, 45);
-UA_ExpandedNodeId RefTypeId_HasProperty; NS0EXPANDEDNODEID(RefTypeId_HasProperty, 46);
-UA_ExpandedNodeId RefTypeId_HasComponent; NS0EXPANDEDNODEID(RefTypeId_HasComponent, 47);
-UA_ExpandedNodeId RefTypeId_HasNotifier; NS0EXPANDEDNODEID(RefTypeId_HasNotifier, 48);
-UA_ExpandedNodeId RefTypeId_HasOrderedComponent; NS0EXPANDEDNODEID(RefTypeId_HasOrderedComponent, 49);
-UA_ExpandedNodeId RefTypeId_HasModelParent; NS0EXPANDEDNODEID(RefTypeId_HasModelParent, 50);
-UA_ExpandedNodeId RefTypeId_FromState; NS0EXPANDEDNODEID(RefTypeId_FromState, 51);
-UA_ExpandedNodeId RefTypeId_ToState; NS0EXPANDEDNODEID(RefTypeId_ToState, 52);
-UA_ExpandedNodeId RefTypeId_HasCause; NS0EXPANDEDNODEID(RefTypeId_HasCause, 53);
-UA_ExpandedNodeId RefTypeId_HasEffect; NS0EXPANDEDNODEID(RefTypeId_HasEffect, 54);
-UA_ExpandedNodeId RefTypeId_HasHistoricalConfiguration; NS0EXPANDEDNODEID(RefTypeId_HasHistoricalConfiguration, 56);
-*/
+} UA_AttributeId;
+
 /**
  * @defgroup services Services
  *

+ 354 - 360
src/server/ua_services_attribute.c

@@ -1,374 +1,368 @@
+#include "ua_server_internal.h"
 #include "ua_services.h"
-
 #include "ua_statuscodes.h"
-#include "ua_server_internal.h"
-#include "ua_namespace_manager.h"
+#include "ua_nodestore.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"
-/*
+
 #define CHECK_NODECLASS(CLASS)                                 \
     if(!(node->nodeClass & (CLASS))) {                         \
-        v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; \
-        v.status       = UA_STATUSCODE_BADNOTREADABLE;         \
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; \
+        v->status       = UA_STATUSCODE_BADNOTREADABLE;         \
         break;                                                 \
     }                                                          \
 
-static UA_DataValue service_read_node(UA_Server *server,
-		const UA_ReadValueId *id) {
-	UA_DataValue v;
-	UA_DataValue_init(&v);
-
-	UA_Node const *node = UA_NULL;
-	UA_Int32 result = UA_NodeStoreExample_get(server->nodestore, &(id->nodeId),
-			&node);
-	if (result != UA_STATUSCODE_GOOD || node == UA_NULL) {
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADNODEIDUNKNOWN;
-		return v;
-	}
-	UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
-	switch (id->attributeId) {
-	case UA_ATTRIBUTEID_NODEID:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID],
-				&node->nodeId);
-		break;
-
-	case UA_ATTRIBUTEID_NODECLASS:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32],
-				&node->nodeClass);
-		break;
-
-	case UA_ATTRIBUTEID_BROWSENAME:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_QUALIFIEDNAME],
-				&node->browseName);
-		break;
-
-	case UA_ATTRIBUTEID_DISPLAYNAME:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&node->displayName);
-		break;
-
-	case UA_ATTRIBUTEID_DESCRIPTION:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&node->description);
-		break;
-
-	case UA_ATTRIBUTEID_WRITEMASK:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32],
-				&node->writeMask);
-		break;
-
-	case UA_ATTRIBUTEID_USERWRITEMASK:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32],
-				&node->userWriteMask);
-		break;
-
-	case UA_ATTRIBUTEID_ISABSTRACT:
-		CHECK_NODECLASS(
-				UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE
-						| UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ReferenceTypeNode *) node)->isAbstract);
-		break;
-
-	case UA_ATTRIBUTEID_SYMMETRIC:
-		CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ReferenceTypeNode *) node)->symmetric);
-		break;
-
-	case UA_ATTRIBUTEID_INVERSENAME:
-		CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT],
-				&((UA_ReferenceTypeNode *) node)->inverseName);
-		break;
-
-	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-		CHECK_NODECLASS(UA_NODECLASS_VIEW)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_ViewNode *) node)->containsNoLoops);
-		break;
-
-	case UA_ATTRIBUTEID_EVENTNOTIFIER:
-		CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_ViewNode *) node)->eventNotifier);
-		break;
-
-	case UA_ATTRIBUTEID_VALUE:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copy(&((UA_VariableNode *) node)->value, &v.value); // todo: zero-copy
-		break;
-
-	case UA_ATTRIBUTEID_DATATYPE:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID],
-				&((UA_VariableTypeNode *) node)->dataType);
-		break;
-
-	case UA_ATTRIBUTEID_VALUERANK:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32],
-				&((UA_VariableTypeNode *) node)->valueRank);
-		break;
-
-	case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		UA_Variant_copySetArray(&v.value, &UA_TYPES[UA_UINT32],
-				((UA_VariableTypeNode *) node)->arrayDimensionsSize,
-				&((UA_VariableTypeNode *) node)->arrayDimensions);
-		break;
-
-	case UA_ATTRIBUTEID_ACCESSLEVEL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_VariableNode *) node)->accessLevel);
-		break;
-
-	case UA_ATTRIBUTEID_USERACCESSLEVEL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE],
-				&((UA_VariableNode *) node)->userAccessLevel);
-		break;
-
-	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_DOUBLE],
-				&((UA_VariableNode *) node)->minimumSamplingInterval);
-		break;
-
-	case UA_ATTRIBUTEID_HISTORIZING:
-		CHECK_NODECLASS(UA_NODECLASS_VARIABLE)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_VariableNode *) node)->historizing);
-		break;
-
-	case UA_ATTRIBUTEID_EXECUTABLE:
-		CHECK_NODECLASS(UA_NODECLASS_METHOD)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_MethodNode *) node)->executable);
-		break;
-
-	case UA_ATTRIBUTEID_USEREXECUTABLE:
-		CHECK_NODECLASS(UA_NODECLASS_METHOD)
-		;
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
-		retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN],
-				&((UA_MethodNode *) node)->userExecutable);
-		break;
-
-	default:
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-		break;
-	}
-
-	UA_NodeStoreExample_releaseManagedNode(node);
-
-	if (retval != UA_STATUSCODE_GOOD) {
-		v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
-		v.status = UA_STATUSCODE_BADNOTREADABLE;
-	}
-
-	return v;
+/** Reads a single attribute from a node in the nodestore. */
+static void __readValue(UA_Server *server, const UA_ReadValueId *id, UA_DataValue *v) {
+    UA_Node const *node   = UA_NULL;
+    UA_Int32       result = UA_NodeStore_get(server->nodestore, &(id->nodeId), &node);
+    if(result != UA_STATUSCODE_GOOD || node == UA_NULL) {
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADNODEIDUNKNOWN;
+        return;
+    }
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+    switch(id->attributeId) {
+    case UA_ATTRIBUTEID_NODEID:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_NODEID], &node->nodeId);
+        break;
+
+    case UA_ATTRIBUTEID_NODECLASS:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_INT32], &node->nodeClass);
+        break;
+
+    case UA_ATTRIBUTEID_BROWSENAME:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_QUALIFIEDNAME], &node->browseName);
+        break;
+
+    case UA_ATTRIBUTEID_DISPLAYNAME:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->displayName);
+        break;
+
+    case UA_ATTRIBUTEID_DESCRIPTION:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->description);
+        break;
+
+    case UA_ATTRIBUTEID_WRITEMASK:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_UINT32], &node->writeMask);
+        break;
+
+    case UA_ATTRIBUTEID_USERWRITEMASK:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_UINT32], &node->userWriteMask);
+        break;
+
+    case UA_ATTRIBUTEID_ISABSTRACT:
+        CHECK_NODECLASS(
+            UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
+            UA_NODECLASS_DATATYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->isAbstract);
+        break;
+
+    case UA_ATTRIBUTEID_SYMMETRIC:
+        CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
+                                          &((UA_ReferenceTypeNode *)node)->symmetric);
+        break;
+
+    case UA_ATTRIBUTEID_INVERSENAME:
+        CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_LOCALIZEDTEXT],
+                                          &((UA_ReferenceTypeNode *)node)->inverseName);
+        break;
+
+    case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+        CHECK_NODECLASS(UA_NODECLASS_VIEW);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
+                                          &((UA_ViewNode *)node)->containsNoLoops);
+        break;
+
+    case UA_ATTRIBUTEID_EVENTNOTIFIER:
+        CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
+                                          &((UA_ViewNode *)node)->eventNotifier);
+        break;
+
+    case UA_ATTRIBUTEID_VALUE:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v->value); // todo: zero-copy
+        break;
+
+    case UA_ATTRIBUTEID_DATATYPE:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_NODEID],
+                                          &((UA_VariableTypeNode *)node)->dataType);
+        break;
+
+    case UA_ATTRIBUTEID_VALUERANK:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_INT32],
+                                          &((UA_VariableTypeNode *)node)->valueRank);
+        break;
+
+    case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        UA_Variant_copySetArray(&v->value, &UA_TYPES[UA_UINT32],
+                                ((UA_VariableTypeNode *)node)->arrayDimensionsSize,
+                                &((UA_VariableTypeNode *)node)->arrayDimensions);
+        break;
+
+    case UA_ATTRIBUTEID_ACCESSLEVEL:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
+                                          &((UA_VariableNode *)node)->accessLevel);
+        break;
+
+    case UA_ATTRIBUTEID_USERACCESSLEVEL:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BYTE],
+                                          &((UA_VariableNode *)node)->userAccessLevel);
+        break;
+
+    case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_DOUBLE],
+                                          &((UA_VariableNode *)node)->minimumSamplingInterval);
+        break;
+
+    case UA_ATTRIBUTEID_HISTORIZING:
+        CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
+                                          &((UA_VariableNode *)node)->historizing);
+        break;
+
+    case UA_ATTRIBUTEID_EXECUTABLE:
+        CHECK_NODECLASS(UA_NODECLASS_METHOD);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
+                                          &((UA_MethodNode *)node)->executable);
+        break;
+
+    case UA_ATTRIBUTEID_USEREXECUTABLE:
+        CHECK_NODECLASS(UA_NODECLASS_METHOD);
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT;
+        retval |= UA_Variant_copySetValue(&v->value, &UA_TYPES[UA_BOOLEAN],
+                                          &((UA_MethodNode *)node)->userExecutable);
+        break;
+
+    default:
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        break;
+    }
+
+    UA_NodeStore_release(node);
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        v->encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE;
+        v->status       = UA_STATUSCODE_BADNOTREADABLE;
+    }
 }
-*/
-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);
-
-	if (request->nodesToReadSize <= 0) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-		return;
-	}
-	if (UA_Array_new((void **) &response->results, request->nodesToReadSize,
-			&UA_TYPES[UA_DATAVALUE]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &response->diagnosticInfos,
-			request->nodesToReadSize, &UA_TYPES[UA_DIAGNOSTICINFO])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->diagnosticInfosSize = request->nodesToReadSize;
-	response->resultsSize = request->nodesToReadSize;
-
-	UA_Int32 *numberOfFoundIndices;
-	UA_UInt16 *associatedIndices;
-	UA_UInt32 differentNamespaceIndexCount = 0;
-	if (UA_Array_new((void **) &numberOfFoundIndices, request->nodesToReadSize,
-			&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &associatedIndices, request->nodesToReadSize,
-			&UA_TYPES[UA_UINT16]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	// find out count of different namespace indices
-	BUILD_INDEX_ARRAYS(request->nodesToReadSize,request->nodesToRead,nodeId,differentNamespaceIndexCount,associatedIndices,numberOfFoundIndices);
-
-
-	UA_UInt32 *readValueIdIndices;
-	if (UA_Array_new((void **) &readValueIdIndices, request->nodesToReadSize,
-			&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	for (UA_UInt32 i = 0; i < differentNamespaceIndexCount; i++) {
-		UA_Namespace *tmpNamespace;
-		UA_NamespaceManager_getNamespace(&server->namespaceManager,
-				associatedIndices[i], &tmpNamespace);
-		if (tmpNamespace != UA_NULL) {
-
-			//build up index array for each read operation onto a different namespace
-			UA_UInt32 n = 0;
-			for (UA_Int32 j = 0; j < request->nodesToReadSize; j++) {
-				if (request->nodesToRead[j].nodeId.namespaceIndex
-						== associatedIndices[i]) {
-					readValueIdIndices[n] = j;
-					n++;
-				}
-			}
-			//call read for every namespace
-			tmpNamespace->nodeStore->readNodes(&request->requestHeader, request->nodesToRead,
-					readValueIdIndices, numberOfFoundIndices[i],
-					response->results, request->timestampsToReturn,
-					response->diagnosticInfos);
-
-			//	response->results[i] = service_read_node(server, &request->nodesToRead[i]);
-		}
-	}
-	UA_free(readValueIdIndices);
-	UA_free(numberOfFoundIndices);
-	UA_free(associatedIndices);
-
-//	 for(UA_Int32 i = 0;i < response->resultsSize;i++){
-//	 response->results[i] = service_read_node(server, &request->nodesToRead[i]);
-//	 }
+
+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;
+    }
+
+    UA_StatusCode retval = UA_Array_new((void**)&response->results,
+                                        request->nodesToReadSize,
+                                        &UA_TYPES[UA_DATAVALUE]);
+    if(retval) {
+        response->responseHeader.serviceResult = retval;
+        return;
+    }
+    response->resultsSize = request->nodesToReadSize;
+
+    /* ### Begin External Namespaces */
+    UA_Boolean isExternal[request->nodesToReadSize];
+    memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToReadSize);
+    UA_UInt32 indices[request->nodesToReadSize];
+    for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
+        UA_UInt32 indexSize = 0;
+        for(UA_Int32 i = 0;i < response->resultsSize;i++) {
+            if(request->nodesToRead[i].nodeId.namespaceIndex !=
+               server->externalNamespaces[j].index)
+                continue;
+            isExternal[i] = UA_TRUE;
+            indices[indexSize] = i;
+            indexSize++;
+        }
+        if(indexSize == 0)
+            continue;
+        UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+        ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
+                       indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
+    }
+    /* ### End External Namespaces */
+
+    for(UA_Int32 i = 0;i < response->resultsSize;i++) {
+        if(!isExternal[i])
+            __readValue(server, &request->nodesToRead[i], &response->results[i]);
+    }
 }
 
-void Service_Write(UA_Server *server, UA_Session *session,
-		const UA_WriteRequest *request, UA_WriteResponse *response) {
-	UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
-
-	response->resultsSize = request->nodesToWriteSize;
-
-	if (UA_Array_new((void **) &response->results, request->nodesToWriteSize,
-			&UA_TYPES[UA_STATUSCODE])) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (request->nodesToWriteSize <= 0) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-		return;
-	}
-
-	if (UA_Array_new((void **) &response->results, request->nodesToWriteSize,
-			&UA_TYPES[UA_DATAVALUE]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &response->diagnosticInfos,
-			request->nodesToWriteSize, &UA_TYPES[UA_DIAGNOSTICINFO])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->diagnosticInfosSize = request->nodesToWriteSize;
-	UA_UInt32 *numberOfFoundIndices;
-	UA_UInt16 *associatedIndices;
-	UA_UInt32 differentNamespaceIndexCount = 0;
-	if (UA_Array_new((void **) &numberOfFoundIndices, request->nodesToWriteSize,
-			&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &associatedIndices, request->nodesToWriteSize,
-			&UA_TYPES[UA_UINT16]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	// find out count of different namespace indices
-	BUILD_INDEX_ARRAYS(request->nodesToWriteSize,request->nodesToWrite,nodeId,differentNamespaceIndexCount,associatedIndices,numberOfFoundIndices);
-
-
-	UA_UInt32 *writeValues;
-	if (UA_Array_new((void **) &writeValues, request->nodesToWriteSize,
-			&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	for (UA_UInt32 i = 0; i < differentNamespaceIndexCount; i++) {
-		UA_Namespace *tmpNamespace;
-		UA_NamespaceManager_getNamespace(&server->namespaceManager,
-				associatedIndices[i], &tmpNamespace);
-		if (tmpNamespace != UA_NULL) {
-
-			//build up index array for each read operation onto a different namespace
-			UA_UInt32 n = 0;
-			for (UA_Int32 j = 0; j < request->nodesToWriteSize; j++) {
-				if (request->nodesToWrite[j].nodeId.namespaceIndex
-						== associatedIndices[i]) {
-					writeValues[n] = j;
-					n++;
-				}
-			}
-			//call read for every namespace
-			tmpNamespace->nodeStore->writeNodes(&request->requestHeader,request->nodesToWrite,
-					writeValues, numberOfFoundIndices[i],
-					response->results,
-					response->diagnosticInfos);
-		}
-	}
-	UA_free(writeValues);
-	UA_free(numberOfFoundIndices);
-	UA_free(associatedIndices);
+static UA_StatusCode __writeValue(UA_Server *server, UA_WriteValue *writeValue) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    const UA_Node *node;
+    retval = UA_NodeStore_get(server->nodestore, &writeValue->nodeId, &node);
+    if(retval)
+        return retval;
+
+    switch(writeValue->attributeId) {
+    case UA_ATTRIBUTEID_NODEID:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_NODECLASS:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_BROWSENAME:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_DISPLAYNAME:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_DESCRIPTION:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_WRITEMASK:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_USERWRITEMASK:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_ISABSTRACT:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_SYMMETRIC:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_INVERSENAME:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_EVENTNOTIFIER:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_VALUE:
+        if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT) {
+            retval |= UA_Variant_copy(&writeValue->value.value, &((UA_VariableNode *)node)->value); // todo: zero-copy
+        }
+        break;
+
+    case UA_ATTRIBUTEID_DATATYPE:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_VALUERANK:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_ACCESSLEVEL:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_USERACCESSLEVEL:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_HISTORIZING:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_EXECUTABLE:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    case UA_ATTRIBUTEID_USEREXECUTABLE:
+        /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
+        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+        break;
+
+    default:
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        break;
+    }
+
+    UA_NodeStore_release(node);
+    return retval;
+}
 
+void Service_Write(UA_Server *server, UA_Session *session,
+                   const UA_WriteRequest *request, UA_WriteResponse *response) {
+    UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
+
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToWriteSize, &UA_TYPES[UA_STATUSCODE]);
+    if(retval) {
+        response->responseHeader.serviceResult = retval;
+        return;
+    }
+    
+    response->resultsSize = request->nodesToWriteSize;
+    for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++)
+        response->results[i] = __writeValue(server, &request->nodesToWrite[i]);
 }

+ 18 - 13
src/server/ua_services_internal.h

@@ -3,16 +3,21 @@
  * internally as well (with a simplified API as no access rights are checked).
  */
 
-//#include "ua_session.h"
-//#include "nodestore/ua_nodestore.h"
-//#include "ua_types_generated.h"
-//
-///* @brief Add a reference (and the inverse reference to the target node).
-// *
-// * @param The node to which the reference shall be added
-// * @param The reference itself
-// * @param The namespace where the target node is looked up for the reverse reference (this is omitted if targetns is UA_NULL)
-// */
-//UA_Int32 AddReference(UA_NodeStoreExample *nodestore, UA_Node *node, UA_ReferenceNode *reference);
-//UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
-//                          UA_ExpandedNodeId *parentNodeId, UA_NodeId *referenceTypeId);
+#ifndef UA_SERVICES_INTERNAL_H_
+#define UA_SERVICES_INTERNAL_H_
+
+#include "ua_session.h"
+#include "ua_nodestore.h"
+#include "ua_types_generated.h"
+
+/* @brief Add a reference (and the inverse reference to the target node).
+ *
+ * @param The node to which the reference shall be added
+ * @param The reference itself
+ * @param The namespace where the target node is looked up for the reverse reference (this is omitted if targetns is UA_NULL)
+ */
+UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference);
+UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
+                          const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId);
+
+#endif /* UA_SERVICES_INTERNAL_H_ */

+ 237 - 159
src/server/ua_services_nodemanagement.c

@@ -1,173 +1,251 @@
+#include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
-#include "ua_server_internal.h"
+#include "ua_nodestore.h"
 #include "ua_services_internal.h"
-#include "ua_namespace_manager.h"
 #include "ua_session.h"
 #include "ua_util.h"
 
+#define COPY_STANDARDATTRIBUTES do {                                    \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) {  \
+        vnode->displayName = attr.displayName;                          \
+        UA_LocalizedText_init(&attr.displayName);                       \
+    }                                                                   \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DESCRIPTION) {  \
+        vnode->description = attr.description;                          \
+        UA_LocalizedText_init(&attr.description);                       \
+    }                                                                   \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_WRITEMASK)      \
+        vnode->writeMask = attr.writeMask;                              \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERWRITEMASK)  \
+        vnode->userWriteMask = attr.userWriteMask;                      \
+    } while(0)
+
+static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node,
+                                       const UA_VTable_Entry **vt) {
+    if(attributes->typeId.identifier.numeric != 357) // VariableAttributes_Encoding_DefaultBinary,357,Object
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+
+    UA_VariableAttributes attr;
+    UA_UInt32 pos = 0;
+    // todo return more informative error codes from decodeBinary
+    if(UA_VariableAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
+
+    UA_VariableNode *vnode = UA_VariableNode_new();
+    if(!vnode) {
+        UA_VariableAttributes_deleteMembers(&attr);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    // now copy all the attributes. This potentially removes them from the decoded attributes.
+    COPY_STANDARDATTRIBUTES;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ACCESSLEVEL)
+        vnode->accessLevel = attr.accessLevel;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERACCESSLEVEL)
+        vnode->userAccessLevel = attr.userAccessLevel;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_HISTORIZING)
+        vnode->historizing = attr.historizing;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL)
+        vnode->minimumSamplingInterval = attr.minimumSamplingInterval;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUERANK)
+        vnode->valueRank = attr.valueRank;
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_ARRAYDIMENSIONS) {
+        vnode->arrayDimensionsSize = attr.arrayDimensionsSize;
+        vnode->arrayDimensions = attr.arrayDimensions;
+        attr.arrayDimensionsSize = -1;
+        attr.arrayDimensions = UA_NULL;
+    }
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DATATYPE ||
+       attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_OBJECTTYPEORDATATYPE) {
+        vnode->dataType = attr.dataType;
+        UA_NodeId_init(&attr.dataType);
+    }
+
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUE) {
+        vnode->value = attr.value;
+        UA_Variant_init(&attr.value);
+    }
+
+    UA_VariableAttributes_deleteMembers(&attr);
+
+    *new_node = (UA_Node*)vnode;
+    *vt = &UA_TYPES[UA_VARIABLENODE];
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference);
+
+/**
+   If adding the node succeeds, the pointer will be set to zero. If the nodeid
+   of the node is null (ns=0,i=0), a unique new nodeid will be assigned and
+   returned in the AddNodesResult.
+ */
+UA_AddNodesResult AddNode(UA_Server *server, UA_Session *session, UA_Node **node,
+                          const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
+    UA_AddNodesResult result;
+    UA_AddNodesResult_init(&result);
+    
+    const UA_Node *parent;
+    if(UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId, &parent) != UA_STATUSCODE_GOOD) {
+        result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
+        return result;
+    }
+
+    const UA_ReferenceTypeNode *referenceType;
+    if(UA_NodeStore_get(server->nodestore, referenceTypeId, (const UA_Node**)&referenceType) != UA_STATUSCODE_GOOD) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        goto ret;
+    }
+
+    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        goto ret2;
+    }
+
+    if(referenceType->isAbstract == UA_TRUE) {
+        result.statusCode = UA_STATUSCODE_BADREFERENCENOTALLOWED;
+        goto ret2;
+    }
+
+    // todo: test if the referenetype is hierarchical
+
+    if(UA_NodeId_isNull(&(*node)->nodeId)) {
+        if(UA_NodeStore_insert(server->nodestore, node,
+                               UA_NODESTORE_INSERT_UNIQUE | UA_NODESTORE_INSERT_GETMANAGED) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto ret2;
+        }
+        result.addedNodeId = (*node)->nodeId; // cannot fail as unique nodeids are numeric
+    } else {
+        if(UA_NodeId_copy(&(*node)->nodeId, &result.addedNodeId) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto ret2;
+        }
+
+        if(UA_NodeStore_insert(server->nodestore, node, UA_NODESTORE_INSERT_GETMANAGED) != UA_STATUSCODE_GOOD) {
+            result.statusCode = UA_STATUSCODE_BADNODEIDEXISTS;  // todo: differentiate out of memory
+            UA_NodeId_deleteMembers(&result.addedNodeId);
+            goto ret2;
+        }
+    }
+    
+    UA_ReferenceNode ref;
+    UA_ReferenceNode_init(&ref);
+    ref.referenceTypeId = referenceType->nodeId; // is numeric
+    ref.isInverse = UA_TRUE; // todo: check if they are all not inverse..
+    ref.targetId.nodeId = parent->nodeId;
+    AddReference(server->nodestore, *node, &ref);
+
+    // todo: error handling. remove new node from nodestore
+
+    UA_NodeStore_release(*node);
+    *node = UA_NULL;
+    
+ ret2:
+    UA_NodeStore_release((UA_Node*)referenceType);
+ ret:
+    UA_NodeStore_release(parent);
+
+    return result;
+}
+
+static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
+                                  UA_AddNodesResult *result) {
+    if(item->requestedNewNodeId.nodeId.namespaceIndex == 0) {
+        // adding nodes to ns0 is not allowed over the wire
+        result->statusCode = UA_STATUSCODE_BADNODEIDREJECTED;
+        return;
+    }
+
+    UA_Node *newNode;
+    const UA_VTable_Entry *newNodeVT = UA_NULL;
+    if(item->nodeClass == UA_NODECLASS_VARIABLE)
+        result->statusCode = parseVariableNode(&item->nodeAttributes, &newNode, &newNodeVT);
+    else // add more node types here..
+        result->statusCode = UA_STATUSCODE_BADNOTIMPLEMENTED;
+
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
+
+    *result = AddNode(server, session, &newNode, &item->parentNodeId, &item->referenceTypeId);
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        newNodeVT->delete(newNode);
+}
+
 void Service_AddNodes(UA_Server *server, UA_Session *session,
-		const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
-	UA_assert(server != UA_NULL && session != UA_NULL);
-
-	if (request->nodesToAddSize <= 0) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-		return;
-	}
-
-	if (UA_Array_new((void **) &response->results, request->nodesToAddSize,
-			&UA_TYPES[UA_ADDNODESRESULT]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->resultsSize = request->nodesToAddSize;
-	if (UA_Array_new((void **) &response->diagnosticInfos,
-			request->nodesToAddSize, &UA_TYPES[UA_DIAGNOSTICINFO])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->diagnosticInfosSize = request->nodesToAddSize;
-
-
-
-	UA_Int32 *numberOfFoundIndices;
-	UA_UInt16 *associatedIndices;
-	UA_UInt32 differentNamespaceIndexCount = 0;
-	if (UA_Array_new((void **) &numberOfFoundIndices,
-			request->nodesToAddSize, &UA_TYPES[UA_UINT32])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &associatedIndices, request->nodesToAddSize,
-			&UA_TYPES[UA_UINT16]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	// find out count of different namespace indices
-	BUILD_INDEX_ARRAYS(request->nodesToAddSize,request->nodesToAdd,requestedNewNodeId.nodeId,differentNamespaceIndexCount,associatedIndices,numberOfFoundIndices);
-
-	UA_UInt32 *addNodesIndices;
-	if (UA_Array_new((void **) &addNodesIndices,
-			request->nodesToAddSize, &UA_TYPES[UA_UINT32])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	for (UA_UInt32 i = 0; i < differentNamespaceIndexCount; i++) {
-		UA_Namespace *tmpNamespace;
-		UA_NamespaceManager_getNamespace(&server->namespaceManager,
-				associatedIndices[i], &tmpNamespace);
-		if (tmpNamespace != UA_NULL) {
-			//build up index array for each read operation onto a different namespace
-			UA_UInt32 n = 0;
-			for (UA_Int32 j = 0; j < request->nodesToAddSize; j++) {
-				if (request->nodesToAdd[j].requestedNewNodeId.nodeId.namespaceIndex
-						== associatedIndices[i]) {
-					addNodesIndices[n] = j;
-					n++;
-				}
-			}
-			//call read for every namespace
-			tmpNamespace->nodeStore->addNodes(&request->requestHeader,request->nodesToAdd,
-					addNodesIndices, numberOfFoundIndices[i],
-					response->results, response->diagnosticInfos);
-		}
-	}
-	UA_free(addNodesIndices);
-	UA_free(numberOfFoundIndices);
-	UA_free(associatedIndices);
+                      const UA_AddNodesRequest *request, UA_AddNodesResponse *response) {
+    if(request->nodesToAddSize <= 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
+
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToAddSize, &UA_TYPES[UA_ADDNODESRESULT]);
+    if(retval) {
+        response->responseHeader.serviceResult = retval;
+        return;
+    }
+    
+    response->resultsSize = request->nodesToAddSize;
+    for(int i = 0;i < request->nodesToAddSize;i++)
+        addNodeFromAttributes(server, session, &request->nodesToAdd[i], &response->results[i]);
 }
 
+static UA_Int32 AddSingleReference(UA_Node *node, UA_ReferenceNode *reference) {
+    // TODO: Check if reference already exists
+    UA_Int32 count = node->referencesSize;
+    UA_ReferenceNode *old_refs = node->references;
+    UA_ReferenceNode *new_refs;
+
+    if(count < 0) count = 0;
+
+    if(!(new_refs = UA_alloc(sizeof(UA_ReferenceNode)*(count+1))))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    UA_memcpy(new_refs, old_refs, sizeof(UA_ReferenceNode)*count);
+    if(UA_ReferenceNode_copy(reference, &new_refs[count]) != UA_STATUSCODE_GOOD) {
+        UA_free(new_refs);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
 
+    node->references     = new_refs;
+    node->referencesSize = count+1;
+    UA_free(old_refs);
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_Int32 AddReference(UA_NodeStore *nodestore, UA_Node *node, UA_ReferenceNode *reference) {
+    UA_Int32 retval = AddSingleReference(node, reference);
+    UA_Node *targetnode;
+    UA_ReferenceNode inversereference;
+    if(retval != UA_STATUSCODE_GOOD || nodestore == UA_NULL)
+        return retval;
+
+    // Do a copy every time?
+    if(UA_NodeStore_get(nodestore, &reference->targetId.nodeId, (const UA_Node **)&targetnode) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADINTERNALERROR;
+
+    inversereference.referenceTypeId       = reference->referenceTypeId;
+    inversereference.isInverse             = !reference->isInverse;
+    inversereference.targetId.nodeId       = node->nodeId;
+    inversereference.targetId.namespaceUri = UA_STRING_NULL;
+    inversereference.targetId.serverIndex  = 0;
+    retval = AddSingleReference(targetnode, &inversereference);
+    UA_NodeStore_release(targetnode);
+
+    return retval;
+}
 
 void Service_AddReferences(UA_Server *server, UA_Session *session,
-		const UA_AddReferencesRequest *request,
-		UA_AddReferencesResponse *response) {
-
-	if (request->referencesToAddSize <= 0) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-		return;
-	}
-
-	if (UA_Array_new((void **) &response->results, request->referencesToAddSize,
-			&UA_TYPES[UA_STATUSCODE]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->resultsSize = request->referencesToAddSize;
-
-	if (UA_Array_new((void **) &response->diagnosticInfos,
-			request->referencesToAddSize, &UA_TYPES[UA_DIAGNOSTICINFO])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	response->diagnosticInfosSize = request->referencesToAddSize;
-	UA_Int32 *numberOfFoundIndices;
-	UA_UInt16 *associatedIndices;
-	UA_UInt32 differentNamespaceIndexCount = 0;
-	if (UA_Array_new((void **) &numberOfFoundIndices,
-			request->referencesToAddSize, &UA_TYPES[UA_UINT32])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	if (UA_Array_new((void **) &associatedIndices, request->referencesToAddSize,
-			&UA_TYPES[UA_UINT16]) != UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-	// find out count of different namespace indices
-
-
-	BUILD_INDEX_ARRAYS(request->referencesToAddSize, request->referencesToAdd, sourceNodeId,differentNamespaceIndexCount, associatedIndices, numberOfFoundIndices);
-
-	UA_UInt32 *readValueIdIndices;
-	if (UA_Array_new((void **) &readValueIdIndices,
-			request->referencesToAddSize, &UA_TYPES[UA_UINT32])
-			!= UA_STATUSCODE_GOOD) {
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-		return;
-	}
-
-	for (UA_UInt32 i = 0; i < differentNamespaceIndexCount; i++) {
-		UA_Namespace *tmpNamespace;
-		UA_NamespaceManager_getNamespace(&server->namespaceManager,
-				associatedIndices[i], &tmpNamespace);
-		if (tmpNamespace != UA_NULL) {
-			//build up index array for each read operation onto a different namespace
-			UA_UInt32 n = 0;
-			for (UA_Int32 j = 0; j < request->referencesToAddSize; j++) {
-				if (request->referencesToAdd[j].sourceNodeId.namespaceIndex
-						== associatedIndices[i]) {
-					readValueIdIndices[n] = j;
-					n++;
-				}
-			}
-			//call read for every namespace
-			tmpNamespace->nodeStore->addReferences(&request->requestHeader,request->referencesToAdd,
-					readValueIdIndices, numberOfFoundIndices[i],
-					response->results, response->diagnosticInfos);
-
-			//	response->results[i] = service_read_node(server, &request->nodesToRead[i]);
-		}
-	}
-	UA_free(readValueIdIndices);
-	UA_free(numberOfFoundIndices);
-	UA_free(associatedIndices);
-	/*
-	 for(UA_Int32 i = 0;i < response->resultsSize;i++){
-	 response->results[i] = service_read_node(server, &request->nodesToRead[i]);
-	 }
-	 }
-	 */
-
-
-	//response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTIMPLEMENTED;
+                           const UA_AddReferencesRequest *request,
+                           UA_AddReferencesResponse *response) {
+    response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTIMPLEMENTED;
 }

+ 215 - 65
src/server/ua_services_view.c

@@ -1,100 +1,250 @@
+#include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_statuscodes.h"
-#include "ua_server_internal.h"
+#include "ua_nodestore.h"
 #include "ua_namespace_0.h"
 #include "ua_util.h"
-#include "ua_namespace_manager.h"
 
-void Service_Browse(UA_Server *server, UA_Session *session,
-                    const UA_BrowseRequest *request, UA_BrowseResponse *response) {
-    UA_Int32 *numberOfFoundIndices;
-    UA_UInt16 *associatedIndices;
-    UA_UInt32 differentNamespaceIndexCount = 0;
-	UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
+/* 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_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_ReferenceDescription_init(referenceDescription);
+    retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
+    //TODO: ExpandedNodeId is mocked up
+    referenceDescription->nodeId.serverIndex = 0;
+    referenceDescription->nodeId.namespaceUri.length = -1;
 
-    if(request->nodesToBrowseSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
+    if(resultMask & UA_BROWSERESULTMASK_REFERENCETYPEID)
+        retval |= UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
+    if(resultMask & UA_BROWSERESULTMASK_ISFORWARD)
+        referenceDescription->isForward = !reference->isInverse;
+    if(resultMask & UA_BROWSERESULTMASK_NODECLASS)
+        retval |= UA_NodeClass_copy(&currentNode->nodeClass, &referenceDescription->nodeClass);
+    if(resultMask & UA_BROWSERESULTMASK_BROWSENAME)
+        retval |= UA_QualifiedName_copy(&currentNode->browseName, &referenceDescription->browseName);
+    if(resultMask & UA_BROWSERESULTMASK_DISPLAYNAME)
+        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 */) {
+                retval |= UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
+                break;
+            }
+        }
     }
 
-    if(UA_Array_new((void **)&(response->results), request->nodesToBrowseSize, &UA_TYPES[UA_BROWSERESULT])
-       != UA_STATUSCODE_GOOD) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
+    if(currentNode)
+        UA_NodeStore_release(currentNode);
+    if(retval)
+        UA_ReferenceDescription_deleteMembers(referenceDescription);
+    return retval;
+}
+
+/* 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 *
+getRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription, UA_Boolean returnAll,
+                      UA_ReferenceNode *reference, UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
+    if(reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
+        return UA_NULL;
+
+    else if(reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
+        return UA_NULL;
+
+    UA_Boolean isRelevant = returnAll;
+    if(!isRelevant) {
+        for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
+            if(UA_NodeId_equal(&reference->referenceTypeId, &relevantRefTypes[i]))
+                isRelevant = UA_TRUE;
+        }
+        if(!isRelevant)
+            return UA_NULL;
     }
 
-    response->resultsSize = request->nodesToBrowseSize;
-    if(UA_Array_new((void **)&numberOfFoundIndices,request->nodesToBrowseSize,&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD){
-    	response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-    	return ;
+    const UA_Node *node;
+    if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &node) != UA_STATUSCODE_GOOD)
+        return UA_NULL;
+
+    if(browseDescription->nodeClassMask != 0 && (node->nodeClass & browseDescription->nodeClassMask) == 0) {
+        UA_NodeStore_release(node);
+        return UA_NULL;
     }
 
-    if(UA_Array_new((void **)&associatedIndices,request->nodesToBrowseSize,&UA_TYPES[UA_UINT16]) != UA_STATUSCODE_GOOD){
-    	response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-    	return ;
+    return node;
+}
+
+/* We do not search across namespaces so far. The id of the root-referencetype
+   is returned in the array also. */
+static UA_StatusCode 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;
     }
-    if(UA_Array_new((void **)&response->diagnosticInfos,request->nodesToBrowseSize,&UA_TYPES[UA_DIAGNOSTICINFO]) != UA_STATUSCODE_GOOD){
-    	response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-    	return ;
+        
+    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;
+
+        // 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_NodeStore_release((UA_Node*)node);
+    } while(++currentIndex <= currentLastIndex && retval == UA_STATUSCODE_GOOD);
+
+    if(retval)
+        UA_Array_delete(typeArray, currentLastIndex, &UA_TYPES[UA_NODEID]);
+    else {
+        *referenceTypes = typeArray;
+        *referenceTypesSize = currentLastIndex + 1;
     }
-    response->diagnosticInfosSize = request->nodesToBrowseSize;
+    
+    return retval;
+}
 
-    BUILD_INDEX_ARRAYS(request->nodesToBrowseSize,request->nodesToBrowse,nodeId,differentNamespaceIndexCount,associatedIndices,numberOfFoundIndices);
+/* 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;
 
-	UA_UInt32 *browseDescriptionIndices;
-    if(UA_Array_new((void **)&browseDescriptionIndices,request->nodesToBrowseSize,&UA_TYPES[UA_UINT32]) != UA_STATUSCODE_GOOD){
-    	response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-    	return ;
+    // 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;
+        }
     }
 
-    for(UA_UInt32 i = 0; i < differentNamespaceIndexCount; i++){
-    	UA_Namespace *tmpNamespace;
-    	UA_NamespaceManager_getNamespace(&server->namespaceManager,associatedIndices[i],&tmpNamespace);
-    	if(tmpNamespace != UA_NULL){
-
-    	    //build up index array for each read operation onto a different namespace
-    	    UA_UInt32 n = 0;
-    	    for(UA_Int32 j = 0; j < request->nodesToBrowseSize; j++){
-    	    	if(request->nodesToBrowse[j].nodeId.namespaceIndex == associatedIndices[i]){
-    	    		browseDescriptionIndices[n] = j;
-					n++;
-    	    	}
-    	    }
-    	    //call read for every namespace
-    		tmpNamespace->nodeStore->browseNodes(&request->requestHeader,
-    				request->nodesToBrowse,
-    				browseDescriptionIndices,
-    				numberOfFoundIndices[i],
-    				request->requestedMaxReferencesPerNode,
-    				response->results,
-    				response->diagnosticInfos);
-    	}
+    const UA_Node *parentNode;
+    if(UA_NodeStore_get(ns, &browseDescription->nodeId, &parentNode) != UA_STATUSCODE_GOOD) {
+        browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+        if(!returnAll && browseDescription->includeSubtypes)
+            UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
+        return;
+    }
+
+    // 0 => unlimited references
+    if(maxReferences == 0 || maxReferences > UA_INT32_MAX || (UA_Int32)maxReferences > parentNode->referencesSize)
+        maxReferences = parentNode->referencesSize;
+
+    /* 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? If yes, the node is retrieved from the nodestore.
+            const UA_Node *currentNode = getRelevantTargetNode(ns, browseDescription, returnAll, &parentNode->references[i],
+                                                               relevantReferenceTypes, relevantReferenceTypesSize);
+            if(!currentNode)
+                continue;
+
+            // 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_TYPES[UA_REFERENCEDESCRIPTION]);
+                currentRefs = 0;
+                browseResult->references = UA_NULL;
+                browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
+                break;
+            }
+            currentRefs++;
+        }
+        if(currentRefs != 0)
+            browseResult->referencesSize = currentRefs;
+        else {
+            UA_free(browseResult->references);
+            browseResult->references = UA_NULL;
+        }
     }
-    UA_free(browseDescriptionIndices);
-    UA_free(numberOfFoundIndices);
-    UA_free(associatedIndices);
+
+    UA_NodeStore_release(parentNode);
+    if(!returnAll && browseDescription->includeSubtypes)
+        UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
 }
 
+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;
+    }
+
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->nodesToBrowseSize, &UA_TYPES[UA_BROWSERESULT]);
+    if(retval) {
+        response->responseHeader.serviceResult = retval;
+        return;
+    }
+        
+    response->resultsSize = request->nodesToBrowseSize;
+    for(UA_Int32 i = 0;i < request->nodesToBrowseSize;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_TYPES[UA_BROWSEPATHRESULT])
-       != UA_STATUSCODE_GOOD) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+    UA_StatusCode retval = UA_Array_new((void**)&response->results, request->browsePathsSize, &UA_TYPES[UA_BROWSEPATHRESULT]);
+    if(retval) {
+        response->responseHeader.serviceResult = retval;
         return;
     }
 
-    for(UA_Int32 i = 0;i < request->browsePathsSize;i++)
+    response->resultsSize = request->browsePathsSize;
+    for(UA_Int32 i = 0;i < response->resultsSize;i++)
         response->results[i].statusCode = UA_STATUSCODE_BADNOMATCH; //FIXME: implement
 }

+ 0 - 137
tests/check_indexedList.c

@@ -1,137 +0,0 @@
-#include <stdlib.h> // EXIT_SUCCESS
-#include "util/ua_indexedList.h"
-#include "util/ua_util.h"
-#include "check.h"
-
-/* global test counters */
-UA_Int32 visit_count = 0;
-UA_Int32 free_count = 0;
-
-void visitor(void* payload){
-	visit_count++;
-}
-
-void freer(void* payload){
-	if(payload){
-		free_count++;
-		UA_free(payload);
-	}
-}
-
-_Bool matcher(void* payload){
-	if(payload == UA_NULL){
-		return FALSE;
-	}
-	if(*((UA_Int32*)payload) == 42){
-		return TRUE;
-	}
-	return FALSE;
-}
-
-START_TEST(linkedList_test_destroyemptylist)
-{
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-	ck_assert_int_eq(list.size, 0);
-	UA_indexedList_destroy(&list, UA_indexedList_defaultFreer);
-	ck_assert_int_eq(list.size, 0);
-}
-END_TEST
-
-START_TEST(linkedList_test_destroysingle)
-{
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-	ck_assert_int_eq(list.size, 0);
-	UA_Int32* payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 10;
-	UA_indexedList_addValue(&list, 1, payload);
-	UA_indexedList_destroy(&list, UA_indexedList_defaultFreer);
-	ck_assert_int_eq(list.size, 0);
-}
-END_TEST
-	
-START_TEST(linkedList_test_basic)
-{
-
-	ck_assert_int_eq(UA_indexedList_addValue(UA_NULL, 0, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_addValueToFront(UA_NULL, 0, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_destroy(UA_NULL, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_find(UA_NULL, 0), UA_NULL);
-	ck_assert_int_eq(UA_indexedList_iterateValues(UA_NULL, UA_NULL), UA_ERROR);
-	ck_assert_int_eq(UA_indexedList_removeElement(UA_NULL, 0, UA_NULL), UA_ERROR);
-
-	UA_indexedList_List list;
-	UA_indexedList_init(&list);
-
-	ck_assert_int_eq(list.size, 0);
-
-	UA_Int32* payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 10;
-	UA_indexedList_addValue(&list, 1, payload);
-	payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 20;
-	UA_indexedList_addValueToFront(&list, 2, payload);
-	payload = (UA_Int32*)malloc(sizeof(*payload));
-	*payload = 30;
-	UA_indexedList_addValue(&list, 3, payload);
-
-	ck_assert_int_eq(list.size, 3);
-
-	UA_indexedList_iterateValues(&list, visitor);
-	ck_assert_int_eq(visit_count, 3);
-	visit_count = 0;
-
-	payload = (UA_Int32*)UA_indexedList_findValue(&list, 2);
-	if(payload){
-		ck_assert_int_eq(*payload, 20);
-	}else{
-		fail("Element 20 not found");
-	}
-
-	UA_indexedList_removeElement(&list, UA_indexedList_find(&list, 2), freer);
-	ck_assert_int_eq(free_count, 1);
-	free_count=0;
-	ck_assert_int_eq(list.size, 2);
-
-	payload = (UA_Int32*)UA_indexedList_findValue(&list, 2);
-	if(payload){
-		fail("Previously removed element 20 found");
-	}
-
-	UA_indexedList_iterateValues(&list, visitor);
-	ck_assert_int_eq(visit_count, 2);
-
-	UA_indexedList_destroy(&list, freer);
-
-	ck_assert_int_eq(free_count, 2);
-	ck_assert_int_eq(list.size, 0);
-	
-}
-END_TEST
-
-Suite*linkedList_testSuite(void)
-{
-	Suite *s = suite_create("linkedList_test");
-	TCase *tc_core = tcase_create("Core");
-	tcase_add_test(tc_core, linkedList_test_basic);
-	tcase_add_test(tc_core, linkedList_test_destroyemptylist);
-	tcase_add_test(tc_core, linkedList_test_destroysingle);
-	suite_add_tcase(s,tc_core);
-	return s;
-}
-
-int main (void)
-{
-	int number_failed = 0;
-
-	Suite* s = linkedList_testSuite();
-	SRunner* sr = srunner_create(s);
-	srunner_run_all(sr,CK_NORMAL);
-	number_failed += srunner_ntests_failed(sr);
-	srunner_free(sr);
-
-	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-
-}
-

+ 12 - 5
tools/generate_builtin.py

@@ -148,14 +148,26 @@ def createStructured(element):
             else:
                 membermap[childname] = "UA_" + typename
 
+    # fixed size?
+    has_fixed_size = True
+    for n,t in membermap.iteritems():
+        if t not in fixed_size:
+            has_fixed_size = False
+    if has_fixed_size:
+        fixed_size.add(name)
+
     # 3) Print structure
     if len(membermap) > 0:
+        if has_fixed_size:
+            printh("#pragma pack (push)\n#pragma pack(1)")
         printh("typedef struct %(name)s {")
         for n,t in membermap.iteritems():
 	    if t.find("*") != -1:
 	        printh("\t" + "UA_Int32 " + n + "Size;")
             printh("\t%(t)s %(n)s;")
         printh("} %(name)s;")
+        if has_fixed_size:
+            printh("#pragma pack (pop)")
     else:
         printh("typedef void* %(name)s;")
         
@@ -168,20 +180,15 @@ def createStructured(element):
     # 4) CalcSizeBinary
     printc('''UA_UInt32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
     return 0''')
-    has_fixed_size = True
     for n,t in membermap.iteritems():
         if t in fixed_size:
             printc('\t + sizeof(%(t)s) // %(n)s')
         elif t.find("*") != -1:
             printc('\t + UA_Array_calcSizeBinary(ptr->%(n)sSize,&UA_TYPES['+ t[0:t.find("*")].upper() +
                    "],ptr->%(n)s)")
-            has_fixed_size = False
         else:
             printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
-            has_fixed_size = False
     printc("\t;\n}\n")
-    if has_fixed_size:
-        fixed_size.add(name)
 
     # 5) EncodeBinary
     printc('''UA_StatusCode %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {

+ 67 - 44
tools/generate_namespace.py

@@ -19,7 +19,6 @@ args = parser.parse_args()
 from type_lists import only_needed_types
 
 # types that are to be excluded
-exclude_kinds = set(["Object","ObjectType","Variable","Method","ReferenceType"])
 exclude_types = set(["Number", "Integer", "UInteger", "Enumeration", "Image", "ImageBMP",
                      "ImageGIF", "ImageJPG", "ImagePNG", "References", "BaseVariableType",
                      "BaseDataVariableType", "PropertyType", "DataTypeDescriptionType",
@@ -60,20 +59,49 @@ fixed_size = ['UA_DeadbandType', 'UA_DataChangeTrigger', 'UA_Guid', 'UA_Applicat
               'UA_ExceptionDeviationFormat', 'UA_ComplianceLevel', 'UA_DateTime', 'UA_Int64',
               'UA_Double']
 
-def skipType(row):
-    if row[0] == "" or row[0] in exclude_types or row[2] in exclude_kinds:
-        return True
+def useDataType(row):
+    if row[0] == "" or row[0] in exclude_types:
+        return False
+    if row[2] != "DataType":
+        return False
     if "Test" in row[0]:
-        return True
+        return False
     if args.only_needed and not(row[0] in only_needed_types):
-        return True
-    return False
+        return False
+    return True
+
+type_names = [] # the names of the datattypes for which we create a vtable entry
+def useOtherType(row):
+    if row[0] in type_names or row[0] == "":
+        return False
+    if row[0].startswith("Audit"):
+        return False
+    if row[0].startswith("Program"):
+        return False
+    if row[0].startswith("Condition"):
+        return False
+    if row[0].startswith("Refresh"):
+        return False
+    if row[0].startswith("OpcUa_"):
+        return False
+    if row[0].startswith("SessionsDiagnosticsSummaryType_"):
+        return False
+    if "Type_" in row[0]:
+        return False
+    if "_Encoding_Default" in row[0]:
+        return False
+    return True
 
 f = open(args.nodeids)
-input_str = f.read() + "\nInvalidType,0,DataType"
+input_str = f.read() + "\nInvalidType,0,DataType" + "\nHasModelParent,50,ReferenceType"
+f.close()
 input_str = input_str.replace('\r','')
 rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
-f.close()
+for index, row in enumerate(rows):
+    if row[0] == "BaseDataType":
+    	rows[index]= ("Variant", row[1], row[2])
+    elif row[0] == "Structure":
+    	rows[index] = ("ExtensionObject", row[1], row[2])
 
 fh = open(args.outfile + ".h",'w')
 fc = open(args.outfile + ".c",'w')
@@ -106,6 +134,7 @@ printh('''/**********************************************************
 UA_Int32 UA_ns0ToVTableIndex(const UA_NodeId *id);\n
 extern const UA_VTable_Entry UA_EXPORT *UA_TYPES;
 extern const UA_NodeId UA_EXPORT *UA_NODEIDS;
+extern const UA_ExpandedNodeId UA_EXPORT *UA_EXPANDEDNODEIDS;
 
 /** The entries of UA_TYPES can be accessed with the following indices */ ''')
 
@@ -124,48 +153,34 @@ UA_Int32 UA_ns0ToVTableIndex(const UA_NodeId *id) {
 	switch (id->identifier.numeric) {''')
 
 i = 0
-for row in rows:
-    if skipType(row):
+for index, row in enumerate(rows):
+    if not useDataType(row):
         continue
-    if row[0] == "BaseDataType":
-    	name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-        name = "UA_" + row[0]
-	
+    type_names.append(row[0])
+    name = "UA_" + row[0]
     printh("#define "+name.upper()+" "+str(i))
+    printh('#define '+name.upper()+'_NS0 '+row[1])
     printc('\tcase '+row[1]+': retval='+name.upper()+'; break; //'+row[2])
     i = i+1
+printc('''\t}\n\treturn retval;\n}\n''');
 
 printh('\n#define SIZE_UA_VTABLE '+str(i));
 printh("") # newline
-printh("/** In UA_NODEIDS are the nodeids of the types, referencetypes and objects */")
-# assign indices to the reference types afterwards
+
+printh("/* Now all the non-datatype nodeids */")
 for row in rows:
-    if row[0] == "" or (row[2] != "ReferenceType" and (row[2] != "Object" or "_Encoding_Default" in row[0])):
+    if not useOtherType(row):
         continue
     name = "UA_" + row[0]
     printh("#define "+name.upper()+" "+str(i))
+    printh('#define '+name.upper()+'_NS0 '+row[1])
     i=i+1
 
-printc('''\n}\nreturn retval;\n}\n''');
-
-printh("") # newline
-printh("/** These are the actual (numeric) nodeids of the types, not the indices to the vtable */")
-
 printc('''const UA_VTable_Entry *UA_TYPES = (UA_VTable_Entry[]){''')
 for row in rows:
-    if skipType(row):
+    if row[0] not in type_names:
         continue
-    if row[0] == "BaseDataType":
-        name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-	name = "UA_" + row[0]
-    printh('#define '+name.upper()+'_NS0 '+row[1])
-
+    name = "UA_" + row[0]
     printc("\t{.typeId={.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric=" + row[1] + "}" + 
            ",\n.name=(UA_Byte*)&\"%(name)s\"" +
            ",\n.new=(void *(*)())%(name)s_new" +
@@ -186,26 +201,34 @@ for row in rows:
             ",\n.decode=(UA_Int32(*)(const UA_ByteString*,UA_UInt32*,void*))%(name)s_decodeXml}" if (args.with_xml) else "") +
            "}},")
 
-printc('};')
+printc('};\n')
 
 # make the nodeids available as well
 printc('''const UA_NodeId *UA_NODEIDS = (UA_NodeId[]){''')
 for row in rows:
-    if skipType(row):
+    if not row[0] in type_names:
         continue
-    if row[0] == "BaseDataType":
-        name = "UA_Variant"
-    elif row[0] == "Structure":
-        name = "UA_ExtensionObject"
-    else:
-	name = "UA_" + row[0]
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
 
 for row in rows:
-    if row[0] == "" or (row[2] != "ReferenceType" and (row[2] != "Object" or "_Encoding_Default" in row[0])):
+    if not useOtherType(row):
         continue
     printc("\t{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] + "},")
+printc('};\n')
+
+# and generate expanded nodeids
+printc('''const UA_ExpandedNodeId *UA_EXPANDEDNODEIDS = (UA_ExpandedNodeId[]){''')
+for row in rows:
+    if not row[0] in type_names:
+        continue
+    printc("\t{.nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] +
+           "}, .namespaceUri = {.length = -1, .data = UA_NULL}, .serverIndex = 0},")
 
+for row in rows:
+    if not useOtherType(row):
+        continue
+    printc("\t{.nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = " + row[1] +
+           "}, .namespaceUri = {.length = -1, .data = UA_NULL}, .serverIndex = 0},")
 printc('};')
 
 printh('\n#endif /* OPCUA_NAMESPACE_0_H_ */')