|
@@ -17,6 +17,20 @@
|
|
|
/* There is no UA_Node_new() method here. Creating nodes is part of the
|
|
|
* NodeStore layer */
|
|
|
|
|
|
+static enum ZIP_CMP
|
|
|
+cmpRefTarget(const void *a, const void *b) {
|
|
|
+ const UA_ReferenceTarget *aa = (const UA_ReferenceTarget*)a;
|
|
|
+ const UA_ReferenceTarget *bb = (const UA_ReferenceTarget*)b;
|
|
|
+ if(aa->targetHash < bb->targetHash)
|
|
|
+ return ZIP_CMP_LESS;
|
|
|
+ if(aa->targetHash > bb->targetHash)
|
|
|
+ return ZIP_CMP_MORE;
|
|
|
+ return (enum ZIP_CMP)UA_ExpandedNodeId_order(&aa->target, &bb->target);
|
|
|
+}
|
|
|
+
|
|
|
+ZIP_IMPL(UA_ReferenceTargetHead, UA_ReferenceTarget, zipfields,
|
|
|
+ UA_ReferenceTarget, zipfields, cmpRefTarget)
|
|
|
+
|
|
|
void UA_Node_deleteMembers(UA_Node *node) {
|
|
|
/* Delete standard content */
|
|
|
UA_NodeId_deleteMembers(&node->nodeId);
|
|
@@ -176,16 +190,39 @@ UA_Node_copy(const UA_Node *src, UA_Node *dst) {
|
|
|
UA_NodeReferenceKind *srefs = &src->references[i];
|
|
|
UA_NodeReferenceKind *drefs = &dst->references[i];
|
|
|
drefs->isInverse = srefs->isInverse;
|
|
|
+ ZIP_INIT(&drefs->refTargetsTree);
|
|
|
retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId);
|
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
|
break;
|
|
|
- retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize,
|
|
|
- (void**)&drefs->targetIds,
|
|
|
- &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
|
+ drefs->refTargets = (UA_ReferenceTarget*)
|
|
|
+ UA_malloc(srefs->refTargetsSize* sizeof(UA_ReferenceTarget));
|
|
|
+ if(!drefs->refTargets) {
|
|
|
+ UA_NodeId_deleteMembers(&drefs->referenceTypeId);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ uintptr_t arraydiff = (uintptr_t)drefs->refTargets - (uintptr_t)srefs->refTargets;
|
|
|
+ for(size_t j = 0; j < srefs->refTargetsSize; j++) {
|
|
|
+ retval |= UA_ExpandedNodeId_copy(&srefs->refTargets[j].target,
|
|
|
+ &drefs->refTargets[j].target);
|
|
|
+ drefs->refTargets[j].targetHash = srefs->refTargets[j].targetHash;
|
|
|
+ drefs->refTargets[j].zipfields.zip_right = NULL;
|
|
|
+ if(srefs->refTargets[j].zipfields.zip_right)
|
|
|
+ *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left =
|
|
|
+ (uintptr_t)srefs->refTargets[j].zipfields.zip_right + arraydiff;
|
|
|
+ drefs->refTargets[j].zipfields.zip_left = NULL;
|
|
|
+ if(srefs->refTargets[j].zipfields.zip_left)
|
|
|
+ *(uintptr_t*)&drefs->refTargets[j].zipfields.zip_left =
|
|
|
+ (uintptr_t)srefs->refTargets[j].zipfields.zip_left + arraydiff;
|
|
|
+ }
|
|
|
+ srefs->refTargetsTree.zip_root = NULL;
|
|
|
+ if(drefs->refTargetsTree.zip_root)
|
|
|
+ *(uintptr_t*)&drefs->refTargetsTree.zip_root =
|
|
|
+ (uintptr_t)srefs->refTargetsTree.zip_root + arraydiff;
|
|
|
+ drefs->refTargetsSize= srefs->refTargetsSize;
|
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
|
break;
|
|
|
- drefs->targetIdsSize = srefs->targetIdsSize;
|
|
|
}
|
|
|
+
|
|
|
if(retval != UA_STATUSCODE_GOOD) {
|
|
|
UA_Node_deleteMembers(dst);
|
|
|
return retval;
|
|
@@ -453,75 +490,100 @@ UA_Node_setAttributes(UA_Node *node, const void *attributes,
|
|
|
/*********************/
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target) {
|
|
|
- UA_ExpandedNodeId *targets =
|
|
|
- (UA_ExpandedNodeId*) UA_realloc(refs->targetIds,
|
|
|
- sizeof(UA_ExpandedNodeId) * (refs->targetIdsSize+1));
|
|
|
+addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target,
|
|
|
+ UA_UInt32 targetHash) {
|
|
|
+ UA_ReferenceTarget *targets = (UA_ReferenceTarget*)
|
|
|
+ UA_realloc(refs->refTargets, (refs->refTargetsSize + 1) * sizeof(UA_ReferenceTarget));
|
|
|
if(!targets)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
|
|
- refs->targetIds = targets;
|
|
|
- UA_StatusCode retval =
|
|
|
- UA_ExpandedNodeId_copy(target, &refs->targetIds[refs->targetIdsSize]);
|
|
|
-
|
|
|
- if(retval == UA_STATUSCODE_GOOD) {
|
|
|
- refs->targetIdsSize++;
|
|
|
- } else if(refs->targetIdsSize == 0) {
|
|
|
- /* We had zero references before (realloc was a malloc) */
|
|
|
- UA_free(refs->targetIds);
|
|
|
- refs->targetIds = NULL;
|
|
|
+ /* Repair the pointers in the tree for the realloced array */
|
|
|
+ uintptr_t arraydiff = (uintptr_t)targets - (uintptr_t)refs->refTargets;
|
|
|
+ if(arraydiff != 0) {
|
|
|
+ for(size_t i = 0; i < refs->refTargetsSize; i++) {
|
|
|
+ if(targets[i].zipfields.zip_left)
|
|
|
+ *(uintptr_t*)&targets[i].zipfields.zip_left += arraydiff;
|
|
|
+ if(targets[i].zipfields.zip_right)
|
|
|
+ *(uintptr_t*)&targets[i].zipfields.zip_right += arraydiff;
|
|
|
+ }
|
|
|
}
|
|
|
- return retval;
|
|
|
+
|
|
|
+ if(refs->refTargetsTree.zip_root)
|
|
|
+ *(uintptr_t*)&refs->refTargetsTree.zip_root += arraydiff;
|
|
|
+ refs->refTargets = targets;
|
|
|
+
|
|
|
+ UA_ReferenceTarget *entry = &refs->refTargets[refs->refTargetsSize];
|
|
|
+ UA_StatusCode retval = UA_ExpandedNodeId_copy(target, &entry->target);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
+ if(refs->refTargetsSize== 0) {
|
|
|
+ /* We had zero references before (realloc was a malloc) */
|
|
|
+ UA_free(refs->refTargets);
|
|
|
+ refs->refTargets = NULL;
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ entry->targetHash = targetHash;
|
|
|
+ ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree,
|
|
|
+ entry, ZIP_FFS32(UA_UInt32_random()));
|
|
|
+ refs->refTargetsSize++;
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) {
|
|
|
- UA_NodeReferenceKind *refs =
|
|
|
- (UA_NodeReferenceKind*)UA_realloc(node->references,
|
|
|
- sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
|
|
|
+ UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)
|
|
|
+ UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * (node->referencesSize+1));
|
|
|
if(!refs)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
node->references = refs;
|
|
|
UA_NodeReferenceKind *newRef = &refs[node->referencesSize];
|
|
|
memset(newRef, 0, sizeof(UA_NodeReferenceKind));
|
|
|
|
|
|
+ ZIP_INIT(&newRef->refTargetsTree);
|
|
|
newRef->isInverse = !item->isForward;
|
|
|
UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &newRef->referenceTypeId);
|
|
|
- retval |= addReferenceTarget(newRef, &item->targetNodeId);
|
|
|
+ UA_UInt32 targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId);
|
|
|
+ retval |= addReferenceTarget(newRef, &item->targetNodeId, targetHash);
|
|
|
|
|
|
- if(retval == UA_STATUSCODE_GOOD) {
|
|
|
- node->referencesSize++;
|
|
|
- } else {
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
UA_NodeId_deleteMembers(&newRef->referenceTypeId);
|
|
|
if(node->referencesSize == 0) {
|
|
|
UA_free(node->references);
|
|
|
node->references = NULL;
|
|
|
}
|
|
|
+ return retval;
|
|
|
}
|
|
|
- return retval;
|
|
|
+
|
|
|
+ node->referencesSize++;
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
UA_StatusCode
|
|
|
UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) {
|
|
|
+ /* Find the matching refkind */
|
|
|
UA_NodeReferenceKind *existingRefs = NULL;
|
|
|
for(size_t i = 0; i < node->referencesSize; ++i) {
|
|
|
UA_NodeReferenceKind *refs = &node->references[i];
|
|
|
- if(refs->isInverse != item->isForward
|
|
|
- && UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) {
|
|
|
+ if(refs->isInverse != item->isForward &&
|
|
|
+ UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) {
|
|
|
existingRefs = refs;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- if(existingRefs != NULL) {
|
|
|
- for(size_t i = 0; i < existingRefs->targetIdsSize; i++) {
|
|
|
- if(UA_ExpandedNodeId_equal(&existingRefs->targetIds[i],
|
|
|
- &item->targetNodeId)) {
|
|
|
- return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED;
|
|
|
- }
|
|
|
- }
|
|
|
- return addReferenceTarget(existingRefs, &item->targetNodeId);
|
|
|
- }
|
|
|
- return addReferenceKind(node, item);
|
|
|
+
|
|
|
+ if(!existingRefs)
|
|
|
+ return addReferenceKind(node, item);
|
|
|
+
|
|
|
+ UA_ReferenceTarget tmpTarget;
|
|
|
+ tmpTarget.target = item->targetNodeId;
|
|
|
+ tmpTarget.targetHash = UA_ExpandedNodeId_hash(&item->targetNodeId);
|
|
|
+
|
|
|
+ UA_ReferenceTarget *found =
|
|
|
+ ZIP_FIND(UA_ReferenceTargetHead, &existingRefs->refTargetsTree, &tmpTarget);
|
|
|
+ if(found)
|
|
|
+ return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED;
|
|
|
+ return addReferenceTarget(existingRefs, &item->targetNodeId, tmpTarget.targetHash);
|
|
|
}
|
|
|
|
|
|
UA_StatusCode
|
|
@@ -533,33 +595,40 @@ UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item) {
|
|
|
if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId))
|
|
|
continue;
|
|
|
|
|
|
- for(size_t j = refs->targetIdsSize; j > 0; --j) {
|
|
|
- UA_ExpandedNodeId *target = &refs->targetIds[j-1];
|
|
|
- if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->nodeId))
|
|
|
+ for(size_t j = refs->refTargetsSize; j > 0; --j) {
|
|
|
+ UA_ReferenceTarget *target = &refs->refTargets[j-1];
|
|
|
+ if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->target.nodeId))
|
|
|
continue;
|
|
|
|
|
|
/* Ok, delete the reference */
|
|
|
- UA_ExpandedNodeId_deleteMembers(target);
|
|
|
- refs->targetIdsSize--;
|
|
|
+ ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree, target);
|
|
|
+ UA_ExpandedNodeId_deleteMembers(&target->target);
|
|
|
+ refs->refTargetsSize--;
|
|
|
|
|
|
/* One matching target remaining */
|
|
|
- if(refs->targetIdsSize > 0) {
|
|
|
- if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
|
|
|
- // and destination overlap in
|
|
|
- // memcpy
|
|
|
- *target = refs->targetIds[refs->targetIdsSize];
|
|
|
+ if(refs->refTargetsSize > 0) {
|
|
|
+ if(j-1 != refs->refTargetsSize) {
|
|
|
+ /* avoid valgrind error: Source and destination overlap in
|
|
|
+ * memcpy */
|
|
|
+ ZIP_REMOVE(UA_ReferenceTargetHead, &refs->refTargetsTree,
|
|
|
+ &refs->refTargets[refs->refTargetsSize]);
|
|
|
+ *target = refs->refTargets[refs->refTargetsSize];
|
|
|
+ ZIP_INSERT(UA_ReferenceTargetHead, &refs->refTargetsTree,
|
|
|
+ target, ZIP_RANK(target, zipfields));
|
|
|
+ }
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
/* No target for the ReferenceType remaining. Remove entry. */
|
|
|
- UA_free(refs->targetIds);
|
|
|
+ UA_free(refs->refTargets);
|
|
|
UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
|
|
node->referencesSize--;
|
|
|
if(node->referencesSize > 0) {
|
|
|
- if(i-1 != node->referencesSize) // avoid valgrind error: Source
|
|
|
- // and destination overlap in
|
|
|
- // memcpy
|
|
|
+ if(i-1 != node->referencesSize) {
|
|
|
+ /* avoid valgrind error: Source and destination overlap in
|
|
|
+ * memcpy */
|
|
|
node->references[i-1] = node->references[node->referencesSize];
|
|
|
+ }
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
@@ -594,7 +663,9 @@ UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize,
|
|
|
continue;
|
|
|
|
|
|
/* Remove references */
|
|
|
- UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
|
|
|
+ for(size_t j = 0; j < refs->refTargetsSize; j++)
|
|
|
+ UA_ExpandedNodeId_deleteMembers(&refs->refTargets[j].target);
|
|
|
+ UA_free(refs->refTargets);
|
|
|
UA_NodeId_deleteMembers(&refs->referenceTypeId);
|
|
|
node->referencesSize--;
|
|
|
|
|
@@ -604,17 +675,18 @@ UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize,
|
|
|
node->references[i-1] = node->references[node->referencesSize];
|
|
|
}
|
|
|
|
|
|
- if(node->referencesSize == 0) {
|
|
|
- /* The array is empty. Remove. */
|
|
|
- UA_free(node->references);
|
|
|
- node->references = NULL;
|
|
|
- } else {
|
|
|
+ if(node->referencesSize > 0) {
|
|
|
/* Realloc to save memory */
|
|
|
UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)
|
|
|
UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * node->referencesSize);
|
|
|
if(refs) /* Do nothing if realloc fails */
|
|
|
node->references = refs;
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ /* The array is empty. Remove. */
|
|
|
+ UA_free(node->references);
|
|
|
+ node->references = NULL;
|
|
|
}
|
|
|
|
|
|
void UA_Node_deleteReferences(UA_Node *node) {
|