Data Types
==========

Introduction
------------

In open62541, all data types share the same basic API for creation, copying and
deletion. The following functions are present for all data types ``T``.

``void T_init(T *ptr)``
  Initialize the data type. This is synonymous with zeroing out the memory, i.e. *memset(dataptr, 0, sizeof(T))*.
``T* T_new()``
  Allocate and return the memory for the data type. The memory is already initialized.
``UA_StatusCode T_copy(const T *src, T *dst)``
  Copy the content of the data type. Returns *UA_STATUSCODE_GOOD* if it succeeded.
``void T_deleteMembers(T *ptr)``
  Delete the dynamically allocated content of the data type, but not the data type itself.
``void T_delete(T *ptr)``
  Delete the content of the data type and the memory for the data type itself.

The builtin data types
----------------------

OPC UA defines 25 builtin data types. All other data types are combinations of
the 25 builtin data types.

UA_Boolean
^^^^^^^^^^

A two-state logical value (true or false).

.. code-block:: c

  typedef bool UA_Boolean;
  #define UA_TRUE true
  #define UA_FALSE false

UA_SByte
^^^^^^^^

An integer value between -128 and 127.

.. code-block:: c

  typedef int8_t UA_SByte;

UA_Byte
^^^^^^^

An integer value between 0 and 256.

.. code-block:: c

  typedef uint8_t UA_Byte;

UA_Int16
^^^^^^^^

An integer value between -32,768 and 32,767.

.. code-block:: c

  typedef int16_t UA_Int16;

UA_UInt16
^^^^^^^^^

An integer value between 0 and 65,535.

.. code-block:: c

  typedef uint16_t UA_UInt16;

UA_Int32
^^^^^^^^

An integer value between -2,147,483,648 and 2,147,483,647.

.. code-block:: c

  typedef int32_t UA_Int32;

UA_UInt32
^^^^^^^^^

An integer value between 0 and 4,294,967,295.

.. code-block:: c

  typedef uint32_t UA_UInt32;

UA_Int64
^^^^^^^^

An integer value between -10,223,372,036,854,775,808 and 9,223,372,036,854,775,807.

.. code-block:: c

  typedef int64_t UA_Int64;

UA_UInt64
^^^^^^^^^

An integer value between 0 and 18,446,744,073,709,551,615.

.. code-block:: c

  typedef uint64_t UA_UInt64;

UA_Float
^^^^^^^^

An IEEE single precision (32 bit) floating point value.

.. code-block:: c

  typedef float UA_Float;

UA_Double
^^^^^^^^^

An IEEE double precision (64 bit) floating point value.

.. code-block:: c

  typedef double UA_Double;

UA_DateTime
^^^^^^^^^^^

An instance in time. A DateTime value is encoded as a 64-bit signed integer
which represents the number of 100 nanosecond intervals since January 1, 1601
(UTC).

.. code-block:: c

  typedef UA_Int64 UA_DateTime;

The following functions and definitions are used with UA_DateTime.

.. code-block:: c

  UA_DateTime UA_DateTime_now(void);

  typedef struct UA_DateTimeStruct {
      UA_UInt16 nanoSec;
      UA_UInt16 microSec;
      UA_UInt16 milliSec;
      UA_UInt16 sec;
      UA_UInt16 min;
      UA_UInt16 hour;
      UA_UInt16 day;
      UA_UInt16 month;
      UA_UInt16 year;
  } UA_DateTimeStruct;

  UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);

  UA_String UA_EXPORT UA_DateTime_toString(UA_DateTime time);

UA_Guid
^^^^^^^

A 16 byte value that can be used as a globally unique identifier.

.. code-block:: c

  typedef struct {
      UA_UInt32 data1;
      UA_UInt16 data2;
      UA_UInt16 data3;
      UA_Byte   data4[8];
  } UA_Guid;

The following functions and definitions are used with UA_Guid.

.. code-block:: c

  UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2);

  UA_Guid UA_Guid_random(UA_UInt32 *seed);

UA_String
^^^^^^^^^

