Browse Source

NodesetCompiler use Byte array instead of string (#1621)

* Use byte array instead of string

fixes #1254

The huge strings are changed as arrays of ASCII values of each element inside for ByteArrays. And also some implementations are done due to task.

* Remove max-string-length parameter for nodeset compiler

* Flush nodeset generated file to avoid buffer race conditions

* Use global variables for byte array to improve performance
Yekta Nizamoğlu 6 years ago
parent
commit
8158f37248

+ 0 - 8
CMakeLists.txt

@@ -739,13 +739,6 @@ add_dependencies(open62541-amalgamation-header open62541-generator-types)
 add_dependencies(open62541-amalgamation-source open62541-generator-types
 add_dependencies(open62541-amalgamation-source open62541-generator-types
                  open62541-generator-transport open62541-generator-statuscode)
                  open62541-generator-transport open62541-generator-statuscode)
 
 
-
-set(NODESET_MAX_STR_LEN 0)
-if(MSVC)
-    # Visual Studio has a maximum string length of 65535 bytes
-    set(NODESET_MAX_STR_LEN 65535)
-endif()
-
 if(NOT UA_NODESET_ENCODE_BINARY_SIZE)
 if(NOT UA_NODESET_ENCODE_BINARY_SIZE)
     set(UA_NODESET_ENCODE_BINARY_SIZE 32000)
     set(UA_NODESET_ENCODE_BINARY_SIZE 32000)
 endif()
 endif()
@@ -757,7 +750,6 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespace0.c
                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
                            --generate-ns0
                            --generate-ns0
                            --internal-headers
                            --internal-headers
-                           --max-string-length=${NODESET_MAX_STR_LEN}
                            --encode-binary-size=${UA_NODESET_ENCODE_BINARY_SIZE}
                            --encode-binary-size=${UA_NODESET_ENCODE_BINARY_SIZE}
                            --ignore ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/NodeID_NS0_Base.txt
                            --ignore ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/NodeID_NS0_Base.txt
                            --xml ${UA_FILE_NS0}
                            --xml ${UA_FILE_NS0}

+ 1 - 1
doc/nodeset_compiler.rst

@@ -212,7 +212,7 @@ Take the previous snippet and save it to a file ``myNS.xml``. To compile this no
                                [--generate-ns0] [--internal-headers]
                                [--generate-ns0] [--internal-headers]
                                [-b <blacklistFile>] [-i <ignoreFile>]
                                [-b <blacklistFile>] [-i <ignoreFile>]
                                [-t <typesArray>]
                                [-t <typesArray>]
-                               [--max-string-length MAX_STRING_LENGTH] [-v]
+                               [-v]
                                <outputFile>
                                <outputFile>
 
 
     positional arguments:
     positional arguments:

+ 12 - 2
tools/nodeset_compiler/backend_open62541.py

@@ -23,6 +23,7 @@ import string
 from os.path import basename
 from os.path import basename
 import logging
 import logging
 import codecs
 import codecs
+import os
 try:
 try:
     from StringIO import StringIO
     from StringIO import StringIO
 except ImportError:
 except ImportError:
@@ -130,7 +131,7 @@ def sortNodes(nodeset):
 # Generate C Code #
 # Generate C Code #
 ###################
 ###################
 
 
-def generateOpen62541Code(nodeset, outfilename, generate_ns0=False, internal_headers=False, typesArray=[], max_string_length=0, encode_binary_size=32000):
+def generateOpen62541Code(nodeset, outfilename, generate_ns0=False, internal_headers=False, typesArray=[], encode_binary_size=32000):
     outfilebase = basename(outfilename)
     outfilebase = basename(outfilename)
     # Printing functions
     # Printing functions
     outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
     outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
@@ -234,12 +235,16 @@ extern UA_StatusCode %s(UA_Server *server);
         parentref = node.popParentRef(parentreftypes)
         parentref = node.popParentRef(parentreftypes)
         if not node.hidden:
         if not node.hidden:
             writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
             writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
-            code = generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, parentref, encode_binary_size)
+            code_global = []
+            code = generateNodeCode_begin(node, nodeset, generate_ns0, parentref, encode_binary_size, code_global)
             if code is None:
             if code is None:
                 writec("/* Ignored. No parent */")
                 writec("/* Ignored. No parent */")
                 nodeset.hide_node(node.id)
                 nodeset.hide_node(node.id)
                 continue
                 continue
             else:
             else:
