浏览代码

added namespace files

Julius Pfrommer 11 年之前
父节点
当前提交
d6058b2e0c

+ 452 - 0
OPCUAServer/src/opcua_namespace.c

@@ -0,0 +1,452 @@
+#include "opcua_namespace.h"
+#include <string.h>
+#include <stdio.h>
+
+/* The basic data structure is a hash-map of UA_Node objects.
+
+   Entry lookup via Algorithm D from Knuth's TAOCP (no linked lists here).
+   Table of primes and mod-functions are from libiberty (licensed under LGPL) */
+
+struct prime_ent {
+	hash_t prime;
+	hash_t inv;
+	hash_t inv_m2;	/* inverse of prime-2 */
+	hash_t shift;
+};
+
+static struct prime_ent const prime_tab[] = {
+  {          7, 0x24924925, 0x9999999b, 2 },
+  {         13, 0x3b13b13c, 0x745d1747, 3 },
+  {         31, 0x08421085, 0x1a7b9612, 4 },
+  {         61, 0x0c9714fc, 0x15b1e5f8, 5 },
+  {        127, 0x02040811, 0x0624dd30, 6 },
+  {        251, 0x05197f7e, 0x073260a5, 7 },
+  {        509, 0x01824366, 0x02864fc8, 8 },
+  {       1021, 0x00c0906d, 0x014191f7, 9 },
+  {       2039, 0x0121456f, 0x0161e69e, 10 },
+  {       4093, 0x00300902, 0x00501908, 11 },
+  {       8191, 0x00080041, 0x00180241, 12 },
+  {      16381, 0x000c0091, 0x00140191, 13 },
+  {      32749, 0x002605a5, 0x002a06e6, 14 },
+  {      65521, 0x000f00e2, 0x00110122, 15 },
+  {     131071, 0x00008001, 0x00018003, 16 },
+  {     262139, 0x00014002, 0x0001c004, 17 },
+  {     524287, 0x00002001, 0x00006001, 18 },
+  {    1048573, 0x00003001, 0x00005001, 19 },
+  {    2097143, 0x00004801, 0x00005801, 20 },
+  {    4194301, 0x00000c01, 0x00001401, 21 },
+  {    8388593, 0x00001e01, 0x00002201, 22 },
+  {   16777213, 0x00000301, 0x00000501, 23 },
+  {   33554393, 0x00001381, 0x00001481, 24 },
+  {   67108859, 0x00000141, 0x000001c1, 25 },
+  {  134217689, 0x000004e1, 0x00000521, 26 },
+  {  268435399, 0x00000391, 0x000003b1, 27 },
+  {  536870909, 0x00000019, 0x00000029, 28 },
+  { 1073741789, 0x0000008d, 0x00000095, 29 },
+  { 2147483647, 0x00000003, 0x00000007, 30 },
+  /* Avoid "decimal constant so large it is unsigned" for 4294967291.  */
+  { 0xfffffffb, 0x00000006, 0x00000008, 31 }
+};
+
+UA_Int32 create_ns(namespace **result, uint32_t size) {
+	namespace *ns = UA_NULL;
+	uint32_t sizePrimeIndex = higher_prime_index(size);
+	size = prime_tab[sizePrimeIndex].prime;
+
+	if (UA_alloc((void **)&ns, sizeof(namespace)) != UA_SUCCESS) return UA_ERR_NO_MEMORY;
+  
+	if (UA_alloc((void **)&(ns->entries), sizeof(void *)*size) != UA_SUCCESS) {
+		UA_free(ns);
+		return UA_ERR_NO_MEMORY;
+	}
+	
+	for (uint32_t i=0; i<size;i++)
+		ns->entries[i] = UA_NULL;
+
+	ns->size = size;
+	ns->count = 0;
+	ns->sizePrimeIndex = sizePrimeIndex;
+	*result = ns;
+	return UA_SUCCESS;
+}
+
+void empty_ns(namespace *ns) {
+	uint32_t size = ns->size;
+	UA_Node **entries = ns->entries;
+
+	for (uint32_t i = 0; i < size; i++) {
+		if (entries[i] != UA_NULL) {
+			switch(entries[i]->nodeClass) {
+			case UA_NODECLASS_OBJECT:
+				// UA_ObjectNode_delete((UA_ObjectNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VARIABLE:
+				// UA_VariableNode_delete((UA_VariableNode *) entries[i]);
+				break;
+			case UA_NODECLASS_METHOD:
+				// UA_MethodNode_delete((UA_MethodNode *) entries[i]);
+				break;
+			case UA_NODECLASS_OBJECTTYPE:
+				// UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VARIABLETYPE:
+				// UA_VariableTypeNode_delete((UA_VariableTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_REFERENCETYPE:
+				// UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_DATATYPE:
+				// UA_DataTypeNode_delete((UA_DataTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VIEW:
+				// UA_ViewNode_delete((UA_ViewNode *) entries[i]);
+				break;
+			default:
+				break; // Unspecified nodes are not permitted.
+			}
+		}
+	}	
+
+	/* Downsize the table.  */
+	if (size > 1024*1024 / sizeof (void *)) {
+		int nindex = higher_prime_index (1024 / sizeof (void *));
+		int nsize = prime_tab[nindex].prime;
+
+		UA_free(ns->entries);
+		UA_alloc((void **)&(ns->entries), sizeof(void *)*nsize); //FIXME: Check if memory was available
+		ns->size = nsize;
+		ns->sizePrimeIndex = nindex;
+		ns->count = 0;
+	}
+	else {
+		memset(*(ns->entries), 0, size * sizeof(void *));
+		ns->count = 0;
+	}
+}
+
+/* This function frees all memory allocated for a namespace */
+void delete_ns (namespace *ns) {
+	uint32_t size = ns->size;
+	UA_Node **entries = ns->entries;
+
+	for (uint32_t i = 0; i < size; i++) {
+		if (entries[i] != UA_NULL) {
+			switch(entries[i]->nodeClass) {
+			case UA_NODECLASS_OBJECT:
+				// UA_ObjectNode_delete((UA_ObjectNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VARIABLE:
+				// UA_VariableNode_delete((UA_VariableNode *) entries[i]);
+				break;
+			case UA_NODECLASS_METHOD:
+				// UA_MethodNode_delete((UA_MethodNode *) entries[i]);
+				break;
+			case UA_NODECLASS_OBJECTTYPE:
+				// UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VARIABLETYPE:
+				// UA_VariableTypeNode_delete((UA_VariableTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_REFERENCETYPE:
+				// UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_DATATYPE:
+				// UA_DataTypeNode_delete((UA_DataTypeNode *) entries[i]);
+				break;
+			case UA_NODECLASS_VIEW:
+				// UA_ViewNode_delete((UA_ViewNode *) entries[i]);
+				break;
+			default:
+				break; // Unspecified nodes are not permitted.
+			}
+		}
+	}	
+
+	UA_free(entries);
+	UA_free(ns);
+}
+
+UA_Int32 insert(namespace *ns, UA_Node *node) {
+	if((float)ns->count/(float)ns->size < 0.75) {
+		if(expand(ns) != UA_SUCCESS)
+			return UA_ERROR;
+	}
+		
+	hash_t h = hash(&(node->nodeId));
+	UA_Node **slot = find_empty_slot(ns, h);
+	*slot = node;
+	return UA_SUCCESS;
+}
+
+UA_Int32 find(namespace *ns, UA_Node **result, UA_NodeId *nodeid) {
+	UA_Node **slot = UA_NULL;
+	if(find_slot(ns, slot, nodeid) == UA_SUCCESS) {
+		*result = *slot;
+		return UA_SUCCESS;
+	}
+	return UA_ERROR;
+}
+
+void delete(namespace *ns, UA_NodeId *nodeid) {
+	UA_Node **slot = UA_NULL;
+	if (find_slot(ns, slot, nodeid) != UA_SUCCESS)
+		return;
+
+	UA_Node *node = *slot;
+	*slot = UA_NULL;
+
+	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:
+		break; // Unspecified nodes are not permitted.
+	}
+}
+
+/* Hashing inspired by code from from http://www.azillionmonkeys.com/qed/hash.html, licensed under the LGPL 2.1 */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+static hash_t hash_string (const UA_Byte * data, UA_Int32 len) {
+	hash_t hash = len;
+	hash_t tmp;
+	int rem;
+
+    if (len <= 0 || data == UA_NULL) return 0;
+
+    rem = len & 3;
+    len >>= 2;
+
+    /* Main loop */
+    for (;len > 0; len--) {
+        hash  += get16bits (data);
+        tmp    = (get16bits (data+2) << 11) ^ hash;
+        hash   = (hash << 16) ^ tmp;
+        data  += 2*sizeof (uint16_t);
+        hash  += hash >> 11;
+    }
+
+    /* Handle end cases */
+    switch (rem) {
+	case 3: hash += get16bits (data);
+		hash ^= hash << 16;
+		hash ^= ((signed char)data[sizeof (uint16_t)]) << 18;
+		hash += hash >> 11;
+		break;
+	case 2: hash += get16bits (data);
+		hash ^= hash << 11;
+		hash += hash >> 17;
+		break;
+	case 1: hash += (signed char)*data;
+		hash ^= hash << 10;
+		hash += hash >> 1;
+    }
+
+    /* Force "avalanching" of final 127 bits */
+    hash ^= hash << 3;
+    hash += hash >> 5;
+    hash ^= hash << 4;
+    hash += hash >> 17;
+    hash ^= hash << 25;
+    hash += hash >> 6;
+
+    return hash;
+}
+
+static hash_t hash(const UA_NodeId *n) {
+	switch (n->encodingByte) {
+	case UA_NODEIDTYPE_TWOBYTE:
+	case UA_NODEIDTYPE_FOURBYTE:
+	case UA_NODEIDTYPE_NUMERIC:
+		return n->identifier.numeric;
+	case UA_NODEIDTYPE_STRING:
+		return hash_string(n->identifier.string.data, n->identifier.string.length);
+	case UA_NODEIDTYPE_GUID:
+		return hash_string((UA_Byte*) &(n->identifier.guid), sizeof(UA_Guid));
+	case UA_NODEIDTYPE_BYTESTRING:
+		return hash_string((UA_Byte*) n->identifier.byteString.data, n->identifier.byteString.length);
+	default:
+		// Not permitted
+		return 0;
+	}
+}
+
+/* The following function returns an index into the above table of the nearest prime number which is greater than N, and near a power of two. */
+static unsigned int higher_prime_index (unsigned long n) {
+	unsigned int low = 0;
+	unsigned int high = sizeof(prime_tab) / sizeof(prime_tab[0]);
+
+	while (low != high) {
+		unsigned int mid = low + (high - low) / 2;
+		if (n > prime_tab[mid].prime)
+			low = mid + 1;
+		else
+			high = mid;
+	}
+
+	// Fixme: If we've run out of primes, abort.
+	/* if (n > prime_tab[low].prime) */
+	/* 	abort ();  */
+  
+	return low;
+}
+
+/* Return X % Y. */
+static inline hash_t mod_1 (hash_t x, hash_t y, hash_t inv, int shift) {
+	/* The multiplicative inverses computed above are for 32-bit types, and requires that we are able to compute a highpart multiply.  */
+#ifdef UNSIGNED_64BIT_TYPE
+	__extension__ typedef UNSIGNED_64BIT_TYPE ull;
+	if (sizeof (hash_t) * CHAR_BIT <= 32) {
+		hash_t t1, t2, t3, t4, q, r;
+		t1 = ((ull)x * inv) >> 32;
+		t2 = x - t1;
+		t3 = t2 >> 1;
+		t4 = t1 + t3;
+		q  = t4 >> shift;
+		r  = x - (q * y);
+		return r;
+	}
+#endif
+
+	return x % y; /* Otherwise just use the native division routines.  */
+}
+
+static inline hash_t mod (hash_t h, const namespace *ns) {
+	const struct prime_ent *p = &prime_tab[ns->sizePrimeIndex];
+	return mod_1 (h, p->prime, p->inv, p->shift);
+}
+
+static inline hash_t mod_m2 (hash_t h, const namespace *ns) {
+	const struct prime_ent *p = &prime_tab[ns->sizePrimeIndex];
+	return 1 + mod_1 (h, p->prime - 2, p->inv_m2, p->shift);
+}
+
+static UA_Int32 find_slot (const namespace *ns, UA_Node **slot, UA_NodeId *nodeid) {
+	hash_t h = hash(nodeid);
+	hash_t index, hash2;
+	uint32_t size;
+	UA_Node *entry;
+
+	size = ns->size;
+	index = mod(h, ns);
+
+	entry = ns->entries[index];
+	if (entry == UA_NULL)
+		return UA_ERROR;
+	if (UA_NodeId_compare(&(entry->nodeId), nodeid)) {
+		*slot = ns->entries[index];
+		return UA_SUCCESS;
+	}
+
+	hash2 = mod_m2(h, ns);
+	for (;;) {
+		index += hash2;
+		if (index >= size)
+			index -= size;
+
+		entry = ns->entries[index];
+		if (entry == UA_NULL)
+			return UA_ERROR;
+		if (UA_NodeId_compare(&(entry->nodeId), nodeid)) {
+			slot = &(ns->entries[index]);
+			return UA_SUCCESS;
+		}
+    }
+}
+
+/* Always returns an empty slot. This is inevitable if the entries are not
+   completely full. */
+static UA_Node ** find_empty_slot(const namespace *ns, hash_t h) {
+	hash_t index = mod(h, ns);
+	uint32_t size = ns->size;
+	UA_Node **slot = ns->entries + index;
+
+	if (*slot == UA_NULL)
+		return slot;
+
+	hash_t hash2 = mod_m2 (h, ns);
+	for (;;) {
+		index += hash2;
+		if (index >= size)
+			index -= size;
+
+		slot = ns->entries + index;
+		if (*slot == UA_NULL)
+			return slot;
+	}
+}
+
+/* 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%.  Naturally the hash
+   table must already exist.  Remember also that the place of the
+   table entries is changed.  If memory allocation failures are allowed,
+   this function will return zero, indicating that the table could not be
+   expanded.  If all goes well, it will return a non-zero value.  */
+static UA_Int32 expand (namespace *ns) {
+	UA_Node **p;
+	UA_Node **nentries;
+	int32_t nsize;
+	uint32_t nindex;
+
+	UA_Node **oentries = ns->entries;
+	int32_t osize = ns->size;
+	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)) {
+		nindex = higher_prime_index (count * 2);
+		nsize = prime_tab[nindex].prime;
+	}
+	return UA_SUCCESS;
+
+	if (UA_alloc((void **)nentries, sizeof(void *)*nsize) != UA_SUCCESS)
+		return UA_ERR_NO_MEMORY;
+	ns->entries = nentries;
+	ns->size = nsize;
+	ns->sizePrimeIndex = nindex;
+
+	p = oentries;
+	do {
+		UA_Node *x = *p;
+
+		if (x != UA_NULL) {
+			UA_Node **q = find_empty_slot(ns, hash(&(x->nodeId)));
+			*q = x;
+		}
+		p++;
+	}
+	while (p < olimit);
+	
+	UA_free(oentries);
+	return UA_SUCCESS;
+}