A sequence of Unicode characters. See also the section :ref:`array-handling` for
the usage of arrays in open62541.

.. code-block:: c

  typedef struct {
      size_t length; // The length of the string
      UA_Byte *data; // The string's content (not null-terminated)
  } UA_String;

The following functions and definitions are used with UA_String.

.. code-block:: c

  extern const UA_String UA_STRING_NULL;

  UA_String UA_STRING(char *chars);

  #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
    
  /** Copies the content on the heap. Returns a null-string when alloc fails */
  UA_String UA_String_fromChars(char const src[]);

  UA_Boolean UA_String_equal(const UA_String *s1, const UA_String *s2);

Here's a small example for the usage of UA_String.

.. code-block:: c

  /* The definition of UA_String copied from ua_types.h */ 
  typedef struct {
      size_t length; ///< The length of the string
      UA_Byte *data; ///< The string's content (not null-terminated)
  } UA_String;

  UA_String s1 = UA_STRING("test1");       /* s1 points to the statically allocated string buffer */
  UA_String_init(&s1);                     /* Reset s1 (no memleak due to the statically allocated buffer) */
  
  UA_String s2 = UA_STRING_ALLOC("test2"); /* s2 points to a new copy of the string buffer (with malloc) */
  UA_String_deleteMembers(&s2);            /* Free the content of s2, but not s2 itself */
  
  UA_String *s3 = UA_String_new();         /* The string s3 is malloced and initialized */
  *s3 = UA_STRING_ALLOC("test3");          /* s3 points to a new copy of the string buffer */
  
  UA_String s4;
  UA_copy(s3, &s4);                        /* Copy the content of s3 to s4 */
  
  UA_String_delete(s3);                    /* Free the string buffer and the string itself */
  UA_String_deleteMembers(&s4);            /* Again, delete only the string buffer */

UA_ByteString
^^^^^^^^^^^^^

A sequence of octets.

.. code-block:: c

  typedef UA_String UA_ByteString;

UA_XmlEelement
^^^^^^^^^^^^^^

An XML element.

.. code-block:: c

  typedef UA_String UA_XmlElement;

UA_NodeId
^^^^^^^^^

An identifier for a node in the address space of an OPC UA Server.

.. code-block:: c

  enum UA_NodeIdType {
      UA_NODEIDTYPE_NUMERIC    = 0, // On the wire, this can be 0, 1 or 2 (shortened numeric nodeids)
      UA_NODEIDTYPE_STRING     = 3,
      UA_NODEIDTYPE_GUID       = 4,
      UA_NODEIDTYPE_BYTESTRING = 5
  };

  typedef struct {
      UA_UInt16 namespaceIndex;
      enum UA_NodeIdType identifierType;
      union {
          UA_UInt32     numeric;
          UA_String     string;
          UA_Guid       guid;
          UA_ByteString byteString;
      } identifier;
  } UA_NodeId;

The following functions and definitions are used with UA_NodeId.

.. code-block:: c

  UA_Boolean UA_NodeId_isNull(const UA_NodeId *p);

  UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);

  extern const UA_NodeId UA_NODEID_NULL;

  UA_NodeId UA_NODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier);

  UA_NodeId UA_NODEID_STRING(UA_UInt16 nsIndex, char *chars) {

  UA_NodeId UA_NODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars);

  UA_NodeId UA_NODEID_GUID(UA_UInt16 nsIndex, UA_Guid guid);

  UA_NodeId UA_NODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars);

  UA_NodeId UA_NODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars);

UA_ExpandedNodeId
^^^^^^^^^^^^^^^^^

A NodeId that allows the namespace URI to be specified instead of an index.

.. code-block:: c

  typedef struct {
      UA_NodeId nodeId;
      UA_String namespaceUri;
      UA_UInt32 serverIndex;
  } UA_ExpandedNodeId;

The following functions and definitions are used with UA_ExpandedNodeId.