+                if len(code_global) > 0:
+                    writec("\n".join(code_global))
+                    writec("\n")
                 writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_begin(UA_Server *server, UA_UInt16* ns) {")
                 writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_begin(UA_Server *server, UA_UInt16* ns) {")
                 if isinstance(node, MethodNode):
                 if isinstance(node, MethodNode):
                     writec("#ifdef UA_ENABLE_METHODCALLS")
                     writec("#ifdef UA_ENABLE_METHODCALLS")
@@ -295,10 +300,15 @@ UA_StatusCode retVal = UA_STATUSCODE_GOOD;""" % (outfilebase))
         writec("retVal |= function_" + outfilebase + "_" + str(i) + "_finish(server, ns);")
         writec("retVal |= function_" + outfilebase + "_" + str(i) + "_finish(server, ns);")
 
 
     writec("return retVal;\n}")
     writec("return retVal;\n}")
+    outfileh.flush()
+    os.fsync(outfileh)
     outfileh.close()
     outfileh.close()
     fullCode = outfilec.getvalue()
     fullCode = outfilec.getvalue()
     outfilec.close()
     outfilec.close()
 
 
     outfilec = codecs.open(outfilename + ".c", r"w+", encoding='utf-8')
     outfilec = codecs.open(outfilename + ".c", r"w+", encoding='utf-8')
     outfilec.write(fullCode)
     outfilec.write(fullCode)
+    outfilec.flush()
+    os.fsync(outfilec)
     outfilec.close()
     outfilec.close()
+

+ 33 - 29
tools/nodeset_compiler/backend_open62541_datatypes.py

@@ -14,18 +14,13 @@ def generateBooleanCode(value):
 def makeCLiteral(value):
 def makeCLiteral(value):
     return value.replace('\\', r'\\\\').replace('\n', r'\\n').replace('\r', r'')
     return value.replace('\\', r'\\\\').replace('\n', r'\\n').replace('\r', r'')
 
 
-def splitStringLiterals(value, splitLength=500, max_string_length=0):
+def splitStringLiterals(value, splitLength=500):
     """
     """
     Split a string literal longer than splitLength into smaller literals.
     Split a string literal longer than splitLength into smaller literals.
     E.g. "Some very long text" will be split into "Some ver" "y long te" "xt"
     E.g. "Some very long text" will be split into "Some ver" "y long te" "xt"
     On VS2008 there is a maximum allowed length of a single string literal.
     On VS2008 there is a maximum allowed length of a single string literal.
-    If maxLength is set and the string is longer than maxLength, then an
-    empty string will be returned.
     """
     """
     value = value.strip()
     value = value.strip()
-    if max_string_length > 0 and len(value) > max_string_length:
-        logger.info("String is longer than {}. Returning empty string.".format(max_string_length))
-        return "\"\""
     if len(value) < splitLength or splitLength == 0:
     if len(value) < splitLength or splitLength == 0:
         return "\"" + value.replace('"', r'\"') + "\""
         return "\"" + value.replace('"', r'\"') + "\""
     ret = ""
     ret = ""
@@ -36,27 +31,33 @@ def splitStringLiterals(value, splitLength=500, max_string_length=0):
     ret += "\"" + tmp.replace('"', r'\"') + "\" "
     ret += "\"" + tmp.replace('"', r'\"') + "\" "
     return ret
     return ret
 
 
-def generateStringCode(value, alloc=False, max_string_length=0):
+def generateStringCode(value, alloc=False):
     value = makeCLiteral(value)
     value = makeCLiteral(value)
-    return u"UA_STRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+    return u"UA_STRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value))
 
 
-def generateXmlElementCode(value, alloc=False, max_string_length=0):
+def generateXmlElementCode(value, alloc=False):
     value = makeCLiteral(value)
     value = makeCLiteral(value)
-    return u"UA_XMLELEMENT{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+    return u"UA_XMLELEMENT{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value))
 
 
-def generateByteStringCode(value, alloc=False, max_string_length=0):
-    value = makeCLiteral(value)
-    return u"UA_BYTESTRING{}({})".format("_ALLOC" if alloc else "", splitStringLiterals(value, max_string_length=max_string_length))
+def generateByteStringCode(value, valueName, global_var_code):
+    asciiarray = [ord(c) for c in value.strip()]
+    asciiarraystr = str(asciiarray).rstrip(']').lstrip('[')
+    global_var_code.append("static const UA_Byte {instance}_byteArray[{len}] = {{{data}}};".format(
+        len=len(asciiarray), data=asciiarraystr, instance=valueName
+    ))
+    # Cast away const with '(UA_Byte *)(void*)(uintptr_t)' since we know that UA_Server_addNode_begin will copy the content
+    return "{instance}->length = {len};\n{instance}->data = (UA_Byte *)(void*)(uintptr_t){instance}_byteArray;"\
+                                                .format(len=len(asciiarray), instance=valueName)
 
 
-def generateLocalizedTextCode(value, alloc=False, max_string_length=0):
+def generateLocalizedTextCode(value, alloc=False):
     vt = makeCLiteral(value.text)
     vt = makeCLiteral(value.text)
     return u"UA_LOCALIZEDTEXT{}(\"{}\", {})".format("_ALLOC" if alloc else "", value.locale,
     return u"UA_LOCALIZEDTEXT{}(\"{}\", {})".format("_ALLOC" if alloc else "", value.locale,
-                                                   splitStringLiterals(vt, max_string_length=max_string_length))
+                                                   splitStringLiterals(vt))
 
 
-def generateQualifiedNameCode(value, alloc=False, max_string_length=0):
+def generateQualifiedNameCode(value, alloc=False,):
     vn = makeCLiteral(value.name)
     vn = makeCLiteral(value.name)
     return u"UA_QUALIFIEDNAME{}(ns[{}], {})".format("_ALLOC" if alloc else "",
     return u"UA_QUALIFIEDNAME{}(ns[{}], {})".format("_ALLOC" if alloc else "",
-                                                     str(value.ns), splitStringLiterals(vn, max_string_length=max_string_length))
+                                                     str(value.ns), splitStringLiterals(vn))
 
 
 def generateNodeIdCode(value):
 def generateNodeIdCode(value):
     if not value:
     if not value:
@@ -81,26 +82,29 @@ def generateDateTimeCode(value):
     mSecsSinceEpoch = int((value - epoch).total_seconds() * 1000.0)
     mSecsSinceEpoch = int((value - epoch).total_seconds() * 1000.0)
     return "( (UA_DateTime)(" + str(mSecsSinceEpoch) + " * UA_DATETIME_MSEC) + UA_DATETIME_UNIX_EPOCH)"
     return "( (UA_DateTime)(" + str(mSecsSinceEpoch) + " * UA_DATETIME_MSEC) + UA_DATETIME_UNIX_EPOCH)"
 
 
-def generateNodeValueCode(node, instanceName, asIndirect=False, max_string_length=0):
+def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_code, asIndirect=False):
     if type(node) in [Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double]:
     if type(node) in [Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double]:
-        return "(UA_" + node.__class__.__name__ + ") " + str(node.value)
+        return prepend + "(UA_" + node.__class__.__name__ + ") " + str(node.value) + ";"
     elif type(node) == String:
     elif type(node) == String:
-        return generateStringCode(node.value, alloc=asIndirect, max_string_length=max_string_length)
+        return prepend + generateStringCode(node.value, alloc=asIndirect) + ";"
     elif type(node) == XmlElement:
     elif type(node) == XmlElement:
-        return generateXmlElementCode(node.value, alloc=asIndirect, max_string_length=max_string_length)
+        return prepend + generateXmlElementCode(node.value, alloc=asIndirect) + ";"
     elif type(node) == ByteString:
     elif type(node) == ByteString:
         # replace whitespaces between tags and remove newlines
         # replace whitespaces between tags and remove newlines
-        return "UA_BYTESTRING_NULL" if not node.value else generateByteStringCode(re.sub(r">\s*<", "><", re.sub(r"[\r\n]+", "", node.value)), alloc=asIndirect, max_string_length=max_string_length)
+        return prepend + "UA_BYTESTRING_NULL;" if not node.value else generateByteStringCode(
+            re.sub(r">\s*<", "><", re.sub(r"[\r\n]+", "", node.value)), valueName, global_var_code)
+        # the replacements done here is just for the array form can be workable in C code. It doesn't couses any problem
+        # because the core data used here is already in byte form. So, there is no way we disturb it.
     elif type(node) == LocalizedText:
     elif type(node) == LocalizedText:
-        return generateLocalizedTextCode(node, alloc=asIndirect, max_string_length=max_string_length)
+        return prepend + generateLocalizedTextCode(node, alloc=asIndirect) + ";"
     elif type(node) == NodeId:
     elif type(node) == NodeId:
-        return generateNodeIdCode(node)
+        return prepend + generateNodeIdCode(node) + ";"
     elif type(node) == ExpandedNodeId:
     elif type(node) == ExpandedNodeId:
-        return generateExpandedNodeIdCode(node)
+        return prepend + generateExpandedNodeIdCode(node) + ";"
     elif type(node) == DateTime:
     elif type(node) == DateTime:
-        return generateDateTimeCode(node.value)
+        return prepend + generateDateTimeCode(node.value) + ";"
     elif type(node) == QualifiedName:
     elif type(node) == QualifiedName:
-        return generateQualifiedNameCode(node.value, alloc=asIndirect, max_string_length=max_string_length)
+        return prepend + generateQualifiedNameCode(node.value, alloc=asIndirect) + ";"
     elif type(node) == StatusCode:
     elif type(node) == StatusCode:
         raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
         raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
     elif type(node) == DiagnosticInfo:
     elif type(node) == DiagnosticInfo:
@@ -109,5 +113,5 @@ def generateNodeValueCode(node, instanceName, asIndirect=False, max_string_lengt
         raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
         raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
     elif type(node) == ExtensionObject:
     elif type(node) == ExtensionObject:
         if asIndirect == False:
         if asIndirect == False:
-            return "*" + str(instanceName)
-        return str(instanceName)
+            return prepend + "*" + str(instanceName) + ";"
+        return prepend + str(instanceName) + ";"

+ 48 - 37
tools/nodeset_compiler/backend_open62541_nodes.py

@@ -72,9 +72,10 @@ def generateObjectNodeCode(node):
         code.append("attr.eventNotifier = true;")
         code.append("attr.eventNotifier = true;")
     return code
     return code
 
 
-def generateVariableNodeCode(node, nodeset, max_string_length, encode_binary_size):
+def generateVariableNodeCode(node, nodeset, encode_binary_size):
     code = []
     code = []
     codeCleanup = []
     codeCleanup = []
+    codeGlobal = []
     code.append("UA_VariableAttributes attr = UA_VariableAttributes_default;")
     code.append("UA_VariableAttributes attr = UA_VariableAttributes_default;")
     if node.historizing:
     if node.historizing:
         code.append("attr.historizing = true;")
         code.append("attr.historizing = true;")
@@ -111,19 +112,21 @@ def generateVariableNodeCode(node, nodeset, max_string_length, encode_binary_siz
 
 
             if dataTypeNode.isEncodable():
             if dataTypeNode.isEncodable():
                 if node.value is not None:
                 if node.value is not None:
-                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length=max_string_length, encode_binary_size=encode_binary_size)
+                    [code1, codeCleanup1, codeGlobal1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, encode_binary_size=encode_binary_size)
                     code += code1
                     code += code1
                     codeCleanup += codeCleanup1
                     codeCleanup += codeCleanup1
+                    codeGlobal += codeGlobal1
                     if node.valueRank > 0 and len(node.arrayDimensions) == node.valueRank:
                     if node.valueRank > 0 and len(node.arrayDimensions) == node.valueRank:
                         code.append("attr.value.arrayDimensionsSize = attr.arrayDimensionsSize;")
                         code.append("attr.value.arrayDimensionsSize = attr.arrayDimensionsSize;")
                         code.append("attr.value.arrayDimensions = attr.arrayDimensions;")
                         code.append("attr.value.arrayDimensions = attr.arrayDimensions;")
                 else:
                 else:
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
-    return [code, codeCleanup]
+    return [code, codeCleanup, codeGlobal]
 
 
-def generateVariableTypeNodeCode(node, nodeset, max_string_length, encode_binary_size):
+def generateVariableTypeNodeCode(node, nodeset, encode_binary_size):
     code = []
     code = []
     codeCleanup = []
     codeCleanup = []
+    codeGlobal = []
     code.append("UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;")
     code.append("UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;")
     if node.historizing:
     if node.historizing:
         code.append("attr.historizing = true;")
         code.append("attr.historizing = true;")
@@ -140,14 +143,15 @@ def generateVariableTypeNodeCode(node, nodeset, max_string_length, encode_binary
             code.append("attr.dataType = %s;" % generateNodeIdCode(dataTypeNode.id))
             code.append("attr.dataType = %s;" % generateNodeIdCode(dataTypeNode.id))
             if dataTypeNode.isEncodable():
             if dataTypeNode.isEncodable():
                 if node.value is not None:
                 if node.value is not None:
-                    [code1, codeCleanup1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, max_string_length, encode_binary_size)
+                    [code1, codeCleanup1, codeGlobal1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset, encode_binary_size)
                     code += code1
                     code += code1
                     codeCleanup += codeCleanup1
                     codeCleanup += codeCleanup1
+                    codeGlobal += codeGlobal1
                 else:
                 else:
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
                     code += generateValueCodeDummy(dataTypeNode, nodeset.nodes[node.id], nodeset)
-    return [code, codeCleanup]
+    return [code, codeCleanup, codeGlobal]
 
 
-def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0, arrayIndex=0, max_string_length=0, encode_binary_size=32000):
+def generateExtensionObjectSubtypeCode(node, parent, nodeset, global_var_code, recursionDepth=0, arrayIndex=0, encode_binary_size=32000):
     code = [""]
     code = [""]
     codeCleanup = [""]
     codeCleanup = [""]
 
 
@@ -185,8 +189,9 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
             "Encoding of field " + subv.alias + " is " + str(subv.encodingRule) + "defined by " + str(encField))
             "Encoding of field " + subv.alias + " is " + str(subv.encodingRule) + "defined by " + str(encField))
         # Check if this is an array
         # Check if this is an array
         if encField[2] == 0:
         if encField[2] == 0:
-            code.append(instanceName + "_struct." + subv.alias + " = " +
-                        generateNodeValueCode(subv, instanceName, asIndirect=False, max_string_length=max_string_length) + ";")
+            valueName = instanceName + "_struct." + subv.alias
+            code.append(generateNodeValueCode(valueName + " = " ,
+                        subv, instanceName,valueName, global_var_code, asIndirect=False))
         else:
         else:
             if isinstance(subv, list):
             if isinstance(subv, list):
                 # this is an array
                 # this is an array
@@ -199,8 +204,9 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
                 for subvidx in range(0, len(subv)):
                 for subvidx in range(0, len(subv)):
                     subvv = subv[subvidx]
                     subvv = subv[subvidx]
                     logger.debug("  " + str(subvidx) + " " + str(subvv))
                     logger.debug("  " + str(subvidx) + " " + str(subvv))
-                    code.append(instanceName + "_struct." + subv.alias + "[" + str(
-                        subvidx) + "] = " + generateNodeValueCode(subvv, instanceName, max_string_length=max_string_length) + ";")
+                    valueName = instanceName + "_struct." + subv.alias + "[" + str(
+                        subvidx) + "]"
+                    code.append(generateNodeValueCode(valueName + " = ", subvv, instanceName, valueName, global_var_code))
                 code.append("}")
                 code.append("}")
             else:
             else:
                 code.append(instanceName + "_struct." + subv.alias + "Size = 1;")
                 code.append(instanceName + "_struct." + subv.alias + "Size = 1;")
@@ -208,9 +214,9 @@ def generateExtensionObjectSubtypeCode(node, parent, nodeset, recursionDepth=0,
                     "{0}_struct.{1} = (UA_{2}*) UA_malloc(sizeof(UA_{2}));".format(
                     "{0}_struct.{1} = (UA_{2}*) UA_malloc(sizeof(UA_{2}));".format(
                         instanceName, subv.alias, subv.__class__.__name__))
                         instanceName, subv.alias, subv.__class__.__name__))
                 codeCleanup.append("UA_free({0}_struct.{1});".format(instanceName, subv.alias))
                 codeCleanup.append("UA_free({0}_struct.{1});".format(instanceName, subv.alias))
-
-                code.append(instanceName + "_struct." + subv.alias + "[0]  = " +
-                            generateNodeValueCode(subv, instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
+                valueName = instanceName + "_struct." + subv.alias + "[0]"
+                code.append(generateNodeValueCode(valueName + " = ",
+                            subv, instanceName, valueName, global_var_code, asIndirect=True))
 
 
     # Allocate some memory
     # Allocate some memory
     code.append("UA_ExtensionObject *" + instanceName + " =  UA_ExtensionObject_new();")
     code.append("UA_ExtensionObject *" + instanceName + " =  UA_ExtensionObject_new();")
@@ -295,9 +301,10 @@ def getTypesArrayForValue(nodeset, value):
     return "&" + typesArray + "[" + typesArray + "_" + \
     return "&" + typesArray + "[" + typesArray + "_" + \
                     value.__class__.__name__.upper() + "]"
                     value.__class__.__name__.upper() + "]"
 
 
-def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_length=0, encode_binary_size=32000):
+def generateValueCode(node, parentNode, nodeset, bootstrapping=True, encode_binary_size=32000):
     code = []
     code = []
     codeCleanup = []
     codeCleanup = []
+    codeGlobal = []
     valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
     valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
 
 
     # node.value either contains a list of multiple identical BUILTINTYPES, or it
     # node.value either contains a list of multiple identical BUILTINTYPES, or it
@@ -330,8 +337,8 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
             if isinstance(node.value[0], ExtensionObject):
             if isinstance(node.value[0], ExtensionObject):
                 for idx, v in enumerate(node.value):
                 for idx, v in enumerate(node.value):
                     logger.debug("Building extObj array index " + str(idx))
                     logger.debug("Building extObj array index " + str(idx))
-                    [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx, max_string_length=max_string_length,
-                                                                               encode_binary_size=encode_binary_size)
+                    [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx,
+                                                                               encode_binary_size=encode_binary_size, global_var_code=codeGlobal)
                     code = code + code1
                     code = code + code1
                     codeCleanup = codeCleanup + codeCleanup1
                     codeCleanup = codeCleanup + codeCleanup1
             code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
             code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
@@ -339,15 +346,15 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
                 for idx, v in enumerate(node.value):
                 for idx, v in enumerate(node.value):
                     logger.debug("Printing extObj array index " + str(idx))
                     logger.debug("Printing extObj array index " + str(idx))
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
-                    code.append(
-                        valueName + "[" + str(idx) + "] = " +
-                        generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
+                    code.append(generateNodeValueCode(
+                        valueName + "[" + str(idx) + "] = ",
+                        v, instanceName, valueName, codeGlobal))
                     # code.append("UA_free(&" +valueName + "[" + str(idx) + "]);")
                     # code.append("UA_free(&" +valueName + "[" + str(idx) + "]);")
             else:
             else:
                 for idx, v in enumerate(node.value):
                 for idx, v in enumerate(node.value):
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
                     instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
-                    code.append(
-                        valueName + "[" + str(idx) + "] = " + generateNodeValueCode(v, instanceName, max_string_length=max_string_length) + ";")
+                    code.append(generateNodeValueCode(
+                        valueName + "[" + str(idx) + "] = " , v, instanceName, valueName, codeGlobal))
             code.append("UA_Variant_setArray(&attr.value, &" + valueName +
             code.append("UA_Variant_setArray(&attr.value, &" + valueName +
                         ", (UA_Int32) " + str(len(node.value)) + ", " +
                         ", (UA_Int32) " + str(len(node.value)) + ", " +
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
@@ -362,14 +369,14 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
         else:
         else:
             # The following strategy applies to all other types, in particular strings and numerics.
             # The following strategy applies to all other types, in particular strings and numerics.
             if isinstance(node.value[0], ExtensionObject):
             if isinstance(node.value[0], ExtensionObject):
-                [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, max_string_length=max_string_length,
-                                                                           encode_binary_size=encode_binary_size)
+                [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset,
+                                                                           encode_binary_size=encode_binary_size, global_var_code=codeGlobal)
                 code = code + code1
                 code = code + code1
                 codeCleanup = codeCleanup + codeCleanup1
                 codeCleanup = codeCleanup + codeCleanup1
             instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
             instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
             if isinstance(node.value[0], ExtensionObject):
             if isinstance(node.value[0], ExtensionObject):
-                code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " +
-                            generateNodeValueCode(node.value[0], instanceName, max_string_length=max_string_length) + ";")
+                code.append(generateNodeValueCode("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " ,
+                            node.value[0], instanceName, valueName, codeGlobal))
                 code.append(
                 code.append(
                     "UA_Variant_setScalar(&attr.value, " + valueName + ", " +
                     "UA_Variant_setScalar(&attr.value, " + valueName + ", " +
                     getTypesArrayForValue(nodeset, node.value[0]) + ");")
                     getTypesArrayForValue(nodeset, node.value[0]) + ");")
@@ -379,13 +386,17 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
             else:
             else:
                 code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " =  UA_" + node.value[
                 code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " =  UA_" + node.value[
                     0].__class__.__name__ + "_new();")
                     0].__class__.__name__ + "_new();")
-                code.append("*" + valueName + " = " + generateNodeValueCode(node.value[0], instanceName, asIndirect=True, max_string_length=max_string_length) + ";")
+                code.append(generateNodeValueCode("*" + valueName + " = " , node.value[0], instanceName, valueName, codeGlobal, asIndirect=True))
                 code.append(
                 code.append(
                         "UA_Variant_setScalar(&attr.value, " + valueName + ", " +
                         "UA_Variant_setScalar(&attr.value, " + valueName + ", " +
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
                         getTypesArrayForValue(nodeset, node.value[0]) + ");")
+                if node.value[0].__class__.__name__ == "ByteString":
+                    # The data is on the stack, not heap, so we can not delete the ByteString
+                    codeCleanup.append("{}->data = NULL;".format(valueName))
+                    codeCleanup.append("{}->length = 0;".format(valueName))
                 codeCleanup.append("UA_{0}_delete({1});".format(
                 codeCleanup.append("UA_{0}_delete({1});".format(
                     node.value[0].__class__.__name__, valueName))
                     node.value[0].__class__.__name__, valueName))
-    return [code, codeCleanup]
+    return [code, codeCleanup, codeGlobal]
 
 
 def generateMethodNodeCode(node):
 def generateMethodNodeCode(node):
     code = []
     code = []
@@ -432,7 +443,7 @@ def generateSubtypeOfDefinitionCode(node):
             return generateNodeIdCode(ref.target)
             return generateNodeIdCode(ref.target)
     return "UA_NODEID_NULL"
     return "UA_NODEID_NULL"
 
 
-def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, parentref, encode_binary_size):
+def generateNodeCode_begin(node, nodeset, generate_ns0, parentref, encode_binary_size, code_global):
     code = []
     code = []
     codeCleanup = []
     codeCleanup = []
     code.append("UA_StatusCode retVal = UA_STATUSCODE_GOOD;")
     code.append("UA_StatusCode retVal = UA_STATUSCODE_GOOD;")
@@ -443,13 +454,15 @@ def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, paren
     elif isinstance(node, ObjectNode):
     elif isinstance(node, ObjectNode):
         code.extend(generateObjectNodeCode(node))
         code.extend(generateObjectNodeCode(node))
     elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
     elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
-        [code1, codeCleanup1] = generateVariableNodeCode(node, nodeset, max_string_length, encode_binary_size)
+        [code1, codeCleanup1, codeGlobal1] = generateVariableNodeCode(node, nodeset, encode_binary_size)
         code.extend(code1)
         code.extend(code1)
         codeCleanup.extend(codeCleanup1)
         codeCleanup.extend(codeCleanup1)
+        code_global.extend(codeGlobal1)
     elif isinstance(node, VariableTypeNode):
     elif isinstance(node, VariableTypeNode):
-        [code1, codeCleanup1] = generateVariableTypeNodeCode(node, nodeset, max_string_length, encode_binary_size)
+        [code1, codeCleanup1, codeGlobal1] = generateVariableTypeNodeCode(node, nodeset, encode_binary_size)
         code.extend(code1)
         code.extend(code1)
         codeCleanup.extend(codeCleanup1)
         codeCleanup.extend(codeCleanup1)
+        code_global.extend(codeGlobal1)
     elif isinstance(node, MethodNode):
     elif isinstance(node, MethodNode):
         code.extend(generateMethodNodeCode(node))
         code.extend(generateMethodNodeCode(node))
     elif isinstance(node, ObjectTypeNode):
     elif isinstance(node, ObjectTypeNode):
@@ -458,11 +471,9 @@ def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, paren
         code.extend(generateDataTypeNodeCode(node))
         code.extend(generateDataTypeNodeCode(node))
     elif isinstance(node, ViewNode):
     elif isinstance(node, ViewNode):
         code.extend(generateViewNodeCode(node))
         code.extend(generateViewNodeCode(node))
-    code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, alloc=False,
-                                                                  max_string_length=max_string_length) + ";")
+    code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, alloc=False) + ";")
     code.append("#ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS")
     code.append("#ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS")
-    code.append("attr.description = " + generateLocalizedTextCode(node.description, alloc=False,
-                                                                  max_string_length=max_string_length) + ";")
+    code.append("attr.description = " + generateLocalizedTextCode(node.description, alloc=False) + ";")
     code.append("#endif")
     code.append("#endif")
     code.append("attr.writeMask = %d;" % node.writeMask)
     code.append("attr.writeMask = %d;" % node.writeMask)
     code.append("attr.userWriteMask = %d;" % node.userWriteMask)
     code.append("attr.userWriteMask = %d;" % node.userWriteMask)
@@ -473,7 +484,7 @@ def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, paren
     code.append(generateNodeIdCode(node.id) + ",")
     code.append(generateNodeIdCode(node.id) + ",")
     code.append(generateNodeIdCode(parentref.target) + ",")
     code.append(generateNodeIdCode(parentref.target) + ",")
     code.append(generateNodeIdCode(parentref.referenceType) + ",")
     code.append(generateNodeIdCode(parentref.referenceType) + ",")
-    code.append(generateQualifiedNameCode(node.browseName, max_string_length=max_string_length) + ",")
+    code.append(generateQualifiedNameCode(node.browseName) + ",")
     if isinstance(node, VariableNode) or isinstance(node, ObjectNode):
     if isinstance(node, VariableNode) or isinstance(node, ObjectNode):
         typeDefRef = node.popTypeDef()
         typeDefRef = node.popTypeDef()
         code.append(generateNodeIdCode(typeDefRef.target) + ",")
         code.append(generateNodeIdCode(typeDefRef.target) + ",")
@@ -482,7 +493,7 @@ def generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, paren
     code.append("(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_{}ATTRIBUTES],NULL, NULL);".
     code.append("(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_{}ATTRIBUTES],NULL, NULL);".
                 format(node.__class__.__name__.upper().replace("NODE" ,"")))
                 format(node.__class__.__name__.upper().replace("NODE" ,"")))
     code.extend(codeCleanup)
     code.extend(codeCleanup)
-    
+
     return "\n".join(code)
     return "\n".join(code)
 
 
 def generateNodeCode_finish(node):
 def generateNodeCode_finish(node):

+ 1 - 7
tools/nodeset_compiler/nodeset_compiler.py

@@ -82,12 +82,6 @@ parser.add_argument('-t', '--types-array',
                     default=[],
                     default=[],
                     help='Types array for the given namespace. Can be used mutliple times to define (in the same order as the .xml files, first for --existing, then --xml) the type arrays')
                     help='Types array for the given namespace. Can be used mutliple times to define (in the same order as the .xml files, first for --existing, then --xml) the type arrays')
 
 
-parser.add_argument('--max-string-length',
-                    type=int,
-                    dest="max_string_length",
-                    default=0,
-                    help='Maximum allowed length of a string literal. If longer, it will be set to an empty string')
-
 parser.add_argument('--encode-binary-size',
 parser.add_argument('--encode-binary-size',
                     type=int,
                     type=int,
                     dest="encode_binary_size",
                     dest="encode_binary_size",
@@ -188,5 +182,5 @@ ns.allocateVariables()
 
 
 # Create the C code with the open62541 backend of the compiler
 # Create the C code with the open62541 backend of the compiler
 logger.info("Generating Code")
 logger.info("Generating Code")
-generateOpen62541Code(ns, args.outputFile, args.generate_ns0, args.internal_headers, args.typesArray, args.max_string_length, args.encode_binary_size)
+generateOpen62541Code(ns, args.outputFile, args.generate_ns0, args.internal_headers, args.typesArray, args.encode_binary_size)
 logger.info("NodeSet generation code successfully printed")
 logger.info("NodeSet generation code successfully printed")