/src/open62541_15/src/server/ua_nodes.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright 2015-2018, 2021 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2015-2016 (c) Sten GrĂ¼ner |
7 | | * Copyright 2015 (c) Chris Iatrou |
8 | | * Copyright 2015, 2017 (c) Florian Palm |
9 | | * Copyright 2015 (c) Oleksiy Vasylyev |
10 | | * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH |
11 | | * Copyright 2017 (c) Julian Grothoff |
12 | | */ |
13 | | |
14 | | #include "ua_server_internal.h" |
15 | | #include "../ua_types_encoding_binary.h" |
16 | | |
17 | | /*********************/ |
18 | | /* ReferenceType Set */ |
19 | | /*********************/ |
20 | | |
21 | | #define UA_REFTYPES_ALL_MASK (~(UA_UInt32)0) |
22 | | #define UA_REFTYPES_ALL_MASK2 UA_REFTYPES_ALL_MASK, UA_REFTYPES_ALL_MASK |
23 | | #define UA_REFTYPES_ALL_MASK4 UA_REFTYPES_ALL_MASK2, UA_REFTYPES_ALL_MASK2 |
24 | | #if UA_REFERENCETYPESET_MAX / 32 > 8 |
25 | | # error Adjust macros to support than 256 reference types |
26 | | #elif UA_REFERENCETYPESET_MAX / 32 == 8 |
27 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK4, UA_REFTYPES_ALL_MASK4 |
28 | | #elif UA_REFERENCETYPESET_MAX / 32 == 7 |
29 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK4, UA_REFTYPES_ALL_MASK2, UA_REFTYPES_ALL_MASK |
30 | | #elif UA_REFERENCETYPESET_MAX / 32 == 6 |
31 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK4, UA_REFTYPES_ALL_MASK2 |
32 | | #elif UA_REFERENCETYPESET_MAX / 32 == 5 |
33 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK4, UA_REFTYPES_ALL_MASK |
34 | | #elif UA_REFERENCETYPESET_MAX / 32 == 4 |
35 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK4 |
36 | | #elif UA_REFERENCETYPESET_MAX / 32 == 3 |
37 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK2, UA_REFTYPES_ALL_MASK |
38 | | #elif UA_REFERENCETYPESET_MAX / 32 == 2 |
39 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK2 |
40 | | #else |
41 | | # define UA_REFTYPES_ALL_ARRAY UA_REFTYPES_ALL_MASK |
42 | | #endif |
43 | | |
44 | | const UA_ReferenceTypeSet UA_REFERENCETYPESET_NONE = {{0}}; |
45 | | const UA_ReferenceTypeSet UA_REFERENCETYPESET_ALL = {{UA_REFTYPES_ALL_ARRAY}}; |
46 | | |
47 | | /*****************/ |
48 | | /* Node Pointers */ |
49 | | /*****************/ |
50 | | |
51 | 3.60G | #define UA_NODEPOINTER_MASK 0x03 |
52 | 465M | #define UA_NODEPOINTER_TAG_IMMEDIATE 0x00 |
53 | 1.18M | #define UA_NODEPOINTER_TAG_NODEID 0x01 |
54 | 771M | #define UA_NODEPOINTER_TAG_EXPANDEDNODEID 0x02 |
55 | 1.90G | #define UA_NODEPOINTER_TAG_NODE 0x03 |
56 | | |
57 | | void |
58 | 79.1M | UA_NodePointer_clear(UA_NodePointer *np) { |
59 | 79.1M | switch(np->immediate & UA_NODEPOINTER_MASK) { |
60 | 42.5k | case UA_NODEPOINTER_TAG_NODEID: |
61 | 42.5k | np->immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
62 | 42.5k | UA_NodeId_delete((UA_NodeId*)(uintptr_t)np->id); |
63 | 42.5k | break; |
64 | 1 | case UA_NODEPOINTER_TAG_EXPANDEDNODEID: |
65 | 1 | np->immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
66 | 1 | UA_ExpandedNodeId_delete((UA_ExpandedNodeId*)(uintptr_t) |
67 | 1 | np->expandedId); |
68 | 1 | break; |
69 | 79.1M | default: |
70 | 79.1M | break; |
71 | 79.1M | } |
72 | 79.1M | UA_NodePointer_init(np); |
73 | 79.1M | } |
74 | | |
75 | | UA_StatusCode |
76 | 79.1M | UA_NodePointer_copy(UA_NodePointer in, UA_NodePointer *out) { |
77 | 79.1M | UA_StatusCode res = UA_STATUSCODE_GOOD; |
78 | 79.1M | UA_Byte tag = in.immediate & UA_NODEPOINTER_MASK; |
79 | 79.1M | in.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
80 | 79.1M | switch(tag) { |
81 | 0 | case UA_NODEPOINTER_TAG_NODE: |
82 | 0 | in.id = &in.node->nodeId; |
83 | 0 | goto nodeid; /* fallthrough */ |
84 | 42.5k | case UA_NODEPOINTER_TAG_NODEID: |
85 | 42.5k | nodeid: |
86 | 42.5k | out->id = UA_NodeId_new(); |
87 | 42.5k | if(!out->id) |
88 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
89 | 42.5k | res = UA_NodeId_copy(in.id, (UA_NodeId*)(uintptr_t)out->id); |
90 | 42.5k | if(res != UA_STATUSCODE_GOOD) { |
91 | 0 | UA_free((void*)out->immediate); |
92 | 0 | out->immediate = 0; |
93 | 0 | break; |
94 | 0 | } |
95 | 42.5k | out->immediate |= UA_NODEPOINTER_TAG_NODEID; |
96 | 42.5k | break; |
97 | 1 | case UA_NODEPOINTER_TAG_EXPANDEDNODEID: |
98 | 1 | out->expandedId = UA_ExpandedNodeId_new(); |
99 | 1 | if(!out->expandedId) |
100 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
101 | 1 | res = UA_ExpandedNodeId_copy(in.expandedId, |
102 | 1 | (UA_ExpandedNodeId*)(uintptr_t) |
103 | 1 | out->expandedId); |
104 | 1 | if(res != UA_STATUSCODE_GOOD) { |
105 | 0 | UA_free((void*)out->immediate); |
106 | 0 | out->immediate = 0; |
107 | 0 | break; |
108 | 0 | } |
109 | 1 | out->immediate |= UA_NODEPOINTER_TAG_EXPANDEDNODEID; |
110 | 1 | break; |
111 | 0 | default: |
112 | 79.1M | case UA_NODEPOINTER_TAG_IMMEDIATE: |
113 | 79.1M | *out = in; |
114 | 79.1M | break; |
115 | 79.1M | } |
116 | 79.1M | return res; |
117 | 79.1M | } |
118 | | |
119 | | UA_Boolean |
120 | 694M | UA_NodePointer_isLocal(UA_NodePointer np) { |
121 | 694M | UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; |
122 | 694M | return (tag != UA_NODEPOINTER_TAG_EXPANDEDNODEID); |
123 | 694M | } |
124 | | |
125 | | UA_Order |
126 | 937M | UA_NodePointer_order(UA_NodePointer p1, UA_NodePointer p2) { |
127 | 937M | if(p1.immediate == p2.immediate) |
128 | 24.8M | return UA_ORDER_EQ; |
129 | | |
130 | | /* Extract the tag and resolve pointers to nodes */ |
131 | 912M | UA_Byte tag1 = p1.immediate & UA_NODEPOINTER_MASK; |
132 | 912M | if(tag1 == UA_NODEPOINTER_TAG_NODE) { |
133 | 0 | p1 = UA_NodePointer_fromNodeId(&p1.node->nodeId); |
134 | 0 | tag1 = p1.immediate & UA_NODEPOINTER_MASK; |
135 | 0 | } |
136 | 912M | UA_Byte tag2 = p2.immediate & UA_NODEPOINTER_MASK; |
137 | 912M | if(tag2 == UA_NODEPOINTER_TAG_NODE) { |
138 | 0 | p2 = UA_NodePointer_fromNodeId(&p2.node->nodeId); |
139 | 0 | tag2 = p2.immediate & UA_NODEPOINTER_MASK; |
140 | 0 | } |
141 | | |
142 | | /* Different tags, cannot be identical */ |
143 | 912M | if(tag1 != tag2) |
144 | 6.98M | return (tag1 > tag2) ? UA_ORDER_MORE : UA_ORDER_LESS; |
145 | | |
146 | | /* Immediate */ |
147 | 905M | if(UA_LIKELY(tag1 == UA_NODEPOINTER_TAG_IMMEDIATE)) |
148 | 905M | return (p1.immediate > p2.immediate) ? |
149 | 714M | UA_ORDER_MORE : UA_ORDER_LESS; |
150 | | |
151 | | /* Compare from pointers */ |
152 | 88.1k | p1.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
153 | 88.1k | p2.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
154 | 88.1k | if(tag1 == UA_NODEPOINTER_TAG_EXPANDEDNODEID) |
155 | 0 | return UA_ExpandedNodeId_order(p1.expandedId, p2.expandedId); |
156 | 88.1k | return UA_NodeId_order(p1.id, p2.id); |
157 | 88.1k | } |
158 | | |
159 | | UA_NodePointer |
160 | 272M | UA_NodePointer_fromNodeId(const UA_NodeId *id) { |
161 | 272M | UA_NodePointer np; |
162 | 272M | if(id->identifierType != UA_NODEIDTYPE_NUMERIC) { |
163 | 113k | np.id = id; |
164 | 113k | np.immediate |= UA_NODEPOINTER_TAG_NODEID; |
165 | 113k | return np; |
166 | 113k | } |
167 | | |
168 | 272M | #if SIZE_MAX > UA_UINT32_MAX |
169 | | /* 64bit: 4 Byte for the numeric identifier + 2 Byte for the namespaceIndex |
170 | | * + 1 Byte for the tagging bit (zero) */ |
171 | 272M | np.immediate = ((uintptr_t)id->identifier.numeric) << 32; |
172 | 272M | np.immediate |= ((uintptr_t)id->namespaceIndex) << 8; |
173 | | #else |
174 | | /* 32bit: 3 Byte for the numeric identifier + 6 Bit for the namespaceIndex |
175 | | * + 2 Bit for the tagging bit (zero) */ |
176 | | if(id->namespaceIndex < (0x01 << 6) && |
177 | | id->identifier.numeric < (0x01 << 24)) { |
178 | | np.immediate = ((uintptr_t)id->identifier.numeric) << 8; |
179 | | np.immediate |= ((uintptr_t)id->namespaceIndex) << 2; |
180 | | } else { |
181 | | np.id = id; |
182 | | np.immediate |= UA_NODEPOINTER_TAG_NODEID; |
183 | | } |
184 | | #endif |
185 | 272M | return np; |
186 | 272M | } |
187 | | |
188 | | UA_NodeId |
189 | 387M | UA_NodePointer_toNodeId(UA_NodePointer np) { |
190 | 387M | UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; |
191 | 387M | np.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
192 | 387M | switch(tag) { |
193 | 0 | case UA_NODEPOINTER_TAG_NODE: |
194 | 0 | return np.node->nodeId; |
195 | 944k | case UA_NODEPOINTER_TAG_NODEID: |
196 | 944k | return *np.id; |
197 | 0 | case UA_NODEPOINTER_TAG_EXPANDEDNODEID: |
198 | 0 | return np.expandedId->nodeId; |
199 | 0 | default: |
200 | 386M | case UA_NODEPOINTER_TAG_IMMEDIATE: |
201 | 386M | break; |
202 | 387M | } |
203 | | |
204 | 386M | UA_NodeId id; |
205 | 386M | id.identifierType = UA_NODEIDTYPE_NUMERIC; |
206 | | #if SIZE_MAX > UA_UINT32_MAX /* 64bit */ |
207 | 386M | id.namespaceIndex = (UA_UInt16)(np.immediate >> 8); |
208 | 386M | id.identifier.numeric = (UA_UInt32)(np.immediate >> 32); |
209 | | #else /* 32bit */ |
210 | | id.namespaceIndex = ((UA_Byte)np.immediate) >> 2; |
211 | | id.identifier.numeric = np.immediate >> 8; |
212 | | #endif |
213 | 386M | return id; |
214 | 387M | } |
215 | | |
216 | | UA_NodePointer |
217 | 177M | UA_NodePointer_fromExpandedNodeId(const UA_ExpandedNodeId *id) { |
218 | 177M | if(!UA_ExpandedNodeId_isLocal(id)) { |
219 | 1 | UA_NodePointer np; |
220 | 1 | np.expandedId = id; |
221 | 1 | np.immediate |= UA_NODEPOINTER_TAG_EXPANDEDNODEID; |
222 | 1 | return np; |
223 | 1 | } |
224 | 177M | return UA_NodePointer_fromNodeId(&id->nodeId); |
225 | 177M | } |
226 | | |
227 | | UA_ExpandedNodeId |
228 | 76.4M | UA_NodePointer_toExpandedNodeId(UA_NodePointer np) { |
229 | | /* Resolve node pointer to get the NodeId */ |
230 | 76.4M | UA_Byte tag = np.immediate & UA_NODEPOINTER_MASK; |
231 | 76.4M | if(tag == UA_NODEPOINTER_TAG_NODE) { |
232 | 0 | np = UA_NodePointer_fromNodeId(&np.node->nodeId); |
233 | 0 | tag = np.immediate & UA_NODEPOINTER_MASK; |
234 | 0 | } |
235 | | |
236 | | /* ExpandedNodeId, make a shallow copy */ |
237 | 76.4M | if(tag == UA_NODEPOINTER_TAG_EXPANDEDNODEID) { |
238 | 0 | np.immediate &= ~(uintptr_t)UA_NODEPOINTER_MASK; |
239 | 0 | return *np.expandedId; |
240 | 0 | } |
241 | | |
242 | | /* NodeId, either immediate or via a pointer */ |
243 | 76.4M | UA_ExpandedNodeId en; |
244 | 76.4M | UA_ExpandedNodeId_init(&en); |
245 | 76.4M | en.nodeId = UA_NodePointer_toNodeId(np); |
246 | 76.4M | return en; |
247 | 76.4M | } |
248 | | |
249 | | /**************/ |
250 | | /* References */ |
251 | | /**************/ |
252 | | |
253 | | static UA_StatusCode |
254 | | addReferenceTarget(UA_NodeReferenceKind *refs, UA_NodePointer target, |
255 | | UA_UInt32 targetNameHash); |
256 | | |
257 | | static UA_StatusCode |
258 | | addReferenceTargetToTree(UA_NodeReferenceKind *rk, UA_NodePointer targetId, |
259 | | UA_UInt32 targetIdHash, UA_UInt32 targetNameHash); |
260 | | |
261 | | enum ZIP_CMP |
262 | 384M | cmpRefTargetId(const void *a, const void *b) { |
263 | 384M | const UA_ReferenceTargetTreeElem *aa = (const UA_ReferenceTargetTreeElem*)a; |
264 | 384M | const UA_ReferenceTargetTreeElem *bb = (const UA_ReferenceTargetTreeElem*)b; |
265 | 384M | if(aa->targetIdHash < bb->targetIdHash) |
266 | 190M | return ZIP_CMP_LESS; |
267 | 194M | if(aa->targetIdHash > bb->targetIdHash) |
268 | 193M | return ZIP_CMP_MORE; |
269 | 799k | return (enum ZIP_CMP)UA_NodePointer_order(aa->target.targetId, |
270 | 799k | bb->target.targetId); |
271 | 194M | } |
272 | | |
273 | | enum ZIP_CMP |
274 | 196M | cmpRefTargetName(const void *a, const void *b) { |
275 | 196M | const UA_ReferenceTarget *aa = (const UA_ReferenceTarget*)a; |
276 | 196M | const UA_ReferenceTarget *bb = (const UA_ReferenceTarget*)b; |
277 | 196M | if(aa->targetNameHash == bb->targetNameHash) |
278 | 22.1M | return ZIP_CMP_EQ; |
279 | 174M | return (aa->targetNameHash < bb->targetNameHash) ? |
280 | 89.7M | ZIP_CMP_LESS : ZIP_CMP_MORE; |
281 | 196M | } |
282 | | |
283 | | /* Move to the array in-order, also deletes the tree elements */ |
284 | | static void |
285 | | moveTreeToArray(UA_ReferenceTarget *array, size_t *pos, |
286 | 0 | UA_ReferenceTargetTreeElem *elem) { |
287 | 0 | if(!elem) |
288 | 0 | return; |
289 | 0 | moveTreeToArray(array, pos, elem->idTreeEntry.left); |
290 | 0 | array[*pos] = elem->target; |
291 | 0 | (*pos)++; |
292 | 0 | moveTreeToArray(array, pos, elem->idTreeEntry.right); |
293 | 0 | UA_free(elem); |
294 | 0 | } |
295 | | |
296 | | static void * |
297 | 23.6M | removeTreeEntry(void *context, UA_ReferenceTargetTreeElem *elem) { |
298 | 23.6M | (void)context; |
299 | 23.6M | UA_NodePointer_clear(&elem->target.targetId); |
300 | 23.6M | UA_free(elem); |
301 | 23.6M | return NULL; |
302 | 23.6M | } |
303 | | |
304 | | UA_StatusCode |
305 | 222k | UA_NodeReferenceKind_switch(UA_NodeReferenceKind *rk) { |
306 | 222k | UA_assert(rk->targetsSize > 0); |
307 | | |
308 | 222k | if(rk->hasRefTree) { |
309 | | /* From tree to array */ |
310 | 0 | UA_ReferenceTarget *array = (UA_ReferenceTarget*) |
311 | 0 | UA_malloc(sizeof(UA_ReferenceTarget) * rk->targetsSize); |
312 | 0 | if(!array) |
313 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
314 | 0 | size_t pos = 0; |
315 | 0 | moveTreeToArray(array, &pos, rk->targets.tree.idRoot); |
316 | 0 | rk->targets.array = array; |
317 | 0 | rk->hasRefTree = false; |
318 | 0 | return UA_STATUSCODE_GOOD; |
319 | 0 | } |
320 | | |
321 | | /* From array to tree */ |
322 | 222k | UA_NodeReferenceKind newRk = *rk; |
323 | 222k | newRk.hasRefTree = true; |
324 | 222k | newRk.targets.tree.idRoot = NULL; |
325 | 222k | newRk.targets.tree.nameRoot = NULL; |
326 | 222k | newRk.targetsSize = 0; |
327 | 4.00M | for(size_t i = 0; i < rk->targetsSize; i++) { |
328 | 3.78M | UA_assert(newRk.hasRefTree == true); |
329 | 3.78M | UA_StatusCode res = |
330 | 3.78M | addReferenceTarget(&newRk, rk->targets.array[i].targetId, |
331 | 3.78M | rk->targets.array[i].targetNameHash); |
332 | 3.78M | if(res != UA_STATUSCODE_GOOD) { |
333 | 0 | ZIP_ITER(UA_ReferenceIdTree, |
334 | 0 | (UA_ReferenceIdTree*)&newRk.targets.tree.idRoot, |
335 | 0 | removeTreeEntry, NULL); |
336 | 0 | return res; |
337 | 0 | } |
338 | 3.78M | } |
339 | 4.00M | for(size_t i = 0; i < rk->targetsSize; i++) |
340 | 3.78M | UA_NodePointer_clear(&rk->targets.array[i].targetId); |
341 | 222k | UA_free(rk->targets.array); |
342 | 222k | *rk = newRk; |
343 | 222k | return UA_STATUSCODE_GOOD; |
344 | 222k | } |
345 | | |
346 | | void * |
347 | | UA_NodeReferenceKind_iterate(UA_NodeReferenceKind *rk, |
348 | | UA_NodeReferenceKind_iterateCallback callback, |
349 | 210M | void *context) { |
350 | 210M | if(rk->hasRefTree) |
351 | 4.09M | return ZIP_ITER(UA_ReferenceIdTree, |
352 | 210M | (UA_ReferenceIdTree*)&rk->targets.tree.idRoot, |
353 | 210M | (UA_ReferenceIdTree_cb)callback, context); |
354 | 310M | for(size_t i = 0; i < rk->targetsSize; i++) { |
355 | 225M | void *res = callback(context, &rk->targets.array[i]); |
356 | 225M | if(res) |
357 | 121M | return res; |
358 | 225M | } |
359 | 85.2M | return NULL; |
360 | 206M | } |
361 | | |
362 | | const UA_ReferenceTarget * |
363 | | UA_NodeReferenceKind_findTarget(const UA_NodeReferenceKind *rk, |
364 | 35.4M | const UA_ExpandedNodeId *targetId) { |
365 | 35.4M | UA_NodePointer targetP = UA_NodePointer_fromExpandedNodeId(targetId); |
366 | 35.4M | if(rk->hasRefTree) { |
367 | | /* Return from the tree */ |
368 | 21.4M | UA_ReferenceTargetTreeElem tmpTarget; |
369 | 21.4M | tmpTarget.target.targetId = targetP; |
370 | 21.4M | tmpTarget.targetIdHash = UA_ExpandedNodeId_hash(targetId); |
371 | 21.4M | UA_ReferenceTargetTreeElem *result = |
372 | 21.4M | ZIP_FIND(UA_ReferenceIdTree, (UA_ReferenceIdTree*) |
373 | 21.4M | (uintptr_t)&rk->targets.tree.idRoot, &tmpTarget); |
374 | 21.4M | if(result) |
375 | 799k | return &result->target; |
376 | 21.4M | } else { |
377 | | /* Return from the array */ |
378 | 72.0M | for(size_t i = 0; i < rk->targetsSize; i++) { |
379 | 59.2M | if(UA_NodePointer_equal(targetP, rk->targets.array[i].targetId)) |
380 | 1.07M | return &rk->targets.array[i]; |
381 | 59.2M | } |
382 | 13.9M | } |
383 | 33.5M | return NULL; |
384 | 35.4M | } |
385 | | |
386 | | /* General node handling methods. There is no UA_Node_new() method here. |
387 | | * Creating nodes is part of the Nodestore layer */ |
388 | | |
389 | 15.2M | void UA_Node_clear(UA_Node *node) { |
390 | | /* Delete references */ |
391 | 15.2M | UA_Node_deleteReferences(node); |
392 | | |
393 | | /* Delete other head content */ |
394 | 15.2M | UA_NodeHead *head = &node->head; |
395 | 15.2M | UA_NodeId_clear(&head->nodeId); |
396 | 15.2M | UA_QualifiedName_clear(&head->browseName); |
397 | | |
398 | 15.2M | UA_LocalizedTextListEntry *lt; |
399 | | |
400 | 30.4M | while((lt = head->displayName)) { |
401 | 15.2M | head->displayName = lt->next; |
402 | 15.2M | UA_LocalizedText_clear(<->localizedText); |
403 | 15.2M | UA_free(lt); |
404 | 15.2M | } |
405 | | |
406 | 15.4M | while((lt = head->description)) { |
407 | 214k | head->description = lt->next; |
408 | 214k | UA_LocalizedText_clear(<->localizedText); |
409 | 214k | UA_free(lt); |
410 | 214k | } |
411 | | |
412 | | /* Delete unique content of the nodeclass */ |
413 | 15.2M | switch(head->nodeClass) { |
414 | 1.24M | case UA_NODECLASS_OBJECT: |
415 | 1.24M | break; |
416 | 609k | case UA_NODECLASS_METHOD: |
417 | 609k | break; |
418 | 906k | case UA_NODECLASS_OBJECTTYPE: |
419 | 906k | break; |
420 | 10.3M | case UA_NODECLASS_VARIABLE: |
421 | 10.7M | case UA_NODECLASS_VARIABLETYPE: { |
422 | 10.7M | UA_VariableNode *p = &node->variableNode; |
423 | 10.7M | UA_NodeId_clear(&p->dataType); |
424 | 10.7M | UA_Array_delete(p->arrayDimensions, p->arrayDimensionsSize, |
425 | 10.7M | &UA_TYPES[UA_TYPES_INT32]); |
426 | 10.7M | p->arrayDimensions = NULL; |
427 | 10.7M | p->arrayDimensionsSize = 0; |
428 | 10.7M | if(p->valueSourceType == UA_VALUESOURCETYPE_INTERNAL) |
429 | 9.67M | UA_DataValue_clear(&p->valueSource.internal.value); |
430 | 10.7M | break; |
431 | 10.3M | } |
432 | 499k | case UA_NODECLASS_REFERENCETYPE: { |
433 | 499k | UA_ReferenceTypeNode *p = &node->referenceTypeNode; |
434 | 499k | UA_LocalizedText_clear(&p->inverseName); |
435 | 499k | break; |
436 | 10.3M | } |
437 | 1.20M | case UA_NODECLASS_DATATYPE: |
438 | 1.20M | break; |
439 | 1 | case UA_NODECLASS_VIEW: |
440 | 1 | break; |
441 | 0 | default: |
442 | 0 | break; |
443 | 15.2M | } |
444 | 15.2M | } |
445 | | |
446 | | static UA_StatusCode |
447 | 131k | UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) { |
448 | 131k | dst->eventNotifier = src->eventNotifier; |
449 | 131k | return UA_STATUSCODE_GOOD; |
450 | 131k | } |
451 | | |
452 | | static UA_StatusCode |
453 | 737k | UA_CommonVariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { |
454 | 737k | UA_StatusCode retval = |
455 | 737k | UA_Array_copy(src->arrayDimensions, src->arrayDimensionsSize, |
456 | 737k | (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32]); |
457 | 737k | if(retval != UA_STATUSCODE_GOOD) |
458 | 0 | return retval; |
459 | 737k | dst->arrayDimensionsSize = src->arrayDimensionsSize; |
460 | 737k | retval = UA_NodeId_copy(&src->dataType, &dst->dataType); |
461 | 737k | dst->valueRank = src->valueRank; |
462 | 737k | dst->valueSourceType = src->valueSourceType; |
463 | 737k | dst->valueSource = src->valueSource; |
464 | 737k | if(src->valueSourceType == UA_VALUESOURCETYPE_INTERNAL) |
465 | 737k | retval |= UA_DataValue_copy(&src->valueSource.internal.value, |
466 | 737k | &dst->valueSource.internal.value); |
467 | 0 | else if(src->valueSourceType == UA_VALUESOURCETYPE_EXTERNAL) |
468 | 0 | retval |= UA_DataValue_copy(*src->valueSource.external.value, |
469 | 0 | &dst->valueSource.internal.value); |
470 | 737k | return retval; |
471 | 737k | } |
472 | | |
473 | | static UA_StatusCode |
474 | 737k | UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { |
475 | 737k | dst->accessLevel = src->accessLevel; |
476 | 737k | dst->minimumSamplingInterval = src->minimumSamplingInterval; |
477 | 737k | dst->historizing = src->historizing; |
478 | 737k | dst->isDynamic = src->isDynamic; |
479 | 737k | return UA_CommonVariableNode_copy(src, dst); |
480 | 737k | } |
481 | | |
482 | | static UA_StatusCode |
483 | | UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, |
484 | 0 | UA_VariableTypeNode *dst) { |
485 | 0 | dst->isAbstract = src->isAbstract; |
486 | 0 | return UA_CommonVariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); |
487 | 0 | } |
488 | | |
489 | | static UA_StatusCode |
490 | 0 | UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) { |
491 | 0 | dst->executable = src->executable; |
492 | 0 | dst->method = src->method; |
493 | 0 | return UA_STATUSCODE_GOOD; |
494 | 0 | } |
495 | | |
496 | | static UA_StatusCode |
497 | 0 | UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) { |
498 | 0 | dst->isAbstract = src->isAbstract; |
499 | 0 | dst->lifecycle = src->lifecycle; |
500 | 0 | return UA_STATUSCODE_GOOD; |
501 | 0 | } |
502 | | |
503 | | static UA_StatusCode |
504 | | UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, |
505 | 0 | UA_ReferenceTypeNode *dst) { |
506 | 0 | dst->isAbstract = src->isAbstract; |
507 | 0 | dst->symmetric = src->symmetric; |
508 | 0 | dst->referenceTypeIndex = src->referenceTypeIndex; |
509 | 0 | dst->subTypes = src->subTypes; |
510 | 0 | return UA_LocalizedText_copy(&src->inverseName, &dst->inverseName); |
511 | 0 | } |
512 | | |
513 | | static UA_StatusCode |
514 | 0 | UA_DataTypeNode_copy(const UA_DataTypeNode *src, UA_DataTypeNode *dst) { |
515 | 0 | dst->isAbstract = src->isAbstract; |
516 | 0 | return UA_STATUSCODE_GOOD; |
517 | 0 | } |
518 | | |
519 | | static UA_StatusCode |
520 | 0 | UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) { |
521 | 0 | dst->containsNoLoops = src->containsNoLoops; |
522 | 0 | dst->eventNotifier = src->eventNotifier; |
523 | 0 | return UA_STATUSCODE_GOOD; |
524 | 0 | } |
525 | | |
526 | | static void * |
527 | 331k | copyTarget(void *context, UA_ReferenceTargetTreeElem *elm) { |
528 | 331k | UA_NodeReferenceKind *drefs = (UA_NodeReferenceKind*)context; |
529 | 331k | return (void*)(uintptr_t) |
530 | 331k | addReferenceTargetToTree(drefs, elm->target.targetId, |
531 | 331k | elm->targetIdHash, |
532 | 331k | elm->target.targetNameHash); |
533 | 331k | } |
534 | | |
535 | | UA_StatusCode |
536 | 869k | UA_Node_copy(const UA_Node *src, UA_Node *dst) { |
537 | 869k | const UA_NodeHead *srchead = &src->head; |
538 | 869k | UA_NodeHead *dsthead = &dst->head; |
539 | 869k | if(srchead->nodeClass != dsthead->nodeClass) |
540 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
541 | | |
542 | | /* Copy standard content */ |
543 | 869k | UA_StatusCode retval = UA_NodeId_copy(&srchead->nodeId, &dsthead->nodeId); |
544 | 869k | retval |= UA_QualifiedName_copy(&srchead->browseName, &dsthead->browseName); |
545 | | |
546 | | /* Copy the display name in several languages */ |
547 | 1.73M | for(UA_LocalizedTextListEntry *lt = srchead->displayName; lt != NULL; lt = lt->next) { |
548 | 869k | UA_LocalizedTextListEntry *newEntry = (UA_LocalizedTextListEntry *) |
549 | 869k | UA_calloc(1, sizeof(UA_LocalizedTextListEntry)); |
550 | 869k | if(!newEntry) { |
551 | 0 | retval |= UA_STATUSCODE_BADOUTOFMEMORY; |
552 | 0 | break; |
553 | 0 | } |
554 | 869k | retval |= UA_LocalizedText_copy(<->localizedText, &newEntry->localizedText); |
555 | | |
556 | | /* Add to the linked list possibly in reverse order */ |
557 | 869k | newEntry->next = dsthead->displayName; |
558 | 869k | dsthead->displayName = newEntry; |
559 | 869k | } |
560 | | |
561 | | /* Copy the description in several languages */ |
562 | 869k | for(UA_LocalizedTextListEntry *lt = srchead->description; lt != NULL; lt = lt->next) { |
563 | 0 | UA_LocalizedTextListEntry *newEntry = (UA_LocalizedTextListEntry *) |
564 | 0 | UA_calloc(1, sizeof(UA_LocalizedTextListEntry)); |
565 | 0 | if(!newEntry) { |
566 | 0 | retval |= UA_STATUSCODE_BADOUTOFMEMORY; |
567 | 0 | break; |
568 | 0 | } |
569 | 0 | retval |= UA_LocalizedText_copy(<->localizedText, &newEntry->localizedText); |
570 | | |
571 | | /* Add to the linked list possibly in reverse order */ |
572 | 0 | newEntry->next = dsthead->description; |
573 | 0 | dsthead->description= newEntry; |
574 | 0 | } |
575 | | |
576 | 869k | dsthead->writeMask = srchead->writeMask; |
577 | 869k | dsthead->context = srchead->context; |
578 | 869k | dsthead->constructed = srchead->constructed; |
579 | 869k | #ifdef UA_ENABLE_SUBSCRIPTIONS |
580 | 869k | dsthead->monitoredItems = srchead->monitoredItems; |
581 | 869k | #endif |
582 | 869k | if(retval != UA_STATUSCODE_GOOD) { |
583 | 0 | UA_Node_clear(dst); |
584 | 0 | return retval; |
585 | 0 | } |
586 | | |
587 | | /* Copy the references */ |
588 | 869k | dsthead->references = NULL; |
589 | 869k | dsthead->referencesSize = 0; |
590 | 869k | if(srchead->referencesSize > 0) { |
591 | 869k | dsthead->references = (UA_NodeReferenceKind*) |
592 | 869k | UA_calloc(srchead->referencesSize, sizeof(UA_NodeReferenceKind)); |
593 | 869k | if(!dsthead->references) { |
594 | 0 | UA_Node_clear(dst); |
595 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
596 | 0 | } |
597 | 869k | dsthead->referencesSize = srchead->referencesSize; |
598 | | |
599 | 3.57M | for(size_t i = 0; i < srchead->referencesSize; ++i) { |
600 | 2.70M | UA_NodeReferenceKind *srefs = &srchead->references[i]; |
601 | 2.70M | UA_NodeReferenceKind *drefs = &dsthead->references[i]; |
602 | 2.70M | drefs->referenceTypeIndex = srefs->referenceTypeIndex; |
603 | 2.70M | drefs->isInverse = srefs->isInverse; |
604 | 2.70M | drefs->hasRefTree = srefs->hasRefTree; /* initially empty */ |
605 | | |
606 | | /* Copy all the targets */ |
607 | 2.70M | if(!srefs->hasRefTree) { |
608 | 2.69M | drefs->targets.array = (UA_ReferenceTarget*) |
609 | 2.69M | UA_malloc(sizeof(UA_ReferenceTarget) * srefs->targetsSize); |
610 | 2.69M | if(!drefs->targets.array) { |
611 | 0 | UA_Node_clear(dst); |
612 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
613 | 0 | } |
614 | 5.45M | for(size_t j = 0; j < srefs->targetsSize; j++) { |
615 | 2.75M | drefs->targets.array[j].targetNameHash = |
616 | 2.75M | srefs->targets.array[j].targetNameHash; |
617 | 2.75M | retval = UA_NodePointer_copy(srefs->targets.array[j].targetId, |
618 | 2.75M | &drefs->targets.array[j].targetId); |
619 | 2.75M | drefs->targetsSize++; /* avoid that targetsSize == 0 in error case */ |
620 | 2.75M | if(retval != UA_STATUSCODE_GOOD) { |
621 | 0 | UA_Node_clear(dst); |
622 | 0 | return retval; |
623 | 0 | } |
624 | 2.75M | } |
625 | 2.69M | } else { |
626 | 7.71k | void *res = ZIP_ITER(UA_ReferenceIdTree, |
627 | 7.71k | (UA_ReferenceIdTree*)&srefs->targets.tree.idRoot, |
628 | 7.71k | copyTarget, drefs); |
629 | 7.71k | if(res != NULL) { |
630 | 0 | UA_Node_clear(dst); |
631 | 0 | return (UA_StatusCode)(uintptr_t)res; |
632 | 0 | } |
633 | 7.71k | } |
634 | | |
635 | 2.70M | UA_assert(srefs->targetsSize > 0); |
636 | 2.70M | UA_assert(srefs->targetsSize == drefs->targetsSize); |
637 | 2.70M | } |
638 | 869k | } |
639 | | |
640 | | /* Copy unique content of the nodeclass */ |
641 | 869k | switch(src->head.nodeClass) { |
642 | 131k | case UA_NODECLASS_OBJECT: |
643 | 131k | retval = UA_ObjectNode_copy(&src->objectNode, &dst->objectNode); |
644 | 131k | break; |
645 | 737k | case UA_NODECLASS_VARIABLE: |
646 | 737k | retval = UA_VariableNode_copy(&src->variableNode, &dst->variableNode); |
647 | 737k | break; |
648 | 0 | case UA_NODECLASS_METHOD: |
649 | 0 | retval = UA_MethodNode_copy(&src->methodNode, &dst->methodNode); |
650 | 0 | break; |
651 | 0 | case UA_NODECLASS_OBJECTTYPE: |
652 | 0 | retval = UA_ObjectTypeNode_copy(&src->objectTypeNode, &dst->objectTypeNode); |
653 | 0 | break; |
654 | 0 | case UA_NODECLASS_VARIABLETYPE: |
655 | 0 | retval = UA_VariableTypeNode_copy(&src->variableTypeNode, &dst->variableTypeNode); |
656 | 0 | break; |
657 | 0 | case UA_NODECLASS_REFERENCETYPE: |
658 | 0 | retval = UA_ReferenceTypeNode_copy(&src->referenceTypeNode, &dst->referenceTypeNode); |
659 | 0 | break; |
660 | 0 | case UA_NODECLASS_DATATYPE: |
661 | 0 | retval = UA_DataTypeNode_copy(&src->dataTypeNode, &dst->dataTypeNode); |
662 | 0 | break; |
663 | 0 | case UA_NODECLASS_VIEW: |
664 | 0 | retval = UA_ViewNode_copy(&src->viewNode, &dst->viewNode); |
665 | 0 | break; |
666 | 0 | default: |
667 | 0 | break; |
668 | 869k | } |
669 | | |
670 | 869k | if(retval != UA_STATUSCODE_GOOD) |
671 | 0 | UA_Node_clear(dst); |
672 | | |
673 | 869k | return retval; |
674 | 869k | } |
675 | | |
676 | | UA_Node * |
677 | 0 | UA_Node_copy_alloc(const UA_Node *src) { |
678 | 0 | size_t nodesize = 0; |
679 | 0 | switch(src->head.nodeClass) { |
680 | 0 | case UA_NODECLASS_OBJECT: |
681 | 0 | nodesize = sizeof(UA_ObjectNode); |
682 | 0 | break; |
683 | 0 | case UA_NODECLASS_VARIABLE: |
684 | 0 | nodesize = sizeof(UA_VariableNode); |
685 | 0 | break; |
686 | 0 | case UA_NODECLASS_METHOD: |
687 | 0 | nodesize = sizeof(UA_MethodNode); |
688 | 0 | break; |
689 | 0 | case UA_NODECLASS_OBJECTTYPE: |
690 | 0 | nodesize = sizeof(UA_ObjectTypeNode); |
691 | 0 | break; |
692 | 0 | case UA_NODECLASS_VARIABLETYPE: |
693 | 0 | nodesize = sizeof(UA_VariableTypeNode); |
694 | 0 | break; |
695 | 0 | case UA_NODECLASS_REFERENCETYPE: |
696 | 0 | nodesize = sizeof(UA_ReferenceTypeNode); |
697 | 0 | break; |
698 | 0 | case UA_NODECLASS_DATATYPE: |
699 | 0 | nodesize = sizeof(UA_DataTypeNode); |
700 | 0 | break; |
701 | 0 | case UA_NODECLASS_VIEW: |
702 | 0 | nodesize = sizeof(UA_ViewNode); |
703 | 0 | break; |
704 | 0 | default: |
705 | 0 | return NULL; |
706 | 0 | } |
707 | | |
708 | 0 | UA_Node *dst = (UA_Node*)UA_calloc(1, nodesize); |
709 | 0 | if(!dst) |
710 | 0 | return NULL; |
711 | | |
712 | 0 | dst->head.nodeClass = src->head.nodeClass; |
713 | |
|
714 | 0 | UA_StatusCode retval = UA_Node_copy(src, dst); |
715 | 0 | if(retval != UA_STATUSCODE_GOOD) { |
716 | 0 | UA_free(dst); |
717 | 0 | return NULL; |
718 | 0 | } |
719 | 0 | return dst; |
720 | 0 | } |
721 | | /******************************/ |
722 | | /* Copy Attributes into Nodes */ |
723 | | /******************************/ |
724 | | |
725 | | static UA_StatusCode |
726 | 14.3M | copyStandardAttributes(UA_Node *node, const UA_NodeAttributes *attr) { |
727 | | /* UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); */ |
728 | | /* UA_QualifiedName_copy(&item->browseName, &node->browseName); */ |
729 | | |
730 | 14.3M | node->head.writeMask = attr->writeMask; |
731 | 14.3M | UA_StatusCode retval = UA_Node_insertOrUpdateDescription(node, &attr->description); |
732 | | /* The new nodeset format has optional display names: |
733 | | * https://github.com/open62541/open62541/issues/2627. If the display name |
734 | | * is NULL, take the name part of the browse name */ |
735 | 14.3M | if(attr->displayName.text.length == 0) { |
736 | 0 | UA_LocalizedText lt; |
737 | 0 | UA_LocalizedText_init(<); |
738 | 0 | lt.text = node->head.browseName.name; |
739 | 0 | retval |= UA_Node_insertOrUpdateDisplayName(node, <); |
740 | 0 | } else |
741 | 14.3M | retval |= UA_Node_insertOrUpdateDisplayName(node, &attr->displayName); |
742 | 14.3M | return retval; |
743 | 14.3M | } |
744 | | |
745 | | static UA_StatusCode |
746 | | copyCommonVariableAttributes(UA_VariableNode *node, |
747 | 10.0M | const UA_VariableAttributes *attr) { |
748 | | /* Copy the array dimensions */ |
749 | 10.0M | UA_StatusCode retval = |
750 | 10.0M | UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize, |
751 | 10.0M | (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); |
752 | 10.0M | if(retval != UA_STATUSCODE_GOOD) |
753 | 0 | return retval; |
754 | 10.0M | node->arrayDimensionsSize = attr->arrayDimensionsSize; |
755 | | |
756 | | /* Data type and value rank */ |
757 | 10.0M | retval = UA_NodeId_copy(&attr->dataType, &node->dataType); |
758 | 10.0M | if(retval != UA_STATUSCODE_GOOD) |
759 | 0 | return retval; |
760 | 10.0M | node->valueRank = attr->valueRank; |
761 | | |
762 | | /* Copy the value */ |
763 | 10.0M | node->valueSourceType = UA_VALUESOURCETYPE_INTERNAL; |
764 | 10.0M | retval = UA_Variant_copy(&attr->value, &node->valueSource.internal.value.value); |
765 | 10.0M | node->valueSource.internal.value.hasValue = |
766 | 10.0M | (node->valueSource.internal.value.value.type != NULL); |
767 | | |
768 | 10.0M | return retval; |
769 | 10.0M | } |
770 | | |
771 | | static UA_StatusCode |
772 | | copyVariableNodeAttributes(UA_VariableNode *vnode, |
773 | 9.59M | const UA_VariableAttributes *attr) { |
774 | 9.59M | vnode->accessLevel = attr->accessLevel; |
775 | 9.59M | vnode->historizing = attr->historizing; |
776 | 9.59M | vnode->minimumSamplingInterval = attr->minimumSamplingInterval; |
777 | 9.59M | return copyCommonVariableAttributes(vnode, attr); |
778 | 9.59M | } |
779 | | |
780 | | static UA_StatusCode |
781 | | copyVariableTypeNodeAttributes(UA_VariableTypeNode *vtnode, |
782 | 428k | const UA_VariableTypeAttributes *attr) { |
783 | 428k | vtnode->isAbstract = attr->isAbstract; |
784 | 428k | return copyCommonVariableAttributes((UA_VariableNode*)vtnode, |
785 | 428k | (const UA_VariableAttributes*)attr); |
786 | 428k | } |
787 | | |
788 | | static UA_StatusCode |
789 | 1.11M | copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) { |
790 | 1.11M | onode->eventNotifier = attr->eventNotifier; |
791 | 1.11M | return UA_STATUSCODE_GOOD; |
792 | 1.11M | } |
793 | | |
794 | | static UA_StatusCode |
795 | | copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode, |
796 | 499k | const UA_ReferenceTypeAttributes *attr) { |
797 | 499k | rtnode->isAbstract = attr->isAbstract; |
798 | 499k | rtnode->symmetric = attr->symmetric; |
799 | 499k | return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName); |
800 | 499k | } |
801 | | |
802 | | static UA_StatusCode |
803 | | copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode, |
804 | 906k | const UA_ObjectTypeAttributes *attr) { |
805 | 906k | otnode->isAbstract = attr->isAbstract; |
806 | 906k | return UA_STATUSCODE_GOOD; |
807 | 906k | } |
808 | | |
809 | | static UA_StatusCode |
810 | 0 | copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) { |
811 | 0 | vnode->containsNoLoops = attr->containsNoLoops; |
812 | 0 | vnode->eventNotifier = attr->eventNotifier; |
813 | 0 | return UA_STATUSCODE_GOOD; |
814 | 0 | } |
815 | | |
816 | | static UA_StatusCode |
817 | | copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode, |
818 | 1.20M | const UA_DataTypeAttributes *attr) { |
819 | 1.20M | dtnode->isAbstract = attr->isAbstract; |
820 | 1.20M | return UA_STATUSCODE_GOOD; |
821 | 1.20M | } |
822 | | |
823 | | static UA_StatusCode |
824 | | copyMethodNodeAttributes(UA_MethodNode *mnode, |
825 | 609k | const UA_MethodAttributes *attr) { |
826 | 609k | mnode->executable = attr->executable; |
827 | 609k | return UA_STATUSCODE_GOOD; |
828 | 609k | } |
829 | | |
830 | 14.3M | #define CHECK_ATTRIBUTES(TYPE) do { \ |
831 | 14.3M | if(attributeType != &UA_TYPES[UA_TYPES_##TYPE]) \ |
832 | 14.3M | return UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \ |
833 | 14.3M | } while(0) |
834 | | |
835 | | UA_StatusCode |
836 | | UA_Node_setAttributes(UA_Node *node, const void *attributes, |
837 | 14.3M | const UA_DataType *attributeType) { |
838 | | /* Copy the attributes into the node */ |
839 | 14.3M | UA_StatusCode retval = UA_STATUSCODE_GOOD; |
840 | 14.3M | switch(node->head.nodeClass) { |
841 | 1.11M | case UA_NODECLASS_OBJECT: |
842 | 1.11M | CHECK_ATTRIBUTES(OBJECTATTRIBUTES); |
843 | 1.11M | retval = copyObjectNodeAttributes(&node->objectNode, |
844 | 1.11M | (const UA_ObjectAttributes*)attributes); |
845 | 1.11M | break; |
846 | 9.59M | case UA_NODECLASS_VARIABLE: |
847 | 9.59M | CHECK_ATTRIBUTES(VARIABLEATTRIBUTES); |
848 | 9.59M | retval = copyVariableNodeAttributes(&node->variableNode, |
849 | 9.59M | (const UA_VariableAttributes*)attributes); |
850 | 9.59M | break; |
851 | 906k | case UA_NODECLASS_OBJECTTYPE: |
852 | 906k | CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES); |
853 | 906k | retval = copyObjectTypeNodeAttributes(&node->objectTypeNode, |
854 | 906k | (const UA_ObjectTypeAttributes*)attributes); |
855 | 906k | break; |
856 | 428k | case UA_NODECLASS_VARIABLETYPE: |
857 | 428k | CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES); |
858 | 428k | retval = copyVariableTypeNodeAttributes(&node->variableTypeNode, |
859 | 428k | (const UA_VariableTypeAttributes*)attributes); |
860 | 428k | break; |
861 | 499k | case UA_NODECLASS_REFERENCETYPE: |
862 | 499k | CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES); |
863 | 499k | retval = copyReferenceTypeNodeAttributes(&node->referenceTypeNode, |
864 | 499k | (const UA_ReferenceTypeAttributes*)attributes); |
865 | 499k | break; |
866 | 1.20M | case UA_NODECLASS_DATATYPE: |
867 | 1.20M | CHECK_ATTRIBUTES(DATATYPEATTRIBUTES); |
868 | 1.20M | retval = copyDataTypeNodeAttributes(&node->dataTypeNode, |
869 | 1.20M | (const UA_DataTypeAttributes*)attributes); |
870 | 1.20M | break; |
871 | 1 | case UA_NODECLASS_VIEW: |
872 | 1 | CHECK_ATTRIBUTES(VIEWATTRIBUTES); |
873 | 0 | retval = copyViewNodeAttributes(&node->viewNode, (const UA_ViewAttributes*)attributes); |
874 | 0 | break; |
875 | 609k | case UA_NODECLASS_METHOD: |
876 | 609k | CHECK_ATTRIBUTES(METHODATTRIBUTES); |
877 | 609k | retval = copyMethodNodeAttributes(&node->methodNode, (const UA_MethodAttributes*)attributes); |
878 | 609k | break; |
879 | 0 | case UA_NODECLASS_UNSPECIFIED: |
880 | 0 | default: |
881 | 0 | retval = UA_STATUSCODE_BADNODECLASSINVALID; |
882 | 14.3M | } |
883 | | |
884 | | /* No need (and no promise) to call UA_Node_clear in the error case. |
885 | | * Gets caught and handled outside. */ |
886 | 14.3M | if(UA_LIKELY(retval == UA_STATUSCODE_GOOD)) |
887 | 14.3M | retval = copyStandardAttributes(node, (const UA_NodeAttributes*)attributes); |
888 | 14.3M | return retval; |
889 | 14.3M | } |
890 | | |
891 | | /*********************/ |
892 | | /* Manage References */ |
893 | | /*********************/ |
894 | | |
895 | | static UA_StatusCode |
896 | | addReferenceTargetToTree(UA_NodeReferenceKind *rk, UA_NodePointer targetId, |
897 | 24.4M | UA_UInt32 targetIdHash, UA_UInt32 targetNameHash) { |
898 | 24.4M | UA_ReferenceTargetTreeElem *entry = (UA_ReferenceTargetTreeElem*) |
899 | 24.4M | UA_malloc(sizeof(UA_ReferenceTargetTreeElem)); |
900 | 24.4M | if(!entry) |
901 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
902 | | |
903 | 24.4M | UA_StatusCode res = UA_NodePointer_copy(targetId, &entry->target.targetId); |
904 | 24.4M | if(res != UA_STATUSCODE_GOOD) { |
905 | 0 | UA_free(entry); |
906 | 0 | return res; |
907 | 0 | } |
908 | | |
909 | 24.4M | entry->targetIdHash = targetIdHash; |
910 | 24.4M | entry->target.targetNameHash = targetNameHash; |
911 | | |
912 | 24.4M | ZIP_INSERT(UA_ReferenceIdTree, |
913 | 24.4M | (UA_ReferenceIdTree*)&rk->targets.tree.idRoot, entry); |
914 | 24.4M | ZIP_INSERT(UA_ReferenceNameTree, |
915 | 24.4M | (UA_ReferenceNameTree*)&rk->targets.tree.nameRoot, entry); |
916 | | |
917 | 24.4M | rk->targetsSize++; |
918 | 24.4M | return UA_STATUSCODE_GOOD; |
919 | 24.4M | } |
920 | | |
921 | | static UA_StatusCode |
922 | | addReferenceTarget(UA_NodeReferenceKind *rk, UA_NodePointer targetId, |
923 | 76.0M | UA_UInt32 targetNameHash) { |
924 | | /* Insert into tree */ |
925 | 76.0M | if(rk->hasRefTree) { |
926 | 24.0M | UA_ExpandedNodeId en = UA_NodePointer_toExpandedNodeId(targetId); |
927 | 24.0M | return addReferenceTargetToTree(rk, targetId, UA_ExpandedNodeId_hash(&en), |
928 | 24.0M | targetNameHash); |
929 | 24.0M | } |
930 | | |
931 | | /* Insert to the array */ |
932 | 51.9M | UA_ReferenceTarget *newRefs = (UA_ReferenceTarget*) |
933 | 51.9M | UA_realloc(rk->targets.array, |
934 | 51.9M | sizeof(UA_ReferenceTarget) * (rk->targetsSize + 1)); |
935 | 51.9M | if(!newRefs) |
936 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
937 | 51.9M | rk->targets.array = newRefs; |
938 | | |
939 | 51.9M | UA_StatusCode retval = |
940 | 51.9M | UA_NodePointer_copy(targetId, |
941 | 51.9M | &rk->targets.array[rk->targetsSize].targetId); |
942 | 51.9M | rk->targets.array[rk->targetsSize].targetNameHash = targetNameHash; |
943 | 51.9M | if(retval != UA_STATUSCODE_GOOD) { |
944 | 0 | if(rk->targetsSize == 0) { |
945 | 0 | UA_free(rk->targets.array); |
946 | 0 | rk->targets.array = NULL; |
947 | 0 | } |
948 | 0 | return retval; |
949 | 0 | } |
950 | | |
951 | 51.9M | rk->targetsSize++; |
952 | 51.9M | return UA_STATUSCODE_GOOD; |
953 | 51.9M | } |
954 | | |
955 | | static UA_StatusCode |
956 | | addReferenceKind(UA_NodeHead *head, UA_Byte refTypeIndex, UA_Boolean isForward, |
957 | 40.8M | const UA_NodePointer target, UA_UInt32 targetBrowseNameHash) { |
958 | 40.8M | UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) |
959 | 40.8M | UA_realloc(head->references, |
960 | 40.8M | sizeof(UA_NodeReferenceKind) * (head->referencesSize+1)); |
961 | 40.8M | if(!refs) |
962 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
963 | 40.8M | head->references = refs; |
964 | | |
965 | 40.8M | UA_NodeReferenceKind *newRef = &refs[head->referencesSize]; |
966 | 40.8M | memset(newRef, 0, sizeof(UA_NodeReferenceKind)); |
967 | 40.8M | newRef->referenceTypeIndex = refTypeIndex; |
968 | 40.8M | newRef->isInverse = !isForward; |
969 | 40.8M | UA_StatusCode res = addReferenceTarget(newRef, target, targetBrowseNameHash); |
970 | 40.8M | if(res != UA_STATUSCODE_GOOD) { |
971 | 0 | if(head->referencesSize == 0) { |
972 | 0 | UA_free(head->references); |
973 | 0 | head->references = NULL; |
974 | 0 | } |
975 | 0 | return res; |
976 | 0 | } |
977 | | |
978 | 40.8M | head->referencesSize++; |
979 | 40.8M | return UA_STATUSCODE_GOOD; |
980 | 40.8M | } |
981 | | |
982 | | UA_StatusCode |
983 | | UA_Node_addReference(UA_Node *node, UA_Byte refTypeIndex, UA_Boolean isForward, |
984 | | const UA_ExpandedNodeId *targetNodeId, |
985 | 72.2M | UA_UInt32 targetBrowseNameHash) { |
986 | | /* Find the matching reference kind */ |
987 | 157M | for(size_t i = 0; i < node->head.referencesSize; ++i) { |
988 | 117M | UA_NodeReferenceKind *refs = &node->head.references[i]; |
989 | | |
990 | | /* Reference direction does not match */ |
991 | 117M | if(refs->isInverse == isForward) |
992 | 53.5M | continue; |
993 | | |
994 | | /* Reference type does not match */ |
995 | 63.4M | if(refs->referenceTypeIndex != refTypeIndex) |
996 | 32.0M | continue; |
997 | | |
998 | | /* Does an identical reference already exist? */ |
999 | 31.4M | const UA_ReferenceTarget *found = |
1000 | 31.4M | UA_NodeReferenceKind_findTarget(refs, targetNodeId); |
1001 | 31.4M | if(found) |
1002 | 0 | return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; |
1003 | | |
1004 | | /* If there are many references, attempt to switch to the |
1005 | | * tree-representation. This speeds up all reference lookups. Continue |
1006 | | * with the array-representation in case of an error. */ |
1007 | 31.4M | if(!refs->hasRefTree && refs->targetsSize >= 31) |
1008 | 0 | UA_NodeReferenceKind_switch(refs); |
1009 | | |
1010 | | /* Add to existing ReferenceKind */ |
1011 | 31.4M | return addReferenceTarget(refs, UA_NodePointer_fromExpandedNodeId(targetNodeId), |
1012 | 31.4M | targetBrowseNameHash); |
1013 | 31.4M | } |
1014 | | |
1015 | | /* Add new ReferenceKind for the target */ |
1016 | 40.8M | return addReferenceKind(&node->head, refTypeIndex, isForward, |
1017 | 40.8M | UA_NodePointer_fromExpandedNodeId(targetNodeId), |
1018 | 40.8M | targetBrowseNameHash); |
1019 | | |
1020 | 72.2M | } |
1021 | | |
1022 | | UA_StatusCode |
1023 | | UA_Node_deleteReference(UA_Node *node, UA_Byte refTypeIndex, UA_Boolean isForward, |
1024 | 1.38M | const UA_ExpandedNodeId *targetNodeId) { |
1025 | 1.38M | UA_NodeHead *head = &node->head; |
1026 | 5.15M | for(size_t i = 0; i < head->referencesSize; i++) { |
1027 | 4.76M | UA_NodeReferenceKind *refs = &head->references[i]; |
1028 | 4.76M | if(isForward == refs->isInverse) |
1029 | 1.74M | continue; |
1030 | 3.02M | if(refTypeIndex != refs->referenceTypeIndex) |
1031 | 1.63M | continue; |
1032 | | |
1033 | | /* Cast out the const qualifier (hack!) */ |
1034 | 1.38M | UA_ReferenceTarget *target = (UA_ReferenceTarget*)(uintptr_t) |
1035 | 1.38M | UA_NodeReferenceKind_findTarget(refs, targetNodeId); |
1036 | 1.38M | if(!target) |
1037 | 385k | continue; |
1038 | | |
1039 | | /* Ok, delete the reference. Cannot fail */ |
1040 | 1.00M | refs->targetsSize--; |
1041 | | |
1042 | 1.00M | if(!refs->hasRefTree) { |
1043 | | /* Remove from array */ |
1044 | 202k | UA_NodePointer_clear(&target->targetId); |
1045 | | |
1046 | | /* Elements remaining. Realloc. */ |
1047 | 202k | if(refs->targetsSize > 0) { |
1048 | 116k | if(target != &refs->targets.array[refs->targetsSize]) |
1049 | 9.42k | *target = refs->targets.array[refs->targetsSize]; |
1050 | 116k | UA_ReferenceTarget *newRefs = (UA_ReferenceTarget*) |
1051 | 116k | UA_realloc(refs->targets.array, |
1052 | 116k | sizeof(UA_ReferenceTarget) * refs->targetsSize); |
1053 | 116k | if(newRefs) |
1054 | 116k | refs->targets.array = newRefs; |
1055 | 116k | return UA_STATUSCODE_GOOD; /* Realloc allowed to fail */ |
1056 | 116k | } |
1057 | | |
1058 | | /* Remove the last target. Remove the ReferenceKind below */ |
1059 | 86.1k | UA_free(refs->targets.array); |
1060 | 799k | } else { |
1061 | 799k | UA_ReferenceTargetTreeElem *elem = (UA_ReferenceTargetTreeElem*)target; |
1062 | 799k | ZIP_REMOVE(UA_ReferenceIdTree, |
1063 | 799k | (UA_ReferenceIdTree*)&refs->targets.tree.idRoot, elem); |
1064 | 799k | ZIP_REMOVE(UA_ReferenceNameTree, |
1065 | 799k | (UA_ReferenceNameTree*)&refs->targets.tree.nameRoot, elem); |
1066 | 799k | UA_NodePointer_clear(&target->targetId); |
1067 | 799k | UA_free(target); |
1068 | 799k | if(refs->targetsSize > 0) |
1069 | 792k | return UA_STATUSCODE_GOOD; |
1070 | 799k | } |
1071 | | |
1072 | | /* No targets remaining. Remove the ReferenceKind. */ |
1073 | 93.5k | head->referencesSize--; |
1074 | 93.5k | if(head->referencesSize > 0) { |
1075 | | /* No target for the ReferenceType remaining. Remove and shrink down |
1076 | | * allocated buffer. Ignore errors in case memory buffer could not |
1077 | | * be shrinked down. */ |
1078 | 93.4k | if(i != head->referencesSize) |
1079 | 22.0k | head->references[i] = head->references[node->head.referencesSize]; |
1080 | 93.4k | UA_NodeReferenceKind *newRefs = (UA_NodeReferenceKind*) |
1081 | 93.4k | UA_realloc(head->references, |
1082 | 93.4k | sizeof(UA_NodeReferenceKind) * head->referencesSize); |
1083 | 93.4k | if(newRefs) |
1084 | 93.4k | head->references = newRefs; |
1085 | 93.4k | } else { |
1086 | | /* No remaining references of any ReferenceType */ |
1087 | 58 | UA_free(head->references); |
1088 | 58 | head->references = NULL; |
1089 | 58 | } |
1090 | 93.5k | return UA_STATUSCODE_GOOD; |
1091 | 1.00M | } |
1092 | 385k | return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED; |
1093 | 1.38M | } |
1094 | | |
1095 | | void |
1096 | 16.0M | UA_Node_deleteReferencesSubset(UA_Node *node, const UA_ReferenceTypeSet *keepSet) { |
1097 | 16.0M | UA_NodeHead *head = &node->head; |
1098 | 16.0M | UA_assert(head->references != NULL || head->referencesSize == 0); |
1099 | 16.0M | size_t i = 0; |
1100 | 60.3M | while(i < head->referencesSize) { |
1101 | | /* Keep the references of this type? */ |
1102 | 44.3M | UA_NodeReferenceKind *refs = &head->references[i]; |
1103 | 44.3M | if(UA_ReferenceTypeSet_contains(keepSet, refs->referenceTypeIndex)){ |
1104 | 869k | i++; |
1105 | 869k | continue; |
1106 | 869k | } |
1107 | | |
1108 | | /* Remove all target entries. Don't remove entries from browseName tree. |
1109 | | * The entire ReferenceKind will be removed anyway. */ |
1110 | 43.4M | if(!refs->hasRefTree) { |
1111 | 93.9M | for(size_t j = 0; j < refs->targetsSize; j++) { |
1112 | | /* Consistency requirement: If refs->targetsSize > 0, then the |
1113 | | * targets array is non-NULL */ |
1114 | 50.7M | UA_assert(refs->targets.array != NULL); |
1115 | 50.7M | UA_NodePointer_clear(&refs->targets.array[j].targetId); |
1116 | 50.7M | } |
1117 | 43.2M | UA_free(refs->targets.array); |
1118 | 43.2M | } else { |
1119 | 223k | ZIP_ITER(UA_ReferenceIdTree, |
1120 | 223k | (UA_ReferenceIdTree*)&refs->targets.tree.idRoot, |
1121 | 223k | removeTreeEntry, NULL); |
1122 | 223k | } |
1123 | | |
1124 | | /* Move last references-kind entry to this position. Don't memcpy over |
1125 | | * the same position. Don't increment i: the swapped-in element must be |
1126 | | * checked on the next iteration. */ |
1127 | 43.4M | head->referencesSize--; |
1128 | 43.4M | if(i != head->referencesSize) |
1129 | 27.3M | head->references[i] = head->references[head->referencesSize]; |
1130 | 43.4M | } |
1131 | | |
1132 | 16.0M | if(head->referencesSize > 0) { |
1133 | | /* Realloc to save memory. Ignore if realloc fails. */ |
1134 | 869k | UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*) |
1135 | 869k | UA_realloc(head->references, |
1136 | 869k | sizeof(UA_NodeReferenceKind) * head->referencesSize); |
1137 | 869k | if(refs) |
1138 | 869k | head->references = refs; |
1139 | 15.2M | } else { |
1140 | | /* The array is empty. Remove. */ |
1141 | 15.2M | UA_free(head->references); |
1142 | 15.2M | head->references = NULL; |
1143 | 15.2M | } |
1144 | 16.0M | } |
1145 | | |
1146 | 15.2M | void UA_Node_deleteReferences(UA_Node *node) { |
1147 | 15.2M | UA_ReferenceTypeSet noRefs; |
1148 | 15.2M | UA_ReferenceTypeSet_init(&noRefs); |
1149 | 15.2M | UA_Node_deleteReferencesSubset(node, &noRefs); |
1150 | 15.2M | } |
1151 | | |
1152 | | static UA_StatusCode |
1153 | | UA_Node_insertOrUpdateLocale(UA_LocalizedTextListEntry **root, |
1154 | 28.7M | const UA_LocalizedText *value) { |
1155 | 28.7M | UA_StatusCode res; |
1156 | 28.7M | UA_LocalizedTextListEntry *lt, *prev = NULL; |
1157 | 28.7M | for(lt = *root; lt != NULL; prev = lt, lt = lt->next) { |
1158 | 0 | if(!UA_String_equal(&value->locale, <->localizedText.locale)) |
1159 | 0 | continue; |
1160 | | |
1161 | | /* No text -> remove the entry for this locale */ |
1162 | 0 | if(value->text.length == 0) { |
1163 | 0 | if(prev == NULL) |
1164 | 0 | *root = lt->next; |
1165 | 0 | else |
1166 | 0 | prev->next = lt->next; |
1167 | 0 | UA_LocalizedText_clear(<->localizedText); |
1168 | 0 | UA_free(lt); |
1169 | 0 | return UA_STATUSCODE_GOOD; |
1170 | 0 | } |
1171 | | |
1172 | | /* First make a copy of the text, if this succeeds replace the old |
1173 | | * version */ |
1174 | 0 | UA_String tmp; |
1175 | 0 | res = UA_String_copy(&value->text, &tmp); |
1176 | 0 | if(res != UA_STATUSCODE_GOOD) |
1177 | 0 | return res; |
1178 | | |
1179 | 0 | UA_String_clear(<->localizedText.text); |
1180 | 0 | lt->localizedText.text = tmp; |
1181 | 0 | return UA_STATUSCODE_GOOD; |
1182 | 0 | } |
1183 | | |
1184 | | /* The locale does not exist so far */ |
1185 | | |
1186 | | /* Do nothing if a non-existing locale should be removed */ |
1187 | 28.7M | if(value->text.length == 0) |
1188 | 14.1M | return UA_STATUSCODE_GOOD; |
1189 | | |
1190 | | /* Add a new localized text */ |
1191 | 14.5M | lt = (UA_LocalizedTextListEntry *)UA_malloc(sizeof(UA_LocalizedTextListEntry)); |
1192 | 14.5M | if(!lt) |
1193 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
1194 | | |
1195 | 14.5M | res = UA_LocalizedText_copy(value, <->localizedText); |
1196 | 14.5M | if(res != UA_STATUSCODE_GOOD) { |
1197 | 0 | UA_free(lt); |
1198 | 0 | return res; |
1199 | 0 | } |
1200 | | |
1201 | 14.5M | lt->next = *root; |
1202 | 14.5M | *root = lt; |
1203 | 14.5M | return UA_STATUSCODE_GOOD; |
1204 | 14.5M | } |
1205 | | |
1206 | | UA_StatusCode |
1207 | | UA_Node_insertOrUpdateDisplayName(UA_Node *node, |
1208 | 14.3M | const UA_LocalizedText *displayName) { |
1209 | 14.3M | return UA_Node_insertOrUpdateLocale(&node->head.displayName, displayName); |
1210 | 14.3M | } |
1211 | | |
1212 | | UA_StatusCode |
1213 | | UA_Node_insertOrUpdateDescription(UA_Node *node, |
1214 | 14.3M | const UA_LocalizedText *description) { |
1215 | 14.3M | return UA_Node_insertOrUpdateLocale(&node->head.description, description); |
1216 | 14.3M | } |