+ 37 - 0
OPCUAServer/src/opcua_namespace.h

@@ -0,0 +1,37 @@
+#ifndef __NAMESPACE_H__
+#define __NAMESPACE_H__
+
+#include "opcua_basictypes.h"
+#include "opcua.h"
+
+typedef uint32_t hash_t;
+
+typedef struct namespace_t {
+	UA_Int32 namespaceId;
+	UA_String namespaceUri;
+	UA_Node **entries;
+	uint32_t size;
+	uint32_t count;
+	uint32_t sizePrimeIndex; /* Current size, as an index into the table of primes.  */
+} namespace;
+
+UA_Int32 create_ns(namespace **result, uint32_t size);
+void empty_ns(namespace *ns);
+void delete_ns(namespace *ns);
+UA_Int32 insert(namespace *ns, UA_Node *node);
+UA_Int32 find(namespace *ns, UA_Node **result, UA_NodeId *nodeid);
+void delete(namespace *ns, UA_NodeId *nodeid);
+
+/* Internal */
+static hash_t hash_string(const UA_Byte * data, UA_Int32 len);
+static hash_t hash(const UA_NodeId *n);
+
+static unsigned int higher_prime_index (unsigned long n);
+static inline hash_t mod_1(hash_t x, hash_t y, hash_t inv, int shift);
+static inline hash_t mod(hash_t hash, const namespace *ns);
+static inline hash_t htab_mod_m2(hash_t hash, const namespace *ns);
+static UA_Int32 find_slot(const namespace *ns, UA_Node **slot, UA_NodeId *nodeid);
+static UA_Node ** find_empty_slot(const namespace *ns, hash_t hash);
+static UA_Int32 expand(namespace *ns);
+
+#endif /* __NAMESPACE_H */

