Browse Source

Stack: Tag DataTypes with a "kind" for explicit dispatch in jumptables

Julius Pfrommer 5 years ago
parent
commit
c75efc25cb

+ 2 - 2
examples/custom_datatype/custom_datatype.h

@@ -39,11 +39,11 @@ static const UA_DataType PointType = {
         {1, UA_NODEIDTYPE_NUMERIC, {4242}}, /* .typeId */
         sizeof(Point),                   /* .memSize */
         0,                               /* .typeIndex, in the array of custom types */
-        3,                               /* .membersSize */
-        false,                           /* .builtin */
+        UA_DATATYPEKIND_STRUCTURE,       /* .typeKind */
         true,                            /* .pointerFree */
         false,                           /* .overlayable (depends on endianness and
                                          the absence of padding) */
+        3,                               /* .membersSize */
         0,                               /* .binaryEncodingId, the numeric
                                          identifier used on the wire (the
                                          namespaceindex is from .typeId) */

+ 48 - 12
include/ua_types.h

@@ -797,22 +797,58 @@ typedef struct {
     UA_Boolean isArray       : 1; /* The member is an array */
 } UA_DataTypeMember;
 
+/* The DataType "kind" is an internal type classification. It is used to
+ * dispatch handling to the correct routines. */
+#define UA_DATATYPEKINDS 31
+typedef enum {
+    UA_DATATYPEKIND_BOOLEAN = 0,
+    UA_DATATYPEKIND_SBYTE = 1,
+    UA_DATATYPEKIND_BYTE = 2,
+    UA_DATATYPEKIND_INT16 = 3,
+    UA_DATATYPEKIND_UINT16 = 4,
+    UA_DATATYPEKIND_INT32 = 5,
+    UA_DATATYPEKIND_UINT32 = 6,
+    UA_DATATYPEKIND_INT64 = 7,
+    UA_DATATYPEKIND_UINT64 = 8,
+    UA_DATATYPEKIND_FLOAT = 9,
+    UA_DATATYPEKIND_DOUBLE = 10,
+    UA_DATATYPEKIND_STRING = 11,
+    UA_DATATYPEKIND_DATETIME = 12,
+    UA_DATATYPEKIND_GUID = 13,
+    UA_DATATYPEKIND_BYTESTRING = 14,
+    UA_DATATYPEKIND_XMLELEMENT = 15,
+    UA_DATATYPEKIND_NODEID = 16,
+    UA_DATATYPEKIND_EXPANDEDNODEID = 17,
+    UA_DATATYPEKIND_STATUSCODE = 18,
+    UA_DATATYPEKIND_QUALIFIEDNAME = 19,
+    UA_DATATYPEKIND_LOCALIZEDTEXT = 20,
+    UA_DATATYPEKIND_EXTENSIONOBJECT = 21,
+    UA_DATATYPEKIND_DATAVALUE = 22,
+    UA_DATATYPEKIND_VARIANT = 23,
+    UA_DATATYPEKIND_DIAGNOSTICINFO = 24,
+    UA_DATATYPEKIND_DECIMAL = 25,
+    UA_DATATYPEKIND_ENUM = 26,
+    UA_DATATYPEKIND_STRUCTURE = 27,
+    UA_DATATYPEKIND_OPTSTRUCT = 28, /* struct with optional fields */
+    UA_DATATYPEKIND_UNION = 29,
+    UA_DATATYPEKIND_BITFIELDCLUSTER = 30 /* bitfields + padding */
+} UA_DataTypeKind;
+
 struct UA_DataType {
 #ifdef UA_ENABLE_TYPENAMES
     const char *typeName;
 #endif
-    UA_NodeId  typeId;           /* The nodeid of the type */
-    UA_UInt16  memSize;          /* Size of the struct in memory */
-    UA_UInt16  typeIndex;        /* Index of the type in the datatypetable */
-    UA_Byte    membersSize;      /* How many members does the type have? */
-    UA_Boolean builtin      : 1; /* The type is "builtin" and has dedicated de-
-                                    and encoding functions */
-    UA_Boolean pointerFree  : 1; /* The type (and its members) contains no
-                                    pointers that need to be freed */
-    UA_Boolean overlayable  : 1; /* The type has the identical memory layout in
-                                    memory and on the binary stream. */
-    UA_UInt16  binaryEncodingId; /* NodeId of datatype when encoded as binary */
-    //UA_UInt16  xmlEncodingId;  /* NodeId of datatype when encoded as XML */
+    UA_NodeId typeId;                /* The nodeid of the type */
+    UA_UInt16 memSize;               /* Size of the struct in memory */
+    UA_UInt16 typeIndex;             /* Index of the type in the datatypetable */
+    UA_UInt32 typeKind         : 6;  /* Dispatch index for the handling routines */
+    UA_UInt32 pointerFree      : 1;  /* The type (and its members) contains no
+                                      * pointers that need to be freed */
+    UA_UInt32 overlayable      : 1;  /* The type has the identical memory layout
+                                      * in memory and on the binary stream. */
+    UA_UInt32 membersSize      : 8;  /* How many members does the type have? */
+    UA_UInt32 binaryEncodingId : 16; /* NodeId of datatype when encoded as binary */
+    //UA_UInt16  xmlEncodingId;      /* NodeId of datatype when encoded as XML */
     UA_DataTypeMember *members;
 };
 

+ 8 - 17
src/server/ua_services_attribute.c

@@ -568,21 +568,12 @@ UA_Server_readObjectProperty(UA_Server *server, const UA_NodeId objectId,
 /* Type Checking */
 /*****************/
 
-enum type_equivalence {
-    TYPE_EQUIVALENCE_NONE,
-    TYPE_EQUIVALENCE_ENUM,
-    TYPE_EQUIVALENCE_OPAQUE
-};
-
-static enum type_equivalence
+static UA_DataTypeKind
 typeEquivalence(const UA_DataType *t) {
-    if(t->membersSize != 1 || !t->members[0].namespaceZero)
-        return TYPE_EQUIVALENCE_NONE;
-    if(t->members[0].memberTypeIndex == UA_TYPES_INT32)
-        return TYPE_EQUIVALENCE_ENUM;
-    if(t->members[0].memberTypeIndex == UA_TYPES_BYTE && t->members[0].isArray)
-        return TYPE_EQUIVALENCE_OPAQUE;
-    return TYPE_EQUIVALENCE_NONE;
+    UA_DataTypeKind k = (UA_DataTypeKind)t->typeKind;
+    if(k == UA_DATATYPEKIND_ENUM)
+        return UA_DATATYPEKIND_INT32;
+    return k;
 }
 
 static const UA_NodeId enumNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ENUMERATION}};