.. code-block:: c

  UA_ExpandedNodeId UA_EXPANDEDNODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier);

  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING(UA_UInt16 nsIndex, char *chars);

  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars);

  UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid);

  UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars);

  UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars);

UA_QualifiedName
^^^^^^^^^^^^^^^^

A name qualified by a namespace.

.. code-block:: c

  typedef struct {
      UA_UInt16 namespaceIndex;
      UA_String name;
  } UA_QualifiedName;

The following functions and definitions are used with UA_QualifiedName.

.. code-block:: c

  UA_QualifiedName UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars);

  UA_QualifiedName UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars);

UA_LocalizedText
^^^^^^^^^^^^^^^^

Human readable text with an optional locale identifier.

.. code-block:: c

  typedef struct {
      UA_String locale;
      UA_String text;
  } UA_LocalizedText;
                
The following functions and definitions are used with UA_LocalizedText.

.. code-block:: c

  UA_LocalizedText UA_LOCALIZEDTEXT(char *locale, char *text);

  UA_LocalizedText UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text);

UA_ExtensionObject
^^^^^^^^^^^^^^^^^^

A structure that contains an application specific data type that may not be
recognized by the receiver.

.. code-block:: c

  typedef struct {
      enum {
          UA_EXTENSIONOBJECT_ENCODED_NOBODY     = 0,
          UA_EXTENSIONOBJECT_ENCODED_BYTESTRING = 1,
          UA_EXTENSIONOBJECT_ENCODED_XML        = 2,
          UA_EXTENSIONOBJECT_DECODED            = 3, ///< There is a pointer to the decoded data
          UA_EXTENSIONOBJECT_DECODED_NODELETE   = 4  ///< Don't delete the decoded data at the lifecycle end
      } encoding;
      union {
          struct {
              UA_NodeId typeId; ///< The nodeid of the datatype
              UA_ByteString body; ///< The bytestring of the encoded data
          } encoded;
          struct {
              const UA_DataType *type;
              void *data;
          } decoded;
      } content;
  } UA_ExtensionObject;

UA_Variant
^^^^^^^^^^

Stores (arrays of) any data type. Please see section :ref:`generic-handling` for
the usage of UA_DataType. The semantics of the arrayLength field is explained in
section :ref:`array-handling`.

.. code-block:: c

  typedef struct {
      const UA_DataType *type; // The data type description
      enum {
          UA_VARIANT_DATA,          /* The data has the same lifecycle as the variant */
          UA_VARIANT_DATA_NODELETE, /* The data is "borrowed" by the variant and shall not be
                                       deleted at the end of the variant's lifecycle. */
      } storageType;
      size_t arrayLength;  // The number of elements in the data array
      void *data; // Points to the scalar or array data
      size_t arrayDimensionsSize; // The number of dimensions the data-array has
      UA_UInt32 *arrayDimensions; // The length of each dimension of the data-array
  } UA_Variant;

  /* NumericRanges are used to indicate subsets of a (multidimensional) variant
  * array. NumericRange has no official type structure in the standard. On the
  * wire, it only exists as an encoded string, such as "1:2,0:3,5". The colon
  * separates min/max index and the comma separates dimensions. A single value
  * indicates a range with a single element (min==max). */
  typedef struct {
      size_t dimensionsSize;
      struct UA_NumericRangeDimension {
          UA_UInt32 min;
          UA_UInt32 max;
      } *dimensions;
  } UA_NumericRange;


The following functions and definitions are used with UA_Variant.