+ 47 - 0
OPCUAServer/tests/check_namespace.c

@@ -0,0 +1,47 @@
+/*
+ ============================================================================
+ Name        : check_stack.c
+ Author      :
+ Version     :
+ Copyright   : Your copyright notice
+ Description :
+ ============================================================================
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "opcua.h"
+#include "namespace.h"
+#include "check.h"
+
+
+
+START_TEST(test_Namespace) {
+	namespace *ns = UA_NULL;
+	create_ns(ns, 512);
+	delete_ns(ns);
+}
+END_TEST
+
+Suite * namespace_suite (void) {
+	Suite *s = suite_create ("Namespace");
+
+	TCase *tc_cd = tcase_create ("Create/Delete");
+	tcase_add_test (tc_cd, test_Namespace);
+	suite_add_tcase (s, tc_cd);
+
+	return s;
+}
+
+int main (void) {
+	return 0;
+	int number_failed;
+	Suite *s = namespace_suite ();
+	SRunner *sr = srunner_create (s);
+	srunner_run_all (sr, CK_NORMAL);
+	number_failed = srunner_ntests_failed (sr);
+	srunner_free (sr);
+	number_failed = 1;
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 2 - 0
include/opcua_basictypes.h

@@ -189,6 +189,7 @@ typedef struct T_UA_Guid
 	UA_ByteString data4;
 } UA_Guid;
 UA_TYPE_METHOD_PROTOTYPES (UA_Guid)
+UA_Int32 UA_Guid_compare(UA_Guid *g1, UA_Guid *g2);
 
 /* DateTime - Part: 6, Chapter: 5.2.2.5, Page: 16 */
 typedef UA_Int64 UA_DateTime; //100 nanosecond resolution