@@ -839,9 +830,9 @@ adjustValue(UA_Server *server, UA_Variant *value,
 
     /* An enum was sent as an int32, or an opaque type as a bytestring. This
      * is detected with the typeIndex indicating the "true" datatype. */
-    enum type_equivalence te1 = typeEquivalence(targetDataType);
-    enum type_equivalence te2 = typeEquivalence(value->type);
-    if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
+    UA_DataTypeKind te1 = typeEquivalence(targetDataType);
+    UA_DataTypeKind te2 = typeEquivalence(value->type);
+    if(te1 == te2 && te1 <= UA_DATATYPEKIND_ENUM) {
         value->type = targetDataType;
         return;
     }

+ 96 - 86
src/ua_types.c

@@ -37,6 +37,13 @@ const UA_Guid UA_GUID_NULL = {0, 0, 0, {0,0,0,0,0,0,0,0}};
 const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
 const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {{0, UA_NODEIDTYPE_NUMERIC, {0}}, {0, NULL}, 0};
 
+typedef UA_StatusCode (*UA_copySignature)(const void *src, void *dst,
+                                          const UA_DataType *type);
+typedef void (*UA_clearSignature)(void *p, const UA_DataType *type);
+
+extern const UA_copySignature copyJumpTable[UA_DATATYPEKINDS];
+extern const UA_clearSignature clearJumpTable[UA_DATATYPEKINDS];
+
 /* TODO: The standard-defined types are ordered. See if binary search is
  * more efficient. */
 const UA_DataType *
@@ -88,9 +95,6 @@ UA_UInt32_random(void) {
 /* Builtin Types */
 /*****************/
 
-static void clear_noInit(void *p, const UA_DataType *type);
-static UA_StatusCode copy_noInit(const void *src, void *dst, const UA_DataType *type);
-
 UA_String
 UA_String_fromChars(char const src[]) {
     UA_String str;
@@ -753,7 +757,7 @@ Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
     } else {
         for(size_t i = 0; i < block_count; ++i) {
             for(size_t j = 0; j < block; ++j) {
-                clear_noInit((void*)nextdst, v->type);
+                clearJumpTable[v->type->typeKind]((void*)nextdst, v->type);
                 retval |= UA_copy((void*)nextsrc, (void*)nextdst, v->type);
                 nextdst += elem_size;
                 nextsrc += elem_size;
@@ -887,53 +891,19 @@ copyGuid(const UA_Guid *src, UA_Guid *dst, const UA_DataType *_) {
     return UA_STATUSCODE_GOOD;
 }
 
-typedef UA_StatusCode
-(*UA_copySignature)(const void *src, void *dst, const UA_DataType *type);
-
-static const UA_copySignature copyJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (UA_copySignature)copyByte, // Boolean
-    (UA_copySignature)copyByte, // SByte
-    (UA_copySignature)copyByte, // Byte
-    (UA_copySignature)copy2Byte, // Int16
-    (UA_copySignature)copy2Byte, // UInt16
-    (UA_copySignature)copy4Byte, // Int32
-    (UA_copySignature)copy4Byte, // UInt32
-    (UA_copySignature)copy8Byte, // Int64
-    (UA_copySignature)copy8Byte, // UInt64
-    (UA_copySignature)copy4Byte, // Float
-    (UA_copySignature)copy8Byte, // Double
-    (UA_copySignature)String_copy,
-    (UA_copySignature)copy8Byte, // DateTime
-    (UA_copySignature)copyGuid, // Guid
-    (UA_copySignature)String_copy, // ByteString
-    (UA_copySignature)String_copy, // XmlElement
-    (UA_copySignature)NodeId_copy,
-    (UA_copySignature)ExpandedNodeId_copy,
-    (UA_copySignature)copy4Byte, // StatusCode
-    (UA_copySignature)QualifiedName_copy,
-    (UA_copySignature)LocalizedText_copy,
-    (UA_copySignature)ExtensionObject_copy,
-    (UA_copySignature)DataValue_copy,
-    (UA_copySignature)Variant_copy,
-    (UA_copySignature)DiagnosticInfo_copy,
-    (UA_copySignature)copy_noInit // all others
-};
-
 static UA_StatusCode
-copy_noInit(const void *src, void *dst, const UA_DataType *type) {
+copyStructure(const void *src, void *dst, const UA_DataType *type) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     uintptr_t ptrs = (uintptr_t)src;
     uintptr_t ptrd = (uintptr_t)dst;
-    u8 membersSize = type->membersSize;
-    for(size_t i = 0; i < membersSize; ++i) {
+    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+    for(size_t i = 0; i < type->membersSize; ++i) {
         const UA_DataTypeMember *m= &type->members[i];
-        const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
         const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
         if(!m->isArray) {
             ptrs += m->padding;
             ptrd += m->padding;
-            size_t fi = mt->builtin ? mt->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            retval |= copyJumpTable[fi]((const void*)ptrs, (void*)ptrd, mt);
+            retval |= copyJumpTable[mt->typeKind]((const void*)ptrs, (void*)ptrd, mt);
             ptrs += mt->memSize;
             ptrd += mt->memSize;
         } else {
@@ -955,61 +925,64 @@ copy_noInit(const void *src, void *dst, const UA_DataType *type) {
     return retval;
 }
 
+static UA_StatusCode
+copyNotImplemented(const void *src, void *dst, const UA_DataType *type) {
+    return UA_STATUSCODE_BADNOTIMPLEMENTED;
+}
+
+const UA_copySignature copyJumpTable[UA_DATATYPEKINDS] = {
+    (UA_copySignature)copyByte, /* Boolean */
+    (UA_copySignature)copyByte, /* SByte */
+    (UA_copySignature)copyByte, /* Byte */
+    (UA_copySignature)copy2Byte, /* Int16 */
+    (UA_copySignature)copy2Byte, /* UInt16 */
+    (UA_copySignature)copy4Byte, /* Int32 */
+    (UA_copySignature)copy4Byte, /* UInt32 */
+    (UA_copySignature)copy8Byte, /* Int64 */
+    (UA_copySignature)copy8Byte, /* UInt64 */
+    (UA_copySignature)copy4Byte, /* Float */
+    (UA_copySignature)copy8Byte, /* Double */
+    (UA_copySignature)String_copy,
+    (UA_copySignature)copy8Byte, /* DateTime */
+    (UA_copySignature)copyGuid, /* Guid */
+    (UA_copySignature)String_copy, /* ByteString */
+    (UA_copySignature)String_copy, /* XmlElement */
+    (UA_copySignature)NodeId_copy,
+    (UA_copySignature)ExpandedNodeId_copy,
+    (UA_copySignature)copy4Byte, /* StatusCode */
+    (UA_copySignature)QualifiedName_copy,
+    (UA_copySignature)LocalizedText_copy,
+    (UA_copySignature)ExtensionObject_copy,
+    (UA_copySignature)DataValue_copy,
+    (UA_copySignature)Variant_copy,
+    (UA_copySignature)DiagnosticInfo_copy,
+    (UA_copySignature)copyNotImplemented, /* Decimal */
+    (UA_copySignature)copy4Byte, /* Enumeration */
+    (UA_copySignature)copyStructure,
+    (UA_copySignature)copyNotImplemented, /* Structure with Optional Fields */
+    (UA_copySignature)copyNotImplemented, /* Union */
+    (UA_copySignature)copyNotImplemented /* BitfieldCluster*/
+};
+
 UA_StatusCode
 UA_copy(const void *src, void *dst, const UA_DataType *type) {
     memset(dst, 0, type->memSize); /* init */
-    UA_StatusCode retval = copy_noInit(src, dst, type);
+    UA_StatusCode retval = copyJumpTable[type->typeKind](src, dst, type);
     if(retval != UA_STATUSCODE_GOOD)
         UA_clear(dst, type);
     return retval;
 }
 
-static void nopClear(void *p, const UA_DataType *type) { }
-
-typedef void (*UA_clearSignature)(void *p, const UA_DataType *type);
-
-static const
-UA_clearSignature clearJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (UA_clearSignature)nopClear, // Boolean
-    (UA_clearSignature)nopClear, // SByte
-    (UA_clearSignature)nopClear, // Byte
-    (UA_clearSignature)nopClear, // Int16
-    (UA_clearSignature)nopClear, // UInt16
-    (UA_clearSignature)nopClear, // Int32
-    (UA_clearSignature)nopClear, // UInt32
-    (UA_clearSignature)nopClear, // Int64
-    (UA_clearSignature)nopClear, // UInt64
-    (UA_clearSignature)nopClear, // Float
-    (UA_clearSignature)nopClear, // Double
-    (UA_clearSignature)String_clear, // String
-    (UA_clearSignature)nopClear, // DateTime
-    (UA_clearSignature)nopClear, // Guid
-    (UA_clearSignature)String_clear, // ByteString
-    (UA_clearSignature)String_clear, // XmlElement
-    (UA_clearSignature)NodeId_clear,
-    (UA_clearSignature)ExpandedNodeId_clear,
-    (UA_clearSignature)nopClear, // StatusCode
-    (UA_clearSignature)QualifiedName_clear,
-    (UA_clearSignature)LocalizedText_clear,
-    (UA_clearSignature)ExtensionObject_clear,
-    (UA_clearSignature)DataValue_clear,
-    (UA_clearSignature)Variant_clear,
-    (UA_clearSignature)DiagnosticInfo_clear,
-    (UA_clearSignature)clear_noInit,
-};
-
 static void
-clear_noInit(void *p, const UA_DataType *type) {
+clearStructure(void *p, const UA_DataType *type) {
     uintptr_t ptr = (uintptr_t)p;
-    u8 membersSize = type->membersSize;
-    for(size_t i = 0; i < membersSize; ++i) {
-        const UA_DataTypeMember *m= &type->members[i];
-        const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+    for(size_t i = 0; i < type->membersSize; ++i) {
+        const UA_DataTypeMember *m = &type->members[i];
         const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
         if(!m->isArray) {
             ptr += m->padding;
-            size_t fi = mt->builtin ? mt->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            clearJumpTable[fi]((void*)ptr, mt);
+            clearJumpTable[mt->typeKind]((void*)ptr, mt);
             ptr += mt->memSize;
         } else {
             ptr += m->padding;
@@ -1021,15 +994,52 @@ clear_noInit(void *p, const UA_DataType *type) {
     }
 }
 
+static void nopClear(void *p, const UA_DataType *type) { }
+
+const
+UA_clearSignature clearJumpTable[UA_DATATYPEKINDS] = {
+    (UA_clearSignature)nopClear, /* Boolean */
+    (UA_clearSignature)nopClear, /* SByte */
+    (UA_clearSignature)nopClear, /* Byte */
+    (UA_clearSignature)nopClear, /* Int16 */
+    (UA_clearSignature)nopClear, /* UInt16 */
+    (UA_clearSignature)nopClear, /* Int32 */
+    (UA_clearSignature)nopClear, /* UInt32 */
+    (UA_clearSignature)nopClear, /* Int64 */
+    (UA_clearSignature)nopClear, /* UInt64 */
+    (UA_clearSignature)nopClear, /* Float */
+    (UA_clearSignature)nopClear, /* Double */
+    (UA_clearSignature)String_clear, /* String */
+    (UA_clearSignature)nopClear, /* DateTime */
+    (UA_clearSignature)nopClear, /* Guid */
+    (UA_clearSignature)String_clear, /* ByteString */
+    (UA_clearSignature)String_clear, /* XmlElement */
+    (UA_clearSignature)NodeId_clear,
+    (UA_clearSignature)ExpandedNodeId_clear,
+    (UA_clearSignature)nopClear, /* StatusCode */
+    (UA_clearSignature)QualifiedName_clear,
+    (UA_clearSignature)LocalizedText_clear,
+    (UA_clearSignature)ExtensionObject_clear,
+    (UA_clearSignature)DataValue_clear,
+    (UA_clearSignature)Variant_clear,
+    (UA_clearSignature)DiagnosticInfo_clear,
+    (UA_clearSignature)nopClear, /* Decimal, not implemented */
+    (UA_clearSignature)nopClear, /* Enumeration */
+    (UA_clearSignature)clearStructure,
+    (UA_clearSignature)nopClear, /* Struct with Optional Fields, not implemented*/
+    (UA_clearSignature)nopClear, /* Union, not implemented*/
+    (UA_clearSignature)nopClear /* BitfieldCluster, not implemented*/
+};
+
 void
 UA_clear(void *p, const UA_DataType *type) {
-    clear_noInit(p, type);
+    clearJumpTable[type->typeKind](p, type);
     memset(p, 0, type->memSize); /* init */
 }
 
 void
 UA_delete(void *p, const UA_DataType *type) {
-    clear_noInit(p, type);
+    clearJumpTable[type->typeKind](p, type);
     UA_free(p);
 }
 

+ 217 - 260
src/ua_types_encoding_binary.c

@@ -42,26 +42,32 @@ typedef struct {
     u8 *pos;
     const u8 *end;
 
-    u16 depth; /* How often did we en-/decoding recurse? */
+    u8 **oldpos; /* Sentinel for a lower stacktrace exchanging the buffer */
+    u16 depth;   /* How often did we en-/decoding recurse? */
 
     const UA_DataTypeArray *customTypes;
-
     UA_exchangeEncodeBuffer exchangeBufferCallback;
     void *exchangeBufferCallbackHandle;
 } Ctx;
 
-typedef status (*encodeBinarySignature)(const void *UA_RESTRICT src, const UA_DataType *type,
-                                        Ctx *UA_RESTRICT ctx);
-typedef status (*decodeBinarySignature)(void *UA_RESTRICT dst, const UA_DataType *type,
-                                        Ctx *UA_RESTRICT ctx);
-typedef size_t (*calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_DataType *contenttype);
-
-#define ENCODE_BINARY(TYPE) static status \
-    TYPE##_encodeBinary(const UA_##TYPE *UA_RESTRICT src, const UA_DataType *type, Ctx *UA_RESTRICT ctx)
-#define DECODE_BINARY(TYPE) static status \
-    TYPE##_decodeBinary(UA_##TYPE *UA_RESTRICT dst, const UA_DataType *type, Ctx *UA_RESTRICT ctx)
-#define CALCSIZE_BINARY(TYPE) static size_t \
-    TYPE##_calcSizeBinary(const UA_##TYPE *UA_RESTRICT src, const UA_DataType *_)
+typedef status
+(*encodeBinarySignature)(const void *UA_RESTRICT src, const UA_DataType *type,
+                         Ctx *UA_RESTRICT ctx);
+typedef status
+(*decodeBinarySignature)(void *UA_RESTRICT dst, const UA_DataType *type,
+                         Ctx *UA_RESTRICT ctx);
+typedef size_t
+(*calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_DataType *contenttype);
+
+#define ENCODE_BINARY(TYPE) static status                               \
+    TYPE##_encodeBinary(const UA_##TYPE *UA_RESTRICT src,               \
+                        const UA_DataType *type, Ctx *UA_RESTRICT ctx)
+#define DECODE_BINARY(TYPE) static status                               \
+    TYPE##_decodeBinary(UA_##TYPE *UA_RESTRICT dst,                     \
+                        const UA_DataType *type, Ctx *UA_RESTRICT ctx)
+#define CALCSIZE_BINARY(TYPE) static size_t                             \
+    TYPE##_calcSizeBinary(const UA_##TYPE *UA_RESTRICT src,             \
+                          const UA_DataType *_)
 #define ENCODE_DIRECT(SRC, TYPE) TYPE##_encodeBinary((const UA_##TYPE*)SRC, NULL, ctx)
 #define DECODE_DIRECT(DST, TYPE) TYPE##_decodeBinary((UA_##TYPE*)DST, NULL, ctx)
 
@@ -69,61 +75,42 @@ typedef size_t (*calcSizeBinarySignature)(const void *UA_RESTRICT p, const UA_Da
  * the decoding jumptable do not all clean up their allocated memory when an
  * error occurs. So a final _clear needs to be called before returning to the
  * user. */
-extern const encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
-extern const decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
-extern const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
-static status encodeBinaryInternal(const void *src, const UA_DataType *type, Ctx *ctx);
-static status decodeBinaryInternal(void *dst, const UA_DataType *type, Ctx *ctx);
+extern const encodeBinarySignature encodeBinaryJumpTable[UA_DATATYPEKINDS];
+extern const decodeBinarySignature decodeBinaryJumpTable[UA_DATATYPEKINDS];
+extern const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_DATATYPEKINDS];
 
-/**
- * Chunking
- * ^^^^^^^^
- * Breaking a message into chunks is integrated with the encoding. When the end
- * of a buffer is reached, a callback is executed that sends the current buffer
- * as a chunk and exchanges the encoding buffer "underneath" the ongoing
- * encoding. This reduces the RAM requirements and unnecessary copying.
- *
- * In encodeBinaryInternal and Array_encodeBinary, we store a pointer to the
- * last "good position" in the buffer. If we reach the end of the buffer, the
- * encoding until that point is sent out. Afterwards the "good position" pointer
- * is no longer valid. In order to prevent reuse, no method must return
- * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED after having called exchangeBuffer().
- * This needs to be ensured for the following methods:
- *
- * encodeBinaryInternal
- * Array_encodeBinary
- * NodeId_encodeBinary
- * ExpandedNodeId_encodeBinary
- * LocalizedText_encodeBinary
- * ExtensionObject_encodeBinary
- * Variant_encodeBinary
- * DataValue_encodeBinary
- * DiagnosticInfo_encodeBinary */
+/* Breaking a message up into chunks is integrated with the encoding. When the
+ * end of a buffer is reached, a callback is executed that sends the current
+ * buffer as a chunk and exchanges the encoding buffer "underneath" the ongoing
+ * encoding. This reduces the RAM requirements and unnecessary copying. */
 
 /* Send the current chunk and replace the buffer */
 static status exchangeBuffer(Ctx *ctx) {
     if(!ctx->exchangeBufferCallback)
         return UA_STATUSCODE_BADENCODINGERROR;
-    return ctx->exchangeBufferCallback(ctx->exchangeBufferCallbackHandle, &ctx->pos, &ctx->end);
+    return ctx->exchangeBufferCallback(ctx->exchangeBufferCallbackHandle,
+                                       &ctx->pos, &ctx->end);
 }
 
-/* If encoding fails, exchange the buffer and try again. It is assumed that the
- * following encoding never fails on a fresh buffer. This is true for numerical
- * types. */
+/* If encoding fails, exchange the buffer and try again. */
 static status
-encodeWithExchangeBuffer(const void *ptr, encodeBinarySignature encodeFunc, Ctx *ctx) {
-    status ret = encodeFunc(ptr, NULL, ctx);
-    if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) {
+encodeWithExchangeBuffer(const void *ptr, const UA_DataType *type, Ctx *ctx) {
+    u8 *oldpos = ctx->pos; /* Last known good position */
+    ctx->oldpos = &oldpos;
+    status ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx);
+    if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED && ctx->oldpos == &oldpos) {
+        ctx->pos = oldpos; /* Send the position to the last known good position
+                            * and switch */
         ret = exchangeBuffer(ctx);
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
-        ret = encodeFunc(ptr, NULL, ctx);
+        ret = encodeBinaryJumpTable[type->typeKind](ptr, type, ctx);
     }
     return ret;
 }
 
 #define ENCODE_WITHEXCHANGE(VAR, TYPE) \
-    encodeWithExchangeBuffer((const void*)VAR, (encodeBinarySignature)TYPE##_encodeBinary, ctx)
+    encodeWithExchangeBuffer((const void*)VAR, &UA_TYPES[TYPE], ctx)
 
 /*****************/
 /* Integer Types */
@@ -426,6 +413,9 @@ Array_encodeBinaryOverlayable(uintptr_t ptr, size_t length, size_t elementMemSiz
         ptr += possibleMem;
         finished += possible;
         status ret = exchangeBuffer(ctx);
+        ctx->oldpos = NULL; /* Set the sentinel so that no upper stack frame
+                             * with a saved pos attempts to exchange from an
+                             * invalid position in the old buffer. */
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
     }
@@ -438,29 +428,13 @@ Array_encodeBinaryOverlayable(uintptr_t ptr, size_t length, size_t elementMemSiz
 
 static status
 Array_encodeBinaryComplex(uintptr_t ptr, size_t length, const UA_DataType *type, Ctx *ctx) {
-    /* Get the encoding function for the data type. The jumptable at
-     * UA_BUILTIN_TYPES_COUNT points to the generic UA_encodeBinary method */
-    size_t encode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
-    encodeBinarySignature encodeType = encodeBinaryJumpTable[encode_index];
-
     /* Encode every element */
     for(size_t i = 0; i < length; ++i) {
-        u8 *oldpos = ctx->pos;
-        status ret = encodeType((const void*)ptr, type, ctx);
+        status ret = encodeWithExchangeBuffer((const void*)ptr, type, ctx);
         ptr += type->memSize;
 
-        /* Encoding failed, switch to the next chunk when possible */
-        if(ret != UA_STATUSCODE_GOOD) {
-            if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) {
-                ctx->pos = oldpos; /* Set buffer position to the end of the last encoded element */
-                ret = exchangeBuffer(ctx);
-                ptr -= type->memSize; /* Undo to retry encoding the ith element */
-                --i;
-            }
-            UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
-            if(ret != UA_STATUSCODE_GOOD)
-                return ret; /* Unrecoverable fail */
-        }
+        if(ret != UA_STATUSCODE_GOOD)
+            return ret; /* Unrecoverable fail */
     }
 
     return UA_STATUSCODE_GOOD;
@@ -478,7 +452,7 @@ Array_encodeBinary(const void *src, size_t length, const UA_DataType *type, Ctx
         signed_length = 0;
 
     /* Encode the array length */
-    status ret = ENCODE_WITHEXCHANGE(&signed_length, UInt32);
+    status ret = ENCODE_WITHEXCHANGE(&signed_length, UA_TYPES_INT32);
 
     /* Quit early? */
     if(ret != UA_STATUSCODE_GOOD || length == 0)
@@ -533,9 +507,8 @@ Array_decodeBinary(void *UA_RESTRICT *UA_RESTRICT dst, size_t *out_length,
     } else {
         /* Decode array members */
         uintptr_t ptr = (uintptr_t)*dst;
-        size_t decode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
         for(size_t i = 0; i < length; ++i) {
-            ret = decodeBinaryJumpTable[decode_index]((void*)ptr, type, ctx);
+            ret = decodeBinaryJumpTable[type->typeKind]((void*)ptr, type, ctx);
             if(ret != UA_STATUSCODE_GOOD) {
                 /* +1 because last element is also already initialized */
                 UA_Array_delete(*dst, i+1, type);
@@ -594,9 +567,7 @@ DECODE_BINARY(Guid) {
 #define UA_EXPANDEDNODEID_SERVERINDEX_FLAG 0x40
 #define UA_EXPANDEDNODEID_NAMESPACEURI_FLAG 0x80
 
-/* For ExpandedNodeId, we prefill the encoding mask. We can return
- * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED before encoding the string, as the
- * buffer is not replaced. */
+/* For ExpandedNodeId, we prefill the encoding mask. */
 static status
 NodeId_encodeBinaryWithEncodingMask(UA_NodeId const *src, u8 encoding, Ctx *ctx) {
     status ret = UA_STATUSCODE_GOOD;
@@ -722,19 +693,16 @@ ENCODE_BINARY(ExpandedNodeId) {
     if(ret != UA_STATUSCODE_GOOD)
         return ret;
 
-    /* Encode the namespace. Do not return
-     * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED afterwards. */
+    /* Encode the namespace. */
     if((void*)src->namespaceUri.data > UA_EMPTY_ARRAY_SENTINEL) {
         ret = ENCODE_DIRECT(&src->namespaceUri, String);
-        UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
     }
 
     /* Encode the serverIndex */
     if(src->serverIndex > 0)
-        ret = ENCODE_WITHEXCHANGE(&src->serverIndex, UInt32);
-    UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
+        ret = ENCODE_WITHEXCHANGE(&src->serverIndex, UA_TYPES_UINT32);
     return ret;
 }
 
@@ -792,7 +760,6 @@ ENCODE_BINARY(LocalizedText) {
         ret |= ENCODE_DIRECT(&src->locale, String);
     if(encoding & UA_LOCALIZEDTEXT_ENCODINGMASKTYPE_TEXT)
         ret |= ENCODE_DIRECT(&src->text, String);
-    UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
     return ret;
 }
 
@@ -849,13 +816,12 @@ UA_findDataTypeByBinary(const UA_NodeId *typeId) {
 ENCODE_BINARY(ExtensionObject) {
     u8 encoding = (u8)src->encoding;
 
-    /* No content or already encoded content. Do not return
-     * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED after encoding the NodeId. */
+    /* No content or already encoded content. */
     if(encoding <= UA_EXTENSIONOBJECT_ENCODED_XML) {
         status ret = ENCODE_DIRECT(&src->content.encoded.typeId, NodeId);
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
-        ret = ENCODE_WITHEXCHANGE(&encoding, Byte);
+        ret = ENCODE_WITHEXCHANGE(&encoding, UA_TYPES_BYTE);
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
         switch(src->encoding) {
@@ -902,7 +868,7 @@ ENCODE_BINARY(ExtensionObject) {
         return ret;
 
     /* Encode the content */
-    return encodeBinaryInternal(src->content.decoded.data, contentType, ctx);
+    return encodeWithExchangeBuffer(src->content.decoded.data, contentType, ctx);
 }
 
 static status
@@ -928,8 +894,7 @@ ExtensionObject_decodeBinaryContent(UA_ExtensionObject *dst, const UA_NodeId *ty
     /* Decode */
     dst->encoding = UA_EXTENSIONOBJECT_DECODED;
     dst->content.decoded.type = type;
-    size_t decode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
-    return decodeBinaryJumpTable[decode_index](dst->content.decoded.data, type, ctx);
+    return decodeBinaryJumpTable[type->typeKind](dst->content.decoded.data, type, ctx);
 }
 
 DECODE_BINARY(ExtensionObject) {
@@ -976,7 +941,6 @@ DECODE_BINARY(ExtensionObject) {
 
 /* Variant */
 
-/* Never returns UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED */
 static status
 Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const UA_Boolean isArray, Ctx *ctx) {
     /* Default to 1 for a scalar. */
@@ -1005,7 +969,7 @@ Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const UA_Boolean
     /* Iterate over the array */
     for(size_t i = 0; i < length && ret == UA_STATUSCODE_GOOD; ++i) {
         eo.content.decoded.data = (void*)ptr;
-        ret = encodeBinaryInternal(&eo, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], ctx);
+        ret = encodeWithExchangeBuffer(&eo, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], ctx);
         ptr += memSize;
     }
     return ret;
@@ -1024,13 +988,12 @@ ENCODE_BINARY(Variant) {
         return ENCODE_DIRECT(&encoding, Byte);
 
     /* Set the content type in the encoding mask */
-    const UA_Boolean isBuiltin = src->type->builtin;
-    const UA_Boolean isAlias = src->type->membersSize == 1
-                         && UA_TYPES[src->type->members[0].memberTypeIndex].builtin;
+    const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO);
+    const UA_Boolean isEnum = (src->type->typeKind == UA_DATATYPEKIND_ENUM);
     if(isBuiltin)
         encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(src->type->typeIndex + 1);
-    else if(isAlias)
-        encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(src->type->members[0].memberTypeIndex + 1);
+    else if(isEnum)
+        encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_INT32 + 1);
     else
         encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_EXTENSIONOBJECT + 1);
 
@@ -1049,10 +1012,10 @@ ENCODE_BINARY(Variant) {
         return ret;
 
     /* Encode the content */
-    if(!isBuiltin && !isAlias)
+    if(!isBuiltin && !isEnum)
         ret = Variant_encodeBinaryWrapExtensionObject(src, isArray, ctx);
     else if(!isArray)
-        ret = encodeBinaryInternal(src->data, src->type, ctx);
+        ret = encodeWithExchangeBuffer(src->data, src->type, ctx);
     else
         ret = Array_encodeBinary(src->data, src->arrayLength, src->type, ctx);
 
@@ -1102,8 +1065,7 @@ Variant_decodeBinaryUnwrapExtensionObject(UA_Variant *dst, Ctx *ctx) {
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     /* Decode the content */
-    size_t decode_index = dst->type->builtin ? dst->type->typeIndex : UA_BUILTIN_TYPES_COUNT;
-    return decodeBinaryJumpTable[decode_index](dst->data, dst->type, ctx);
+    return decodeBinaryJumpTable[dst->type->typeKind](dst->data, dst->type, ctx);
 }
 
 /* The resulting variant always has the storagetype UA_VARIANT_DATA. */
@@ -1175,9 +1137,7 @@ ENCODE_BINARY(DataValue) {
     if(ret != UA_STATUSCODE_GOOD)
         return ret;
 
-    /* Encode the variant. Afterwards, do not return
-     * UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED, as the buffer might have been
-     * exchanged during encoding of the variant. */
+    /* Encode the variant. */
     if(src->hasValue) {
         ret = ENCODE_DIRECT(&src->value, Variant);
         if(ret != UA_STATUSCODE_GOOD)
@@ -1185,16 +1145,15 @@ ENCODE_BINARY(DataValue) {
     }
 
     if(src->hasStatus)
-        ret |= ENCODE_WITHEXCHANGE(&src->status, UInt32);
+        ret |= ENCODE_WITHEXCHANGE(&src->status, UA_TYPES_STATUSCODE);
     if(src->hasSourceTimestamp)
-        ret |= ENCODE_WITHEXCHANGE(&src->sourceTimestamp, UInt64);
+        ret |= ENCODE_WITHEXCHANGE(&src->sourceTimestamp, UA_TYPES_DATETIME);
     if(src->hasSourcePicoseconds)
-        ret |= ENCODE_WITHEXCHANGE(&src->sourcePicoseconds, UInt16);
+        ret |= ENCODE_WITHEXCHANGE(&src->sourcePicoseconds, UA_TYPES_UINT16);
     if(src->hasServerTimestamp)
-        ret |= ENCODE_WITHEXCHANGE(&src->serverTimestamp, UInt64);
+        ret |= ENCODE_WITHEXCHANGE(&src->serverTimestamp, UA_TYPES_DATETIME);
     if(src->hasServerPicoseconds)
-        ret |= ENCODE_WITHEXCHANGE(&src->serverPicoseconds, UInt16);
-    UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
+        ret |= ENCODE_WITHEXCHANGE(&src->serverPicoseconds, UA_TYPES_UINT16);
     return ret;
 }
 
@@ -1212,7 +1171,6 @@ DECODE_BINARY(DataValue) {
         return UA_STATUSCODE_BADENCODINGERROR;
     ctx->depth++;
 
-
     /* Decode the content */
     if(encodingMask & 0x01) {
         dst->hasValue = true;
@@ -1276,22 +1234,18 @@ ENCODE_BINARY(DiagnosticInfo) {
             return ret;
     }
 
-    /* From here on, do not return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED, as
-     * the buffer might have been exchanged during encoding of the string. */
-
     /* Encode the inner status code */
     if(src->hasInnerStatusCode) {
-        ret = ENCODE_WITHEXCHANGE(&src->innerStatusCode, UInt32);
-        UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
+        ret = ENCODE_WITHEXCHANGE(&src->innerStatusCode, UA_TYPES_UINT32);
         if(ret != UA_STATUSCODE_GOOD)
             return ret;
     }
 
     /* Encode the inner diagnostic info */
     if(src->hasInnerDiagnosticInfo)
-        ret = encodeBinaryInternal(src->innerDiagnosticInfo, &UA_TYPES[UA_TYPES_DIAGNOSTICINFO], ctx);
+        ret = ENCODE_WITHEXCHANGE(&src->innerDiagnosticInfo,
+                                  UA_TYPES_DIAGNOSTICINFO);
 
-    UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
     return ret;
 }
 
@@ -1346,11 +1300,52 @@ DECODE_BINARY(DiagnosticInfo) {
     return ret;
 }
 
+static status
+encodeBinaryStruct(const void *src, const UA_DataType *type, Ctx *ctx) {
+    /* Check the recursion limit */
+    if(ctx->depth > UA_ENCODING_MAX_RECURSION)
+        return UA_STATUSCODE_BADENCODINGERROR;
+    ctx->depth++;
+
+    uintptr_t ptr = (uintptr_t)src;
+    status ret = UA_STATUSCODE_GOOD;
+    u8 membersSize = type->membersSize;
+    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+
+    /* Loop over members */
+    for(size_t i = 0; i < membersSize; ++i) {
+        const UA_DataTypeMember *m = &type->members[i];
+        const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
+        ptr += m->padding;
+
+        /* Array. Buffer-exchange is done inside Array_encodeBinary if required. */
+        if(m->isArray) {
+            const size_t length = *((const size_t*)ptr);
+            ptr += sizeof(size_t);
+            ret = Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, mt, ctx);
+            ptr += sizeof(void*);
+            continue;
+        }
+
+        /* Scalar */
+        ret = encodeWithExchangeBuffer((const void*)ptr, mt, ctx);
+        ptr += mt->memSize;
+    }
+
+    ctx->depth--;
+    return ret;
+}
+
+static status
+encodeBinaryNotImplemented(const void *src, const UA_DataType *type, Ctx *ctx) {
+    return UA_STATUSCODE_BADNOTIMPLEMENTED;
+}
+
 /********************/
 /* Structured Types */
 /********************/
 
-const encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+const encodeBinarySignature encodeBinaryJumpTable[UA_DATATYPEKINDS] = {
     (encodeBinarySignature)Boolean_encodeBinary,
     (encodeBinarySignature)Byte_encodeBinary, /* SByte */
     (encodeBinarySignature)Byte_encodeBinary,
@@ -1376,63 +1371,14 @@ const encodeBinarySignature encodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] =
     (encodeBinarySignature)DataValue_encodeBinary,
     (encodeBinarySignature)Variant_encodeBinary,
     (encodeBinarySignature)DiagnosticInfo_encodeBinary,
-    (encodeBinarySignature)encodeBinaryInternal,
+    (encodeBinarySignature)encodeBinaryNotImplemented, /* Decimal */
+    (encodeBinarySignature)UInt32_encodeBinary, /* Enumeration */
+    (encodeBinarySignature)encodeBinaryStruct,
+    (encodeBinarySignature)encodeBinaryNotImplemented, /* Structure with Optional Fields */
+    (encodeBinarySignature)encodeBinaryStruct, /* Union */
+    (encodeBinarySignature)encodeBinaryStruct /* BitfieldCluster */
 };
 
-static status
-encodeBinaryInternal(const void *src, const UA_DataType *type, Ctx *ctx) {
-    /* Check the recursion limit */
-    if(ctx->depth > UA_ENCODING_MAX_RECURSION)
-        return UA_STATUSCODE_BADENCODINGERROR;
-    ctx->depth++;
-
-    uintptr_t ptr = (uintptr_t)src;
-    status ret = UA_STATUSCODE_GOOD;
-    u8 membersSize = type->membersSize;
-    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-
-    /* Loop over members */
-    for(size_t i = 0; i < membersSize; ++i) {
-        const UA_DataTypeMember *member = &type->members[i];
-        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        ptr += member->padding;
-
-        /* Array. Buffer-exchange is done inside Array_encodeBinary if required. */
-        if(member->isArray) {
-            const size_t length = *((const size_t*)ptr);
-            ptr += sizeof(size_t);
-            ret = Array_encodeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype, ctx);
-            ptr += sizeof(void*);
-            continue;
-        }
-
-        /* Scalar */
-        size_t encode_index = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-        size_t memSize = membertype->memSize;
-        u8 *oldpos = ctx->pos;
-        ret = encodeBinaryJumpTable[encode_index]((const void*)ptr, membertype, ctx);
-        ptr += memSize;
-
-        /* Exchange/send the buffer */
-        if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED) {
-            ctx->pos = oldpos;
-            ret = exchangeBuffer(ctx);
-            if(ret == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED || ctx->pos + memSize > ctx->end) {
-                /* the send buffer is too small to encode the member, even after exchangeBuffer */
-                ret = UA_STATUSCODE_BADRESPONSETOOLARGE;
-                break;
-            }
-            /* Try to encode the same member in the next iteration on the new buffer */
-            ptr -= member->padding + memSize;
-            --i;
-        }
-    }
-
-    UA_assert(ret != UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
-    ctx->depth--;
-    return ret;
-}
-
 status
 UA_encodeBinary(const void *src, const UA_DataType *type,
                 u8 **bufPos, const u8 **bufEnd,
@@ -1445,11 +1391,11 @@ UA_encodeBinary(const void *src, const UA_DataType *type,
     ctx.exchangeBufferCallback = exchangeCallback;
     ctx.exchangeBufferCallbackHandle = exchangeHandle;
 
-    if (!ctx.pos)
+    if(!ctx.pos)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
 
     /* Encode */
-    status ret = encodeBinaryInternal(src, type, &ctx);
+    status ret = encodeWithExchangeBuffer(src, type, &ctx);
 
     /* Set the new buffer position for the output. Beware that the buffer might
      * have been exchanged internally. */
@@ -1458,37 +1404,13 @@ UA_encodeBinary(const void *src, const UA_DataType *type,
     return ret;
 }
 
-const decodeBinarySignature decodeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (decodeBinarySignature)Boolean_decodeBinary,
-    (decodeBinarySignature)Byte_decodeBinary, /* SByte */
-    (decodeBinarySignature)Byte_decodeBinary,
-    (decodeBinarySignature)UInt16_decodeBinary, /* Int16 */
-    (decodeBinarySignature)UInt16_decodeBinary,
-    (decodeBinarySignature)UInt32_decodeBinary, /* Int32 */
-    (decodeBinarySignature)UInt32_decodeBinary,
-    (decodeBinarySignature)UInt64_decodeBinary, /* Int64 */
-    (decodeBinarySignature)UInt64_decodeBinary,
-    (decodeBinarySignature)Float_decodeBinary,
-    (decodeBinarySignature)Double_decodeBinary,
-    (decodeBinarySignature)String_decodeBinary,
-    (decodeBinarySignature)UInt64_decodeBinary, /* DateTime */
-    (decodeBinarySignature)Guid_decodeBinary,
-    (decodeBinarySignature)String_decodeBinary, /* ByteString */
-    (decodeBinarySignature)String_decodeBinary, /* XmlElement */
-    (decodeBinarySignature)NodeId_decodeBinary,
-    (decodeBinarySignature)ExpandedNodeId_decodeBinary,
-    (decodeBinarySignature)UInt32_decodeBinary, /* StatusCode */
-    (decodeBinarySignature)QualifiedName_decodeBinary,
-    (decodeBinarySignature)LocalizedText_decodeBinary,
-    (decodeBinarySignature)ExtensionObject_decodeBinary,
-    (decodeBinarySignature)DataValue_decodeBinary,
-    (decodeBinarySignature)Variant_decodeBinary,
-    (decodeBinarySignature)DiagnosticInfo_decodeBinary,
-    (decodeBinarySignature)decodeBinaryInternal
-};
+static status
+decodeBinaryNotImplemented(void *dst, const UA_DataType *type, Ctx *ctx) {
+    return UA_STATUSCODE_BADNOTIMPLEMENTED;
+}
 
 static status
-decodeBinaryInternal(void *dst, const UA_DataType *type, Ctx *ctx) {
+decodeBinaryStructure(void *dst, const UA_DataType *type, Ctx *ctx) {
     /* Check the recursion limit */
     if(ctx->depth > UA_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
@@ -1515,16 +1437,48 @@ decodeBinaryInternal(void *dst, const UA_DataType *type, Ctx *ctx) {
         }
 
         /* Scalar */
-        size_t fi = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-        size_t memSize = membertype->memSize;
-        ret = decodeBinaryJumpTable[fi]((void *UA_RESTRICT)ptr, membertype, ctx);
-        ptr += memSize;
+        ret = decodeBinaryJumpTable[membertype->typeKind]((void *UA_RESTRICT)ptr, membertype, ctx);
+        ptr += membertype->memSize;
     }
 
     ctx->depth--;
     return ret;
 }
 
+const decodeBinarySignature decodeBinaryJumpTable[UA_DATATYPEKINDS] = {
+    (decodeBinarySignature)Boolean_decodeBinary,
+    (decodeBinarySignature)Byte_decodeBinary, /* SByte */
+    (decodeBinarySignature)Byte_decodeBinary,
+    (decodeBinarySignature)UInt16_decodeBinary, /* Int16 */
+    (decodeBinarySignature)UInt16_decodeBinary,
+    (decodeBinarySignature)UInt32_decodeBinary, /* Int32 */
+    (decodeBinarySignature)UInt32_decodeBinary,
+    (decodeBinarySignature)UInt64_decodeBinary, /* Int64 */
+    (decodeBinarySignature)UInt64_decodeBinary,
+    (decodeBinarySignature)Float_decodeBinary,
+    (decodeBinarySignature)Double_decodeBinary,
+    (decodeBinarySignature)String_decodeBinary,
+    (decodeBinarySignature)UInt64_decodeBinary, /* DateTime */
+    (decodeBinarySignature)Guid_decodeBinary,
+    (decodeBinarySignature)String_decodeBinary, /* ByteString */
+    (decodeBinarySignature)String_decodeBinary, /* XmlElement */
+    (decodeBinarySignature)NodeId_decodeBinary,
+    (decodeBinarySignature)ExpandedNodeId_decodeBinary,
+    (decodeBinarySignature)UInt32_decodeBinary, /* StatusCode */
+    (decodeBinarySignature)QualifiedName_decodeBinary,
+    (decodeBinarySignature)LocalizedText_decodeBinary,
+    (decodeBinarySignature)ExtensionObject_decodeBinary,
+    (decodeBinarySignature)DataValue_decodeBinary,
+    (decodeBinarySignature)Variant_decodeBinary,
+    (decodeBinarySignature)DiagnosticInfo_decodeBinary,
+    (decodeBinarySignature)decodeBinaryNotImplemented, /* Decimal */
+    (decodeBinarySignature)UInt32_decodeBinary, /* Enumeration */
+    (decodeBinarySignature)decodeBinaryStructure,
+    (decodeBinarySignature)decodeBinaryNotImplemented, /* Structure with optional fields */
+    (decodeBinarySignature)decodeBinaryNotImplemented, /* Union */
+    (decodeBinarySignature)decodeBinaryNotImplemented /* BitfieldCluster */
+};
+
 status
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
                 const UA_DataType *type, const UA_DataTypeArray *customTypes) {
@@ -1537,7 +1491,7 @@ UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
 
     /* Decode */
     memset(dst, 0, type->memSize); /* Initialize the value */
-    status ret = decodeBinaryInternal(dst, type, &ctx);
+    status ret = decodeBinaryJumpTable[type->typeKind](dst, type, &ctx);
 
     if(ret == UA_STATUSCODE_GOOD) {
         /* Set the new offset */
@@ -1564,9 +1518,8 @@ Array_calcSizeBinary(const void *src, size_t length, const UA_DataType *type) {
         return s;
     }
     uintptr_t ptr = (uintptr_t)src;
-    size_t encode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
     for(size_t i = 0; i < length; ++i) {
-        s += calcSizeBinaryJumpTable[encode_index]((const void*)ptr, type);
+        s += calcSizeBinaryJumpTable[type->typeKind]((const void*)ptr, type);
         ptr += type->memSize;
     }
     return s;
@@ -1661,8 +1614,7 @@ CALCSIZE_BINARY(ExtensionObject) {
     s += NodeId_calcSizeBinary(&src->content.decoded.type->typeId, NULL); /* Type encoding length */
     s += 4; /* Encoding length field */
     const UA_DataType *type = src->content.decoded.type;
-    size_t encode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
-    s += calcSizeBinaryJumpTable[encode_index](src->content.decoded.data, type); /* Encoding length */
+    s += calcSizeBinaryJumpTable[type->typeKind](src->content.decoded.data, type); /* Encoding length */
     return s;
 }
 
@@ -1671,31 +1623,22 @@ CALCSIZE_BINARY(Variant) {
     if(!src->type)
         return s;
 
-    UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
-    UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
-    UA_Boolean isBuiltin = src->type->builtin;
-
-
-    size_t encode_index = src->type->typeIndex;
-    if(!isBuiltin) {
-        encode_index = UA_BUILTIN_TYPES_COUNT;
-        if(src->type->typeId.identifierType != UA_NODEIDTYPE_NUMERIC)
-            return 0;
-    }
-
-    uintptr_t ptr = (uintptr_t)src->data;
-    size_t length = isArray ? src->arrayLength : 1;
-    if (isArray)
-        s += Array_calcSizeBinary((const void*)ptr, length, src->type);
+    const UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
+    if(isArray)
+        s += Array_calcSizeBinary(src->data, src->arrayLength, src->type);
     else
-        s += calcSizeBinaryJumpTable[encode_index]((const void*)ptr, src->type);
+        s += calcSizeBinaryJumpTable[src->type->typeKind](src->data, src->type);
 
-    if (!isBuiltin) {
+    const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO);
+    const UA_Boolean isEnum = (src->type->typeKind == UA_DATATYPEKIND_ENUM);
+    if(!isBuiltin && !isEnum) {
         /* The type is wrapped inside an extensionobject */
         /* (NodeId + encoding byte + extension object length) * array length */
+        size_t length = isArray ? src->arrayLength : 1;
         s += (NodeId_calcSizeBinary(&src->type->typeId, NULL) + 1 + 4) * length;
     }
 
+    const UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
     if(hasDimensions)
         s += Array_calcSizeBinary(src->arrayDimensions, src->arrayDimensionsSize,
                                   &UA_TYPES[UA_TYPES_INT32]);
@@ -1738,7 +1681,42 @@ CALCSIZE_BINARY(DiagnosticInfo) {
     return s;
 }
 
-const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
+static size_t
+calcSizeBinaryStructure(const void *p, const UA_DataType *type) {
+    size_t s = 0;
+    uintptr_t ptr = (uintptr_t)p;
+    u8 membersSize = type->membersSize;
+    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
+
+    /* Loop over members */
+    for(size_t i = 0; i < membersSize; ++i) {
+        const UA_DataTypeMember *member = &type->members[i];
+        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
+        ptr += member->padding;
+
+        /* Array */
+        if(member->isArray) {
+            const size_t length = *((const size_t*)ptr);
+            ptr += sizeof(size_t);
+            s += Array_calcSizeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype);
+            ptr += sizeof(void*);
+            continue;
+        }
+
+        /* Scalar */
+        s += calcSizeBinaryJumpTable[membertype->typeKind]((const void*)ptr, membertype);
+        ptr += membertype->memSize;
+    }
+
+    return s;
+}
+
+static size_t
+calcSizeBinaryNotImplemented(const void *p, const UA_DataType *type) {
+    return 0;
+}
+
+const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_DATATYPEKINDS] = {
     (calcSizeBinarySignature)calcSizeBinary1, /* Boolean */
     (calcSizeBinarySignature)calcSizeBinary1, /* SByte */
     (calcSizeBinarySignature)calcSizeBinary1, /* Byte */
@@ -1764,36 +1742,15 @@ const calcSizeBinarySignature calcSizeBinaryJumpTable[UA_BUILTIN_TYPES_COUNT + 1
     (calcSizeBinarySignature)DataValue_calcSizeBinary,
     (calcSizeBinarySignature)Variant_calcSizeBinary,
     (calcSizeBinarySignature)DiagnosticInfo_calcSizeBinary,
-    (calcSizeBinarySignature)UA_calcSizeBinary
+    (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Decimal */
+    (calcSizeBinarySignature)calcSizeBinary4, /* Enumeration */
+    (calcSizeBinarySignature)calcSizeBinaryStructure,
+    (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Structure with Optional Fields */
+    (calcSizeBinarySignature)calcSizeBinaryNotImplemented, /* Union */
+    (calcSizeBinarySignature)calcSizeBinaryNotImplemented /* BitfieldCluster */
 };
 
 size_t
 UA_calcSizeBinary(const void *p, const UA_DataType *type) {
-    size_t s = 0;
-    uintptr_t ptr = (uintptr_t)p;
-    u8 membersSize = type->membersSize;
-    const UA_DataType *typelists[2] = { UA_TYPES, &type[-type->typeIndex] };
-
-    /* Loop over members */
-    for(size_t i = 0; i < membersSize; ++i) {
-        const UA_DataTypeMember *member = &type->members[i];
-        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        ptr += member->padding;
-
-        /* Array */
-        if(member->isArray) {
-            const size_t length = *((const size_t*)ptr);
-            ptr += sizeof(size_t);
-            s += Array_calcSizeBinary(*(void *UA_RESTRICT const *)ptr, length, membertype);
-            ptr += sizeof(void*);
-            continue;
-        }
-
-        /* Scalar */
-        size_t encode_index = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-        s += calcSizeBinaryJumpTable[encode_index]((const void*)ptr, membertype);
-        ptr += membertype->memSize;
-    }
-
-    return s;
+    return calcSizeBinaryJumpTable[type->typeKind](p, type);
 }

+ 144 - 124
src/ua_types_encoding_json.c

@@ -80,8 +80,8 @@
 #define ENCODE_DIRECT_JSON(SRC, TYPE) \
     TYPE##_encodeJson((const UA_##TYPE*)SRC, NULL, ctx)
 
-extern const encodeJsonSignature encodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
-extern const decodeJsonSignature decodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1];
+extern const encodeJsonSignature encodeJsonJumpTable[UA_DATATYPEKINDS];
+extern const decodeJsonSignature decodeJsonJumpTable[UA_DATATYPEKINDS];
 
 /* Forward declarations */
 UA_String UA_DateTime_toJSON(UA_DateTime t);
@@ -508,8 +508,7 @@ ENCODE_JSON(Double) {
 static status
 encodeJsonArray(CtxJson *ctx, const void *ptr, size_t length,
                 const UA_DataType *type) {
-    size_t encode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
-    encodeJsonSignature encodeType = encodeJsonJumpTable[encode_index];
+    encodeJsonSignature encodeType = encodeJsonJumpTable[type->typeKind];
     status ret = writeJsonArrStart(ctx);
     uintptr_t uptr = (uintptr_t)ptr;
     for(size_t i = 0; i < length && ret == UA_STATUSCODE_GOOD; ++i) {
@@ -1170,9 +1169,8 @@ ENCODE_JSON(Variant) {
     }
         
     /* Set the content type in the encoding mask */
-    const bool isBuiltin = src->type->builtin;
-    const bool isAlias = src->type->membersSize == 1
-            && UA_TYPES[src->type->members[0].memberTypeIndex].builtin;
+    const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO);
+    const UA_Boolean isEnum = (src->type->typeKind == UA_DATATYPEKIND_ENUM);
     
     /* Set the array type in the encoding mask */
     const bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
@@ -1185,7 +1183,7 @@ ENCODE_JSON(Variant) {
             return ret;
 
         /* Encode the content */
-        if(!isBuiltin && !isAlias) {
+        if(!isBuiltin && !isEnum) {
             /* REVERSIBLE:  NOT BUILTIN, can it be encoded? Wrap in extension object.*/
             ret |= writeJsonKey(ctx, UA_JSONKEY_TYPE);
             ret |= ENCODE_DIRECT_JSON(&UA_TYPES[UA_TYPES_EXTENSIONOBJECT].typeId.identifier.numeric, UInt32);
@@ -1229,7 +1227,7 @@ ENCODE_JSON(Variant) {
      */
 
     ret |= writeJsonObjStart(ctx);
-    if(!isBuiltin && !isAlias) {
+    if(!isBuiltin && !isEnum) {
         /*NON REVERSIBLE:  NOT BUILTIN, can it be encoded? Wrap in extension object.*/
         if(src->arrayDimensionsSize > 1) {
             return UA_STATUSCODE_BADNOTIMPLEMENTED;
@@ -1385,78 +1383,89 @@ ENCODE_JSON(DiagnosticInfo) {
     return ret;
 }
 
-const encodeJsonSignature encodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (encodeJsonSignature) Boolean_encodeJson,
-    (encodeJsonSignature) SByte_encodeJson, /* SByte */
-    (encodeJsonSignature) Byte_encodeJson,
-    (encodeJsonSignature) Int16_encodeJson, /* Int16 */
-    (encodeJsonSignature) UInt16_encodeJson,
-    (encodeJsonSignature) Int32_encodeJson, /* Int32 */
-    (encodeJsonSignature) UInt32_encodeJson,
-    (encodeJsonSignature) Int64_encodeJson, /* Int64 */
-    (encodeJsonSignature) UInt64_encodeJson,
-    (encodeJsonSignature) Float_encodeJson,
-    (encodeJsonSignature) Double_encodeJson,
-    (encodeJsonSignature) String_encodeJson,
-    (encodeJsonSignature) DateTime_encodeJson, /* DateTime */
-    (encodeJsonSignature) Guid_encodeJson,
-    (encodeJsonSignature) ByteString_encodeJson, /* ByteString */
-    (encodeJsonSignature) String_encodeJson, /* XmlElement */
-    (encodeJsonSignature) NodeId_encodeJson,
-    (encodeJsonSignature) ExpandedNodeId_encodeJson,
-    (encodeJsonSignature) StatusCode_encodeJson, /* StatusCode */
-    (encodeJsonSignature) QualifiedName_encodeJson, /* QualifiedName */
-    (encodeJsonSignature) LocalizedText_encodeJson,
-    (encodeJsonSignature) ExtensionObject_encodeJson,
-    (encodeJsonSignature) DataValue_encodeJson,
-    (encodeJsonSignature) Variant_encodeJson,
-    (encodeJsonSignature) DiagnosticInfo_encodeJson,
-    (encodeJsonSignature) encodeJsonInternal,
-};
-
-status
-encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx) {
+static status
+encodeJsonStructure(const void *src, const UA_DataType *type, CtxJson *ctx) {
     /* Check the recursion limit */
     if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
     ctx->depth++;
 
-    status ret = UA_STATUSCODE_GOOD; 
-    if(!type->builtin)
-        ret |= writeJsonObjStart(ctx);
+    status ret = writeJsonObjStart(ctx);
 
     uintptr_t ptr = (uintptr_t) src;
     u8 membersSize = type->membersSize;
     const UA_DataType * typelists[2] = {UA_TYPES, &type[-type->typeIndex]};
     for(size_t i = 0; i < membersSize && ret == UA_STATUSCODE_GOOD; ++i) {
-        const UA_DataTypeMember *member = &type->members[i];
-        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
+        const UA_DataTypeMember *m = &type->members[i];
+        const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
 
-        if(member->memberName != NULL && *member->memberName != 0)
-            ret |= writeJsonKey(ctx, member->memberName);
+        if(m->memberName != NULL && *m->memberName != 0)
+            ret |= writeJsonKey(ctx, m->memberName);
 
-        if(!member->isArray) {
-            ptr += member->padding;
-            size_t encode_index = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            size_t memSize = membertype->memSize;
-            ret = encodeJsonJumpTable[encode_index]((const void*) ptr, membertype, ctx);
+        if(!m->isArray) {
+            ptr += m->padding;
+            size_t memSize = mt->memSize;
+            ret = encodeJsonJumpTable[mt->typeKind]((const void*) ptr, mt, ctx);
             ptr += memSize;
         } else {
-            ptr += member->padding;
+            ptr += m->padding;
             const size_t length = *((const size_t*) ptr);
             ptr += sizeof (size_t);
-            ret = encodeJsonArray(ctx, *(void * const *) ptr, length, membertype);
+            ret = encodeJsonArray(ctx, *(void * const *)ptr, length, mt);
             ptr += sizeof (void*);
         }
     }
 
-    if(!type->builtin)
-        ret |= writeJsonObjEnd(ctx);
+    ret |= writeJsonObjEnd(ctx);
 
     ctx->depth--;
     return ret;
 }
 
+static status
+encodeJsonNotImplemented(const void *src, const UA_DataType *type, CtxJson *ctx) {
+    return UA_STATUSCODE_BADNOTIMPLEMENTED;
+}
+
+const encodeJsonSignature encodeJsonJumpTable[UA_DATATYPEKINDS] = {
+    (encodeJsonSignature)Boolean_encodeJson,
+    (encodeJsonSignature)SByte_encodeJson, /* SByte */
+    (encodeJsonSignature)Byte_encodeJson,
+    (encodeJsonSignature)Int16_encodeJson, /* Int16 */
+    (encodeJsonSignature)UInt16_encodeJson,
+    (encodeJsonSignature)Int32_encodeJson, /* Int32 */
+    (encodeJsonSignature)UInt32_encodeJson,
+    (encodeJsonSignature)Int64_encodeJson, /* Int64 */
+    (encodeJsonSignature)UInt64_encodeJson,
+    (encodeJsonSignature)Float_encodeJson,
+    (encodeJsonSignature)Double_encodeJson,
+    (encodeJsonSignature)String_encodeJson,
+    (encodeJsonSignature)DateTime_encodeJson, /* DateTime */
+    (encodeJsonSignature)Guid_encodeJson,
+    (encodeJsonSignature)ByteString_encodeJson, /* ByteString */
+    (encodeJsonSignature)String_encodeJson, /* XmlElement */
+    (encodeJsonSignature)NodeId_encodeJson,
+    (encodeJsonSignature)ExpandedNodeId_encodeJson,
+    (encodeJsonSignature)StatusCode_encodeJson, /* StatusCode */
+    (encodeJsonSignature)QualifiedName_encodeJson, /* QualifiedName */
+    (encodeJsonSignature)LocalizedText_encodeJson,
+    (encodeJsonSignature)ExtensionObject_encodeJson,
+    (encodeJsonSignature)DataValue_encodeJson,
+    (encodeJsonSignature)Variant_encodeJson,
+    (encodeJsonSignature)DiagnosticInfo_encodeJson,
+    (encodeJsonSignature)encodeJsonNotImplemented, /* Decimal */
+    (encodeJsonSignature)encodeJsonNotImplemented, /* Enum */
+    (encodeJsonSignature)encodeJsonStructure,
+    (encodeJsonSignature)encodeJsonNotImplemented, /* Structure with optional fields */
+    (encodeJsonSignature)encodeJsonNotImplemented, /* Union */
+    (encodeJsonSignature)encodeJsonNotImplemented /* BitfieldCluster */
+};
+
+status
+encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx) {
+    return encodeJsonJumpTable[type->typeKind](src, type, ctx);
+}
+
 status UA_FUNC_ATTR_WARN_UNUSED_RESULT
 UA_encodeJson(const void *src, const UA_DataType *type,
               u8 **bufPos, const u8 **bufEnd, UA_String *namespaces, 
@@ -1479,7 +1488,7 @@ UA_encodeJson(const void *src, const UA_DataType *type,
     ctx.calcOnly = false;
     
     /* Encode */
-    status ret = encodeJsonInternal(src, type, &ctx);
+    status ret = encodeJsonJumpTable[type->typeKind](src, type, &ctx);
     
     *bufPos = ctx.pos;
     *bufEnd = ctx.end;
@@ -1511,7 +1520,7 @@ UA_calcSizeJson(const void *src, const UA_DataType *type,
     ctx.calcOnly = true;
 
     /* Encode */
-    status ret = encodeJsonInternal(src, type, &ctx);
+    status ret = encodeJsonJumpTable[type->typeKind](src, type, &ctx);
     if(ret != UA_STATUSCODE_GOOD)
         return 0;
     return (size_t)ctx.pos;
@@ -2892,11 +2901,11 @@ DECODE_JSON(ExtensionObject) {
             if(!dst->content.decoded.data)
                 return UA_STATUSCODE_BADOUTOFMEMORY;
 
-            size_t decode_index = typeOfBody->builtin ? typeOfBody->typeIndex : UA_BUILTIN_TYPES_COUNT;
             UA_NodeId typeId_dummy;
             DecodeEntry entries[2] = {
                 {UA_JSONKEY_TYPEID, &typeId_dummy, (decodeJsonSignature) NodeId_decodeJson, false, NULL},
-                {UA_JSONKEY_BODY, dst->content.decoded.data, (decodeJsonSignature) decodeJsonJumpTable[decode_index], false, NULL}
+                {UA_JSONKEY_BODY, dst->content.decoded.data,
+                 (decodeJsonSignature) decodeJsonJumpTable[typeOfBody->typeKind], false, NULL}
             };
 
             return decodeFields(ctx, parseCtx, entries, 2, typeOfBody);
@@ -3004,12 +3013,12 @@ Variant_decodeJsonUnwrapExtensionObject(UA_Variant *dst, const UA_DataType *type
         }
 
         /* Decode the content */
-        size_t decode_index = dst->type->builtin ? dst->type->typeIndex : UA_BUILTIN_TYPES_COUNT;
         UA_NodeId nodeIddummy;
         DecodeEntry entries[3] =
             {
              {UA_JSONKEY_TYPEID, &nodeIddummy, (decodeJsonSignature) NodeId_decodeJson, false, NULL},
-             {UA_JSONKEY_BODY, dst->data, (decodeJsonSignature) decodeJsonJumpTable[decode_index], false, NULL},
+             {UA_JSONKEY_BODY, dst->data,
+              (decodeJsonSignature) decodeJsonJumpTable[dst->type->typeKind], false, NULL},
              {UA_JSONKEY_ENCODING, NULL, NULL, false, NULL}};
 
         ret = decodeFields(ctx, parseCtx, entries, encodingFound ? 3:2, typeOfBody);
@@ -3143,39 +3152,6 @@ decodeFields(CtxJson *ctx, ParseCtx *parseCtx, DecodeEntry *entries,
     return ret;
 }
 
-decodeJsonSignature getDecodeSignature(u8 index) {
-    return decodeJsonJumpTable[index];
-}
-
-const decodeJsonSignature decodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
-    (decodeJsonSignature)Boolean_decodeJson,
-    (decodeJsonSignature)SByte_decodeJson, /* SByte */
-    (decodeJsonSignature)Byte_decodeJson,
-    (decodeJsonSignature)Int16_decodeJson, /* Int16 */
-    (decodeJsonSignature)UInt16_decodeJson,
-    (decodeJsonSignature)Int32_decodeJson, /* Int32 */
-    (decodeJsonSignature)UInt32_decodeJson,
-    (decodeJsonSignature)Int64_decodeJson, /* Int64 */
-    (decodeJsonSignature)UInt64_decodeJson,
-    (decodeJsonSignature)Float_decodeJson,
-    (decodeJsonSignature)Double_decodeJson,
-    (decodeJsonSignature)String_decodeJson,
-    (decodeJsonSignature)DateTime_decodeJson, /* DateTime */
-    (decodeJsonSignature)Guid_decodeJson,
-    (decodeJsonSignature)ByteString_decodeJson, /* ByteString */
-    (decodeJsonSignature)String_decodeJson, /* XmlElement */
-    (decodeJsonSignature)NodeId_decodeJson,
-    (decodeJsonSignature)ExpandedNodeId_decodeJson,
-    (decodeJsonSignature)StatusCode_decodeJson, /* StatusCode */
-    (decodeJsonSignature)QualifiedName_decodeJson, /* QualifiedName */
-    (decodeJsonSignature)LocalizedText_decodeJson,
-    (decodeJsonSignature)ExtensionObject_decodeJson,
-    (decodeJsonSignature)DataValue_decodeJson,
-    (decodeJsonSignature)Variant_decodeJson,
-    (decodeJsonSignature)DiagnosticInfo_decodeJson,
-    (decodeJsonSignature)decodeJsonInternal
-};
-
 static status
 Array_decodeJson_internal(void **dst, const UA_DataType *type, 
         CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
@@ -3205,9 +3181,8 @@ Array_decodeJson_internal(void **dst, const UA_DataType *type,
     
     /* Decode array members */
     uintptr_t ptr = (uintptr_t)*dst;
-    size_t decode_index = type->builtin ? type->typeIndex : UA_BUILTIN_TYPES_COUNT;
     for(size_t i = 0; i < length; ++i) {
-        ret = decodeJsonJumpTable[decode_index]((void*)ptr, type, ctx, parseCtx, true);
+        ret = decodeJsonJumpTable[type->typeKind]((void*)ptr, type, ctx, parseCtx, true);
         if(ret != UA_STATUSCODE_GOOD) {
             UA_Array_delete(*dst, i+1, type);
             *dst = NULL;
@@ -3225,9 +3200,9 @@ Array_decodeJson(void * dst, const UA_DataType *type, CtxJson *ctx,
     return Array_decodeJson_internal((void **)dst, type, ctx, parseCtx, moveToken);
 }
 
-status
-decodeJsonInternal(void *dst, const UA_DataType *type, CtxJson *ctx, 
-        ParseCtx *parseCtx, UA_Boolean moveToken) {
+static status
+decodeJsonStructure(void *dst, const UA_DataType *type, CtxJson *ctx, 
+                    ParseCtx *parseCtx, UA_Boolean moveToken) {
     /* Check the recursion limit */
     if(ctx->depth > UA_JSON_ENCODING_MAX_RECURSION)
         return UA_STATUSCODE_BADENCODINGERROR;
@@ -3241,31 +3216,24 @@ decodeJsonInternal(void *dst, const UA_DataType *type, CtxJson *ctx,
     UA_STACKARRAY(DecodeEntry, entries, membersSize);
 
     for(size_t i = 0; i < membersSize && ret == UA_STATUSCODE_GOOD; ++i) {
-        const UA_DataTypeMember *member = &type->members[i];
-        const UA_DataType *membertype = &typelists[!member->namespaceZero][member->memberTypeIndex];
-        if(!member->isArray) {
-            ptr += member->padding;
-            size_t fi = membertype->builtin ? membertype->typeIndex : UA_BUILTIN_TYPES_COUNT;
-            size_t memSize = membertype->memSize;
-            
-            /*Setup the decoding functions, field names and destinations*/
-            entries[i].fieldName = member->memberName;
+        const UA_DataTypeMember *m = &type->members[i];
+        const UA_DataType *mt = &typelists[!m->namespaceZero][m->memberTypeIndex];
+
+        entries[i].type = mt;
+        if(!m->isArray) {
+            ptr += m->padding;
+            entries[i].fieldName = m->memberName;
             entries[i].fieldPointer = (void*)ptr;
-            entries[i].function = decodeJsonJumpTable[fi];
+            entries[i].function = decodeJsonJumpTable[mt->typeKind];
             entries[i].found = false;
-            entries[i].type = membertype;
-
-            ptr += memSize;
+            ptr += mt->memSize;
         } else {
-            ptr += member->padding;
-            ptr += sizeof(size_t); /* length is filled in Array_decodeJson */
-            
-            entries[i].fieldName = member->memberName;
+            ptr += m->padding;
+            ptr += sizeof(size_t);
+            entries[i].fieldName = m->memberName;
             entries[i].fieldPointer = (void*)ptr;
             entries[i].function = (decodeJsonSignature)Array_decodeJson;
             entries[i].found = false;
-            entries[i].type = membertype;
-
             ptr += sizeof(void*);
         }
     }
@@ -3276,6 +3244,50 @@ decodeJsonInternal(void *dst, const UA_DataType *type, CtxJson *ctx,
     return ret;
 }
 
+static status
+decodeJsonNotImplemented(void *dst, const UA_DataType *type, CtxJson *ctx, 
+                         ParseCtx *parseCtx, UA_Boolean moveToken) {
+    return UA_STATUSCODE_BADNOTIMPLEMENTED;
+}
+
+const decodeJsonSignature decodeJsonJumpTable[UA_DATATYPEKINDS] = {
+    (decodeJsonSignature)Boolean_decodeJson,
+    (decodeJsonSignature)SByte_decodeJson, /* SByte */
+    (decodeJsonSignature)Byte_decodeJson,
+    (decodeJsonSignature)Int16_decodeJson, /* Int16 */
+    (decodeJsonSignature)UInt16_decodeJson,
+    (decodeJsonSignature)Int32_decodeJson, /* Int32 */
+    (decodeJsonSignature)UInt32_decodeJson,
+    (decodeJsonSignature)Int64_decodeJson, /* Int64 */
+    (decodeJsonSignature)UInt64_decodeJson,
+    (decodeJsonSignature)Float_decodeJson,
+    (decodeJsonSignature)Double_decodeJson,
+    (decodeJsonSignature)String_decodeJson,
+    (decodeJsonSignature)DateTime_decodeJson, /* DateTime */
+    (decodeJsonSignature)Guid_decodeJson,
+    (decodeJsonSignature)ByteString_decodeJson, /* ByteString */
+    (decodeJsonSignature)String_decodeJson, /* XmlElement */
+    (decodeJsonSignature)NodeId_decodeJson,
+    (decodeJsonSignature)ExpandedNodeId_decodeJson,
+    (decodeJsonSignature)StatusCode_decodeJson, /* StatusCode */
+    (decodeJsonSignature)QualifiedName_decodeJson, /* QualifiedName */
+    (decodeJsonSignature)LocalizedText_decodeJson,
+    (decodeJsonSignature)ExtensionObject_decodeJson,
+    (decodeJsonSignature)DataValue_decodeJson,
+    (decodeJsonSignature)Variant_decodeJson,
+    (decodeJsonSignature)DiagnosticInfo_decodeJson,
+    (decodeJsonSignature)decodeJsonNotImplemented, /* Decimal */
+    (decodeJsonSignature)decodeJsonNotImplemented, /* Enum */
+    (decodeJsonSignature)decodeJsonStructure,
+    (decodeJsonSignature)decodeJsonNotImplemented, /* Structure with optional fields */
+    (decodeJsonSignature)decodeJsonNotImplemented, /* Union */
+    (decodeJsonSignature)decodeJsonNotImplemented /* BitfieldCluster */
+};
+
+decodeJsonSignature getDecodeSignature(u8 index) {
+    return decodeJsonJumpTable[index];
+}
+
 status
 tokenize(ParseCtx *parseCtx, CtxJson *ctx, const UA_ByteString *src) {
     /* Set up the context */
@@ -3288,8 +3300,9 @@ tokenize(ParseCtx *parseCtx, CtxJson *ctx, const UA_ByteString *src) {
     /*Set up tokenizer jsmn*/
     jsmn_parser p;
     jsmn_init(&p);
-    parseCtx->tokenCount = (UA_Int32)jsmn_parse(&p, (char*)src->data,
-                                                src->length, parseCtx->tokenArray, TOKENCOUNT);
+    parseCtx->tokenCount = (UA_Int32)
+        jsmn_parse(&p, (char*)src->data, src->length,
+                   parseCtx->tokenArray, TOKENCOUNT);
     
     if(parseCtx->tokenCount < 0) {
         if(parseCtx->tokenCount == JSMN_ERROR_NOMEM)
@@ -3300,6 +3313,13 @@ tokenize(ParseCtx *parseCtx, CtxJson *ctx, const UA_ByteString *src) {
     return UA_STATUSCODE_GOOD;
 }
 
+UA_StatusCode
+decodeJsonInternal(void *dst, const UA_DataType *type,
+                   CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
+
+    return decodeJsonJumpTable[type->typeKind](dst, type, ctx, parseCtx, moveToken);
+}
+
 status UA_FUNC_ATTR_WARN_UNUSED_RESULT
 UA_decodeJson(const UA_ByteString *src, void *dst, const UA_DataType *type) {
     
@@ -3327,9 +3347,9 @@ UA_decodeJson(const UA_ByteString *src, void *dst, const UA_DataType *type) {
         if(parseCtx.tokenCount == 1) {
             if(parseCtx.tokenArray[0].type == JSMN_PRIMITIVE ||
                parseCtx.tokenArray[0].type == JSMN_STRING) {
-               /*Only a primitive to parse. Do it directly.*/
+               /* Only a primitive to parse. Do it directly. */
                memset(dst, 0, type->memSize); /* Initialize the value */
-               ret = decodeJsonInternal(dst, type, &ctx, &parseCtx, true);
+               ret = decodeJsonJumpTable[type->typeKind](dst, type, &ctx, &parseCtx, true);
                goto cleanup;
             }
         }
@@ -3339,7 +3359,7 @@ UA_decodeJson(const UA_ByteString *src, void *dst, const UA_DataType *type) {
 
     /* Decode */
     memset(dst, 0, type->memSize); /* Initialize the value */
-    ret = decodeJsonInternal(dst, type, &ctx, &parseCtx, true);
+    ret = decodeJsonJumpTable[type->typeKind](dst, type, &ctx, &parseCtx, true);
 
     cleanup:
     free(parseCtx.tokenArray);

+ 0 - 1
tests/check_types_builtin_json.c

@@ -2154,7 +2154,6 @@ START_TEST(UA_Variant_Double_json_encode) {
 
         UA_Double srcData = *((UA_Double*)src->data);
         UA_Double outData = *((UA_Double*)out.data);
-        assert(memcmp(&srcData, &outData, sizeof(UA_Double)) == 0);
         ck_assert(memcmp(&srcData, &outData, sizeof(UA_Double)) == 0);
 
         UA_ByteString_deleteMembers(&buf);

+ 2 - 2
tests/check_types_custom.c

@@ -62,11 +62,11 @@ static const UA_DataType PointType = {
     {1, UA_NODEIDTYPE_NUMERIC, {1}}, /* .typeId */
     sizeof(Point),                   /* .memSize */
     0,                               /* .typeIndex, in the array of custom types */
-    3,                               /* .membersSize */
-    false,                           /* .builtin */
+    UA_DATATYPEKIND_STRUCTURE,       /* .typeKind */
     true,                            /* .pointerFree */
     false,                           /* .overlayable (depends on endianness and
                                          the absence of padding) */
+    3,                               /* .membersSize */
     0,                               /* .binaryEncodingId, the numeric
                                          identifier used on the wire (the
                                          namespaceindex is from .typeId) */

+ 38 - 1
tests/server/check_services_attributes.c

@@ -61,6 +61,19 @@ static void setup(void) {
                                        vattr, NULL, NULL);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
+    /* Enum VariableNode */
+    UA_MessageSecurityMode m = UA_MESSAGESECURITYMODE_SIGN;
+    UA_Variant_setScalar(&vattr.value, &m, &UA_TYPES[UA_TYPES_MESSAGESECURITYMODE]);
+    vattr.description = UA_LOCALIZEDTEXT("locale","the enum answer");
+    vattr.displayName = UA_LOCALIZEDTEXT("locale","the enum answer");
+    vattr.valueRank = UA_VALUERANK_ANY;
+    retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, "the.enum.answer"),
+                                       parentNodeId, parentReferenceNodeId,
+                                       UA_QUALIFIEDNAME(1, "the enum answer"),
+                                       UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
+                                       vattr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
     /* DataSource VariableNode */
     vattr = UA_VariableAttributes_default;
     UA_DataSource temperatureDataSource;
@@ -564,7 +577,7 @@ START_TEST(ReadSingleDataSourceAttributeDataTypeWithoutTimestamp) {
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
 
-START_TEST (ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp) {
+START_TEST(ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp) {
     UA_ReadValueId rvi;
     UA_ReadValueId_init(&rvi);
     rvi.nodeId = UA_NODEID_STRING(1, "cpu.temperature");
@@ -738,6 +751,29 @@ START_TEST(WriteSingleAttributeValue) {
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
 
+START_TEST(WriteSingleAttributeValueEnum) {
+    UA_WriteValue wValue;
+    UA_WriteValue_init(&wValue);
+    UA_Int32 myInteger = 4;
+    UA_Variant_setScalar(&wValue.value.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    wValue.value.hasValue = true;
+    wValue.nodeId = UA_NODEID_STRING(1, "the.enum.answer");
+    wValue.attributeId = UA_ATTRIBUTEID_VALUE;
+    UA_StatusCode retval = UA_Server_write(server, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+    UA_ReadValueId rvi;
+    UA_ReadValueId_init(&rvi);
+    rvi.nodeId = UA_NODEID_STRING(1, "the.enum.answer");
+    rvi.attributeId = UA_ATTRIBUTEID_VALUE;
+    UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
+
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    ck_assert(resp.hasValue);
+    ck_assert_int_eq(4, *(UA_Int32*)resp.value.data);
+    UA_DataValue_deleteMembers(&resp);
+} END_TEST
+
 START_TEST(WriteSingleAttributeValueRangeFromScalar) {
     UA_WriteValue wValue;
     UA_WriteValue_init(&wValue);
@@ -912,6 +948,7 @@ static Suite * testSuite_services_attributes(void) {
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeContainsNoLoops);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeEventNotifier);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValue);
+    tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueEnum);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeDataType);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRangeFromScalar);
     tcase_add_test(tc_writeSingleAttributes, WriteSingleAttributeValueRangeFromArray);

+ 11 - 13
tools/generate_datatypes.py

@@ -99,17 +99,13 @@ class Type(object):
             self.typeIndex = makeCIdentifier(outname.upper() + "_" + self.name.upper())
         else:
             self.typeIndex = makeCIdentifier(outname.upper())
-
         self.ns0 = ("true" if namespace == 0 else "false")
         self.outname = outname
+        self.kind = None
         self.description = ""
         self.pointerfree = "false"
         self.overlayable = "false"
-        if self.name in builtin_types:
-            self.builtin = "true"
-        else:
-            self.builtin = "false"
-        self.members = [StructMember("", self, False)] # Returns one member: itself. Overwritten by some types.
+        self.members = []
         if xml is not None:
             for child in xml:
                 if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
@@ -131,10 +127,10 @@ class Type(object):
             "    " + typeid + ", /* .typeId */\n" + \
             "    sizeof(UA_" + idName + "), /* .memSize */\n" + \
             "    " + self.typeIndex + ", /* .typeIndex */\n" + \
-            "    " + str(len(self.members)) + ", /* .membersSize */\n" + \
-            "    " + self.builtin + ", /* .builtin */\n" + \
+            "    " + self.kind + ", /* .typeKind */\n" + \
             "    " + self.pointerfree + ", /* .pointerFree */\n" + \
             "    " + self.overlayable + ", /* .overlayable */\n" + \
+            "    " + str(len(self.members)) + ", /* .membersSize */\n" + \
             "    " + binaryEncodingId + ", /* .binaryEncodingId */\n" + \
             "    %s_members" % idName + " /* .members */\n}"
 
@@ -211,6 +207,7 @@ class BuiltinType(Type):
         self.ns0 = "true"
         self.typeIndex = makeCIdentifier("UA_TYPES_" + self.name.upper())
         self.outname = "ua_types"
+        self.kind = "UA_DATATYPEKIND_" + self.name.upper()
         self.description = ""
         self.pointerfree = "false"
         if self.name in builtin_overlayable.keys():
@@ -218,16 +215,15 @@ class BuiltinType(Type):
         self.overlayable = "false"
         if name in builtin_overlayable:
             self.overlayable = builtin_overlayable[name]
-        self.builtin = "true"
-        self.members = [StructMember("", self, False)] # builtin types contain only one member: themselves (drops into the jumptable during processing)
+        self.members = []
 
 class EnumerationType(Type):
     def __init__(self, outname, xml, namespace):
         Type.__init__(self, outname, xml, namespace)
         self.pointerfree = "true"
         self.overlayable = "UA_BINARY_OVERLAYABLE_INTEGER"
-        self.members = [StructMember("", types["Int32"], False)] # encoded as uint32
-        self.builtin = "true"
+        self.members = []
+        self.kind = "UA_DATATYPEKIND_ENUM"
         self.typeIndex = "UA_TYPES_INT32"
         self.elements = OrderedDict()
         for child in xml:
@@ -247,8 +243,9 @@ class EnumerationType(Type):
 class OpaqueType(Type):
     def __init__(self, outname, xml, namespace, baseType):
         Type.__init__(self, outname, xml, namespace)
+        self.kind = "UA_DATATYPEKIND_" + baseType.upper()
         self.baseType = baseType
-        self.members = [StructMember("", types[baseType], False)] # encoded as string
+        self.members = []
 
     def typedef_h(self):
         return "typedef UA_" + self.baseType + " UA_%s;" % self.name
@@ -275,6 +272,7 @@ class StructType(Type):
 
         self.pointerfree = "true"
         self.overlayable = "true"
+        self.kind = "UA_DATATYPEKIND_STRUCTURE"
         before = None
         for m in self.members:
             if m.isArray or m.memberType.pointerfree != "true":