.. code-block:: c

  /**
   * Returns true if the variant contains a scalar value. Note that empty
   * variants contain an array of length -1 (undefined).
   *
   * @param v The variant
   * @return Does the variant contain a scalar value.
   */
  UA_Boolean UA_Variant_isScalar(const UA_Variant *v);

  /**
   * Set the variant to a scalar value that already resides in memory. The value
   * takes on the lifecycle of the variant and is deleted with it.
   *
   * @param v The variant
   * @param p A pointer to the value data
   * @param type The datatype of the value in question
   */
  UA_Variant_setScalar(UA_Variant *v, void * UA_RESTRICT p, const UA_DataType *type);

  /**
   * Set the variant to a scalar value that is copied from an existing variable.
   *
   * @param v The variant
   * @param p A pointer to the value data
   * @param type The datatype of the value
   * @return Indicates whether the operation succeeded or returns an error code
   */
  UA_StatusCode UA_Variant_setScalarCopy(UA_Variant *v, const void *p, const UA_DataType *type);

  /**
   * Set the variant to an array that already resides in memory. The array takes
   * on the lifecycle of the variant and is deleted with it.
   *
   * @param v The variant
   * @param array A pointer to the array data
   * @param arraySize The size of the array
   * @param type The datatype of the array
   */
  void UA_Variant_setArray(UA_Variant *v, void * UA_RESTRICT array,
                           size_t arraySize, const UA_DataType *type);

  /**
   * Set the variant to an array that is copied from an existing array.
   *
   * @param v The variant
   * @param array A pointer to the array data
   * @param arraySize The size of the array
   * @param type The datatype of the array
   * @return Indicates whether the operation succeeded or returns an error code
   */
  UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array,
                                        size_t arraySize, const UA_DataType *type);

  /**
   * Copy the variant, but use only a subset of the (multidimensional) array
   * into a variant. Returns an error code if the variant is not an array or if
   * the indicated range does not fit.
   *
   * @param src The source variant
   * @param dst The target variant
   * @param range The range of the copied data
   * @return Returns UA_STATUSCODE_GOOD or an error code
   */
  UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
                                     const UA_NumericRange range);

  /**
   * Insert a range of data into an existing variant. The data array can't be
   * reused afterwards if it contains types without a fixed size (e.g. strings)
   * since the members are moved into the variant and take on its lifecycle.
   *
   * @param v The variant
   * @param dataArray The data array. The type must match the variant
   * @param dataArraySize The length of the data array. This is checked to match the range size.
   * @param range The range of where the new data is inserted
   * @return Returns UA_STATUSCODE_GOOD or an error code
   */
  UA_StatusCode UA_Variant_setRange(UA_Variant *v, void * UA_RESTRICT array,
                                    size_t arraySize, const UA_NumericRange range);

  /**
   * Deep-copy a range of data into an existing variant.
   *
   * @param v The variant
   * @param dataArray The data array. The type must match the variant
   * @param dataArraySize The length of the data array. This is checked to match the range size.
   * @param range The range of where the new data is inserted
   * @return Returns UA_STATUSCODE_GOOD or an error code
   */
  UA_StatusCode UA_Variant_setRangeCopy(UA_Variant *v, const void *array,
                                        size_t arraySize, const UA_NumericRange range);

UA_DataValue
^^^^^^^^^^^^

A data value with an associated status code and timestamps.

.. code-block:: c
   
  typedef struct {
      UA_Boolean    hasValue             : 1;
      UA_Boolean    hasStatus            : 1;
      UA_Boolean    hasSourceTimestamp   : 1;
      UA_Boolean    hasServerTimestamp   : 1;
      UA_Boolean    hasSourcePicoseconds : 1;
      UA_Boolean    hasServerPicoseconds : 1;
      UA_Variant    value;
      UA_StatusCode status;
      UA_DateTime   sourceTimestamp;
      UA_Int16      sourcePicoseconds;
      UA_DateTime   serverTimestamp;
      UA_Int16      serverPicoseconds;
  } UA_DataValue;

UA_DiagnosticInfo
^^^^^^^^^^^^^^^^^

A structure that contains detailed error and diagnostic information associated
with a StatusCode.