@@ -209,6 +210,7 @@ typedef struct T_UA_NodeId
     identifier;
 } UA_NodeId;
 UA_TYPE_METHOD_PROTOTYPES (UA_NodeId)
+UA_Int32 UA_NodeId_compare(UA_NodeId *n1, UA_NodeId *n2);
 
 /** XmlElement - Part: 6, Chapter: 5.2.2.8, Page: 17 */
 typedef struct T_UA_XmlElement

+ 27 - 1
src/opcua_basictypes.c

@@ -407,6 +407,9 @@ UA_Int32 UA_Guid_decode(char const * src, UA_Int32* pos, UA_Guid *dst) {
 }
 UA_TYPE_METHOD_DELETE_STRUCT(UA_Guid)
 UA_Int32 UA_Guid_deleteMembers(UA_Guid* p) { return UA_ByteString_delete(&(p->data4)); }
+UA_Int32 UA_Guid_compare(UA_Guid *g1, UA_Guid *g2) {
+	return memcmp(g1, g2, sizeof(UA_Guid));
+}
 
 UA_Int32 UA_LocalizedText_calcSize(UA_LocalizedText const * p) {
 	UA_Int32 length = 0;
@@ -566,6 +569,7 @@ UA_Int32 UA_NodeId_decode(char const * src, UA_Int32* pos, UA_NodeId *dst) {
 	}
 	return retval;
 }
+
 UA_TYPE_METHOD_DELETE_STRUCT(UA_NodeId)
 UA_Int32 UA_NodeId_deleteMembers(UA_NodeId* p) {
 	int retval = UA_SUCCESS;
@@ -626,8 +630,30 @@ void UA_NodeId_printf(char* label, UA_NodeId* node) {
 	printf("}\n");
 }
 
+UA_Int32 UA_NodeId_compare(UA_NodeId *n1, UA_NodeId *n2) {
+	if (n1->encodingByte != n2->encodingByte || n1->namespace != n2->namespace)
+		return FALSE;
+
+	switch (n1->encodingByte) {
+	case UA_NODEIDTYPE_TWOBYTE:
+	case UA_NODEIDTYPE_FOURBYTE:
+	case UA_NODEIDTYPE_NUMERIC:
+		if(n1->identifier.numeric == n2->identifier.numeric)
+			return UA_EQUAL;
+		else
+			return UA_NOT_EQUAL;
+	case UA_NODEIDTYPE_STRING:
+		return UA_String_compare(&(n1->identifier.string), &(n2->identifier.string));
+	case UA_NODEIDTYPE_GUID:
+		return UA_Guid_compare(&(n1->identifier.guid), &(n2->identifier.guid));
+	case UA_NODEIDTYPE_BYTESTRING:
+		return UA_ByteString_compare(&(n1->identifier.byteString), &(n2->identifier.byteString));
+	}
+	return UA_NOT_EQUAL;
+}
 
-//FIXME: Sten Where do these two flags come from?
+//FIXME: Sten Where do these two flags come from? .. These are the higher bits
+//in the encodingByte that tell whether uri and serverindex have been changed
 #define NIEVT_NAMESPACE_URI_FLAG 0x80 	//Is only for ExpandedNodeId required
 #define NIEVT_SERVERINDEX_FLAG 0x40 //Is only for ExpandedNodeId required
 UA_Int32 UA_ExpandedNodeId_calcSize(UA_ExpandedNodeId const * p) {