.. code-block:: c

  typedef struct UA_DiagnosticInfo {
      UA_Boolean    hasSymbolicId          : 1;
      UA_Boolean    hasNamespaceUri        : 1;
      UA_Boolean    hasLocalizedText       : 1;
      UA_Boolean    hasLocale              : 1;
      UA_Boolean    hasAdditionalInfo      : 1;
      UA_Boolean    hasInnerStatusCode     : 1;
      UA_Boolean    hasInnerDiagnosticInfo : 1;
      UA_Int32      symbolicId;
      UA_Int32      namespaceUri;
      UA_Int32      localizedText;
      UA_Int32      locale;
      UA_String     additionalInfo;
      UA_StatusCode innerStatusCode;
      struct UA_DiagnosticInfo *innerDiagnosticInfo;
  } UA_DiagnosticInfo;

.. _generic-handling:

Generic Data Type Handling
--------------------------

All standard-defined data types are described with an ``UA_DataType`` structure.
In addition to the 25 builtin data types, OPC UA defines many more. But they are
mere combinations of the builtin data types. We handle all types in a unified
way by storing their internal structure. So it is not necessary to define
specialized functions for all additional types.

The ``UA_TYPES`` array contains the description of every standard-defined data
type.

.. code-block:: c

  extern const UA_DataType UA_TYPES[UA_TYPES_COUNT];

The following is an excerpt from ``ua_types_generated.h`` with the definition of
OPC UA read requests. This file is auto-generated from the XML-description of
the OPC UA data types that is part of the ISO/IEC 62541 standard.

.. code-block:: c

  typedef struct {
      UA_RequestHeader requestHeader;
      UA_Double maxAge;
      UA_TimestampsToReturn timestampsToReturn;
      size_t nodesToReadSize;
      UA_ReadValueId *nodesToRead;
  } UA_ReadRequest;

  #define UA_TYPES_READREQUEST 118
  
  static UA_INLINE void UA_ReadRequest_init(UA_ReadRequest *p) {
      memset(p, 0, sizeof(UA_ReadRequest)); }

  static UA_INLINE void UA_ReadRequest_delete(UA_ReadRequest *p) {
      UA_delete(p, &UA_TYPES[UA_TYPES_READREQUEST]); }

  static UA_INLINE void UA_ReadRequest_deleteMembers(UA_ReadRequest *p) {
      UA_deleteMembers(p, &UA_TYPES[UA_TYPES_READREQUEST]); }

  static UA_INLINE UA_ReadRequest * UA_ReadRequest_new(void) {
      return (UA_ReadRequest*) UA_new(&UA_TYPES[UA_TYPES_READREQUEST]); }

  static UA_INLINE UA_StatusCode
  UA_ReadRequest_copy(const UA_ReadRequest *src, UA_ReadRequest *dst) {
      return UA_copy(src, dst, &UA_TYPES[UA_TYPES_READREQUEST]); }

.. _array-handling:

Array Handling
--------------

In OPC UA, all arrays can be undefined, have length 0 or a positive length. In
data structures, all arrays are represented by a ``size_t`` length field and an
appropriate pointer directly afterwards. In order to distinguish between
undefined and length-zero arrays, we define the following.

.. code-block:: c

  #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)

- size == 0 and data == NULL: The array is undefined
- size == 0 and data == ``UA_EMPTY_ARRAY_SENTINEL``: The array has length 0
- size > 0: The array at the given memory address has the given size

The following functions are defined for array handling.

.. code-block:: c

  /**
   * Allocates and initializes an array of variables of a specific type
   *
   * @param size The requested array length
   * @param type The datatype description
   * @return Returns the memory location of the variable or (void*)0 if no memory could be allocated
   */
  void * UA_Array_new(size_t size, const UA_DataType *type);

  /**
   * Allocates and copies an array. dst is set to (void*)0 if not enough memory is available.
   *
   * @param src The memory location of the source array
   * @param src_size The size of the array
   * @param dst The location of the pointer to the new array
   * @param type The datatype of the array members
   * @return Returns whether copying succeeded
   */
  UA_StatusCode UA_Array_copy(const void *src, size_t src_size, void **dst,
                              const UA_DataType *type);

  /**
   * Deletes an array.
   *
   * @param p The memory location of the array
   * @param size The size of the array
   * @param type The datatype of the array members
   */
  void UA_Array_delete(void *p, size_t size, const UA_DataType *type);