Merge pull request #260 from eboasson/plist-update
parameter list update: properties, fixes, unit tests
This commit is contained in:
		
						commit
						78fc9c2e85
					
				
					 8 changed files with 976 additions and 159 deletions
				
			
		| 
						 | 
				
			
			@ -70,6 +70,7 @@ PREPEND(hdrs_private_ddsi "${CMAKE_CURRENT_LIST_DIR}/include/dds/ddsi"
 | 
			
		|||
    ddsi_raweth.h
 | 
			
		||||
    ddsi_ipaddr.h
 | 
			
		||||
    ddsi_mcgroup.h
 | 
			
		||||
    ddsi_plist_generic.h
 | 
			
		||||
    ddsi_serdata.h
 | 
			
		||||
    ddsi_sertopic.h
 | 
			
		||||
    ddsi_serdata_default.h
 | 
			
		||||
| 
						 | 
				
			
			@ -129,3 +130,8 @@ install(
 | 
			
		|||
  DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/dds"
 | 
			
		||||
  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
 | 
			
		||||
  COMPONENT dev)
 | 
			
		||||
 | 
			
		||||
# TODO: improve test inclusion.
 | 
			
		||||
if((BUILD_TESTING) AND ((NOT DEFINED MSVC_VERSION) OR (MSVC_VERSION GREATER "1800")))
 | 
			
		||||
  add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/tests")
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										67
									
								
								src/core/ddsi/include/dds/ddsi/ddsi_plist_generic.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/core/ddsi/include/dds/ddsi/ddsi_plist_generic.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright(c) 2019 ADLINK Technology Limited and others
 | 
			
		||||
 *
 | 
			
		||||
 * This program and the accompanying materials are made available under the
 | 
			
		||||
 * terms of the Eclipse Public License v. 2.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
 | 
			
		||||
 * v. 1.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 | 
			
		||||
 */
 | 
			
		||||
#ifndef DDSI_PLIST_GENERIC_H
 | 
			
		||||
#define DDSI_PLIST_GENERIC_H
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#include "dds/export.h"
 | 
			
		||||
 | 
			
		||||
#include "dds/ddsrt/attributes.h"
 | 
			
		||||
 | 
			
		||||
#if defined (__cplusplus)
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Instructions for the generic serializer (&c) that handles most parameters.
 | 
			
		||||
   The "packed" attribute means single-byte instructions on GCC and Clang. */
 | 
			
		||||
enum pserop {
 | 
			
		||||
  XSTOP,
 | 
			
		||||
  XO, /* octet sequence */
 | 
			
		||||
  XS, /* string */
 | 
			
		||||
  XE1, XE2, XE3, /* enum 0..1, 0..2, 0..3 */
 | 
			
		||||
  Xi, Xix2, Xix3, Xix4, /* int32_t, 1 .. 4 in a row */
 | 
			
		||||
  Xu, Xux2, Xux3, Xux4, Xux5, /* uint32_t, 1 .. 5 in a row */
 | 
			
		||||
  XD, XDx2, /* duration, 1 .. 2 in a row */
 | 
			
		||||
  Xo, Xox2, /* octet, 1 .. 2 in a row */
 | 
			
		||||
  Xb, Xbx2, /* boolean, 1 .. 2 in a row */
 | 
			
		||||
  XbCOND, /* boolean: compare to ignore remainder if false (for use_... flags) */
 | 
			
		||||
  XbPROP, /* boolean: omit in serialized form; skip serialization if false; always true on deserialize */
 | 
			
		||||
  XG, /* GUID */
 | 
			
		||||
  XK, /* keyhash */
 | 
			
		||||
  XQ, /* arbitary non-nested sequence */
 | 
			
		||||
  Xopt, /* remainder is optional on deser, 0-init if not present */
 | 
			
		||||
} ddsrt_attribute_packed;
 | 
			
		||||
 | 
			
		||||
inline bool pserop_seralign_is_1 (enum pserop op) {
 | 
			
		||||
  /* NB: XbPROP is never serialized, so its alignment is irrelevant.  If ever there
 | 
			
		||||
     is a need to allow calling this function when op = XbPROP, it needs to be changed
 | 
			
		||||
     to taking the address of the pserop, and in that case inspect the following
 | 
			
		||||
     operator */
 | 
			
		||||
  assert (op != XbPROP && op != Xopt && op != XSTOP);
 | 
			
		||||
  return (op >= Xo && op <= XK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DDS_EXPORT void plist_fini_generic (void * __restrict dst, const enum pserop *desc, bool aliased);
 | 
			
		||||
DDS_EXPORT dds_return_t plist_deser_generic (void * __restrict dst, const void * __restrict src, size_t srcsize, bool bswap, const enum pserop * __restrict desc);
 | 
			
		||||
DDS_EXPORT dds_return_t plist_ser_generic (void **dst, size_t *dstsize, const void *src, const enum pserop * __restrict desc);
 | 
			
		||||
DDS_EXPORT dds_return_t plist_unalias_generic (void * __restrict dst, const enum pserop * __restrict desc);
 | 
			
		||||
DDS_EXPORT bool plist_equal_generic (const void *srcx, const void *srcy, const enum pserop * __restrict desc);
 | 
			
		||||
DDS_EXPORT size_t plist_memsize_generic (const enum pserop * __restrict desc);
 | 
			
		||||
 | 
			
		||||
#if defined (__cplusplus)
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,41 @@ typedef ddsi_octetseq_t dds_userdata_qospolicy_t;
 | 
			
		|||
typedef ddsi_octetseq_t dds_topicdata_qospolicy_t;
 | 
			
		||||
typedef ddsi_octetseq_t dds_groupdata_qospolicy_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_property {
 | 
			
		||||
  /* The propagate boolean will not be send over the wire.
 | 
			
		||||
   * When the value is 'false', the complete struct shouldn't be send.
 | 
			
		||||
   * It has to be the first variable within the structure because it
 | 
			
		||||
   * is mapped to XbPROP in the serialiser. */
 | 
			
		||||
  unsigned char propagate;
 | 
			
		||||
  char *name;
 | 
			
		||||
  char *value;
 | 
			
		||||
} dds_property_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_propertyseq {
 | 
			
		||||
  uint32_t n;
 | 
			
		||||
  dds_property_t *props;
 | 
			
		||||
} dds_propertyseq_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_binaryproperty {
 | 
			
		||||
  /* The propagate boolean will not be send over the wire.
 | 
			
		||||
   * When the value is 'false', the complete struct shouldn't be send.
 | 
			
		||||
   * It has to be the first variable within the structure because it
 | 
			
		||||
   * is mapped to XbPROP in the serialiser. */
 | 
			
		||||
  unsigned char propagate;
 | 
			
		||||
  char *name;
 | 
			
		||||
  ddsi_octetseq_t value;
 | 
			
		||||
} dds_binaryproperty_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_binarypropertyseq {
 | 
			
		||||
  uint32_t n;
 | 
			
		||||
  dds_binaryproperty_t *props;
 | 
			
		||||
} dds_binarypropertyseq_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_property_qospolicy {
 | 
			
		||||
  dds_propertyseq_t value;
 | 
			
		||||
  dds_binarypropertyseq_t binary_value;
 | 
			
		||||
} dds_property_qospolicy_t;
 | 
			
		||||
 | 
			
		||||
typedef struct dds_durability_qospolicy {
 | 
			
		||||
  dds_durability_kind_t kind;
 | 
			
		||||
} dds_durability_qospolicy_t;
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +247,7 @@ typedef struct dds_ignorelocal_qospolicy {
 | 
			
		|||
#define QP_PRISMTECH_SUBSCRIPTION_KEYS       ((uint64_t)1 << 25)
 | 
			
		||||
#define QP_PRISMTECH_ENTITY_FACTORY          ((uint64_t)1 << 27)
 | 
			
		||||
#define QP_CYCLONE_IGNORELOCAL               ((uint64_t)1 << 30)
 | 
			
		||||
#define QP_PROPERTY_LIST                     ((uint64_t)1 << 31)
 | 
			
		||||
 | 
			
		||||
/* Partition QoS is not RxO according to the specification (DDS 1.2,
 | 
			
		||||
   section 7.1.3), but communication will not take place unless it
 | 
			
		||||
| 
						 | 
				
			
			@ -263,6 +299,7 @@ struct dds_qos {
 | 
			
		|||
  /*x xR*/dds_subscription_keys_qospolicy_t subscription_keys;
 | 
			
		||||
  /*x xR*/dds_reader_lifespan_qospolicy_t reader_lifespan;
 | 
			
		||||
  /* x  */dds_ignorelocal_qospolicy_t ignorelocal;
 | 
			
		||||
  /*xxx */dds_property_qospolicy_t property;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nn_xmsg;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -453,6 +453,7 @@ static struct ddsi_serdata *serdata_default_from_sample_plist (const struct ddsi
 | 
			
		|||
#ifndef NDEBUG
 | 
			
		||||
  size_t keysize;
 | 
			
		||||
#endif
 | 
			
		||||
  assert(rawkey);
 | 
			
		||||
  switch (sample->keyparam)
 | 
			
		||||
  {
 | 
			
		||||
    case PID_PARTICIPANT_GUID:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,8 @@
 | 
			
		|||
#include "dds/ddsrt/avl.h"
 | 
			
		||||
#include "dds/ddsi/q_misc.h" /* for vendor_is_... */
 | 
			
		||||
 | 
			
		||||
#include "dds/ddsi/ddsi_plist_generic.h"
 | 
			
		||||
 | 
			
		||||
/* I am tempted to change LENGTH_UNLIMITED to 0 in the API (with -1
 | 
			
		||||
   supported for backwards compatibility) ... on the wire however
 | 
			
		||||
   it must be -1 */
 | 
			
		||||
| 
						 | 
				
			
			@ -88,25 +90,6 @@ struct flagset {
 | 
			
		|||
  uint64_t wanted;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Instructions for the generic serializer (&c) that handles most parameters.
 | 
			
		||||
   The "packed" attribute means single-byte instructions on GCC and Clang. */
 | 
			
		||||
enum pserop {
 | 
			
		||||
  XSTOP,
 | 
			
		||||
  XO, /* octet sequence */
 | 
			
		||||
  XS, /* string */
 | 
			
		||||
  XZ, /* string sequence */
 | 
			
		||||
  XE1, XE2, XE3, /* enum 0..1, 0..2, 0..3 */
 | 
			
		||||
  Xl, /* length, int32_t, -1 or >= 1 */
 | 
			
		||||
  Xi, Xix2, Xix3, Xix4, /* int32_t, 1 .. 4 in a row */
 | 
			
		||||
  Xu, Xux2, Xux3, Xux4, Xux5, /* uint32_t, 1 .. 5 in a row */
 | 
			
		||||
  XD, XDx2, /* duration, 1 .. 2 in a row */
 | 
			
		||||
  Xo, Xox2, /* octet, 1 .. 2 in a row */
 | 
			
		||||
  Xb, Xbx2, /* boolean, 1 .. 2 in a row */
 | 
			
		||||
  XbCOND, /* boolean: compare to ignore remainder if false (for use_... flags) */
 | 
			
		||||
  XG, /* GUID */
 | 
			
		||||
  XK /* keyhash */
 | 
			
		||||
} ddsrt_attribute_packed;
 | 
			
		||||
 | 
			
		||||
struct piddesc {
 | 
			
		||||
  nn_parameterid_t pid;  /* parameter id or PID_PAD if strictly local */
 | 
			
		||||
  uint16_t flags;        /* see PDF_xxx flags */
 | 
			
		||||
| 
						 | 
				
			
			@ -115,10 +98,10 @@ struct piddesc {
 | 
			
		|||
  size_t plist_offset;   /* offset from start of nn_plist_t */
 | 
			
		||||
  size_t size;           /* in-memory size for copying */
 | 
			
		||||
  union {
 | 
			
		||||
    /* descriptor for generic code: 4 is enough for the current set of
 | 
			
		||||
    /* descriptor for generic code: 12 is enough for the current set of
 | 
			
		||||
       parameters, compiler will warn if one ever tries to use more than
 | 
			
		||||
       will fit; on non-GCC/Clang and 32-bits machines */
 | 
			
		||||
    const enum pserop desc[4];
 | 
			
		||||
       will fit */
 | 
			
		||||
    const enum pserop desc[12];
 | 
			
		||||
    struct {
 | 
			
		||||
      dds_return_t (*deser) (void * __restrict dst, size_t * __restrict dstoff, struct flagset *flagset, uint64_t flag, const struct dd * __restrict dd, size_t * __restrict srcoff);
 | 
			
		||||
      dds_return_t (*ser) (struct nn_xmsg *xmsg, nn_parameterid_t pid, const void *src, size_t srcoff);
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +114,8 @@ struct piddesc {
 | 
			
		|||
  dds_return_t (*deser_validate_xform) (void * __restrict dst, const struct dd * __restrict dd);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern inline bool pserop_seralign_is_1 (enum pserop op);
 | 
			
		||||
 | 
			
		||||
static void log_octetseq (uint32_t cat, const struct ddsrt_log_cfg *logcfg, uint32_t n, const unsigned char *xs);
 | 
			
		||||
static dds_return_t validate_history_qospolicy (const dds_history_qospolicy_t *q);
 | 
			
		||||
static dds_return_t validate_resource_limits_qospolicy (const dds_resource_limits_qospolicy_t *q);
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +310,47 @@ static dds_return_t fini_locator (void * __restrict dst, size_t * __restrict dst
 | 
			
		|||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fini_generic_partial (void * __restrict dst, size_t * __restrict dstoff, const enum pserop *desc, const enum pserop * const desc_end, bool aliased)
 | 
			
		||||
static size_t ser_generic_srcsize (const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  size_t srcoff = 0, srcalign = 0;
 | 
			
		||||
#define SIMPLE(basecase_, type_) do {                          \
 | 
			
		||||
    const uint32_t cnt = 1 + (uint32_t) (*desc - (basecase_)); \
 | 
			
		||||
    const size_t align = alignof (type_);                      \
 | 
			
		||||
    srcalign = (align > srcalign) ? align : srcalign;          \
 | 
			
		||||
    srcoff = (srcoff + align - 1) & ~(align - 1);              \
 | 
			
		||||
    srcoff += cnt * sizeof (type_);                            \
 | 
			
		||||
  } while (0)
 | 
			
		||||
  while (true)
 | 
			
		||||
  {
 | 
			
		||||
    switch (*desc)
 | 
			
		||||
    {
 | 
			
		||||
      case XSTOP: return (srcoff + srcalign - 1) & ~(srcalign - 1);
 | 
			
		||||
      case XO: SIMPLE (XO, ddsi_octetseq_t); break;
 | 
			
		||||
      case XS: SIMPLE (XS, const char *); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: SIMPLE (*desc, unsigned); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: SIMPLE (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: SIMPLE (Xu, uint32_t); break;
 | 
			
		||||
      case XD: case XDx2: SIMPLE (XD, dds_duration_t); break;
 | 
			
		||||
      case Xo: case Xox2: SIMPLE (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break;
 | 
			
		||||
      case XbCOND: SIMPLE (XbCOND, unsigned char); break;
 | 
			
		||||
      case XG: SIMPLE (XG, ddsi_guid_t); break;
 | 
			
		||||
      case XK: SIMPLE (XK, nn_keyhash_t); break;
 | 
			
		||||
      case XbPROP: SIMPLE (XbPROP, unsigned char); break;
 | 
			
		||||
      case XQ: SIMPLE (XQ, ddsi_octetseq_t); while (*++desc != XSTOP) { } break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
#undef SIMPLE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t plist_memsize_generic (const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  return ser_generic_srcsize (desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fini_generic_embeddable (void * __restrict dst, size_t * __restrict dstoff, const enum pserop *desc, const enum pserop * const desc_end, bool aliased)
 | 
			
		||||
{
 | 
			
		||||
#define COMPLEX(basecase_, type_, cleanup_unaliased_, cleanup_always_) do { \
 | 
			
		||||
    type_ *x = deser_generic_dst (dst, dstoff, alignof (type_));            \
 | 
			
		||||
| 
						 | 
				
			
			@ -344,17 +369,29 @@ static void fini_generic_partial (void * __restrict dst, size_t * __restrict dst
 | 
			
		|||
      case XSTOP: return;
 | 
			
		||||
      case XO: COMPLEX (XO, ddsi_octetseq_t, ddsrt_free (x->value), (void) 0); break;
 | 
			
		||||
      case XS: COMPLEX (XS, char *, ddsrt_free (*x), (void) 0); break;
 | 
			
		||||
      case XZ: COMPLEX (XZ, ddsi_stringseq_t, { for (uint32_t i = 0; i < x->n; i++) ddsrt_free (x->strs[i]); }, ddsrt_free (x->strs)); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: COMPLEX (*desc, unsigned, (void) 0, (void) 0); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: SIMPLE (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: SIMPLE (Xu, uint32_t); break;
 | 
			
		||||
      case Xl: SIMPLE (Xl, int32_t); break;
 | 
			
		||||
      case XD: case XDx2: SIMPLE (XD, dds_duration_t); break;
 | 
			
		||||
      case Xo: case Xox2: SIMPLE (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break;
 | 
			
		||||
      case XbCOND: SIMPLE (XbCOND, unsigned char); break;
 | 
			
		||||
      case XG: SIMPLE (XG, ddsi_guid_t); break;
 | 
			
		||||
      case XK: SIMPLE (XK, nn_keyhash_t); break;
 | 
			
		||||
      case XbPROP: SIMPLE (XbPROP, unsigned char); break;
 | 
			
		||||
      case XQ:
 | 
			
		||||
        /* non-nested, so never a need to deallocate only some of the entries and no complications
 | 
			
		||||
           in locating the end of the sequence element description */
 | 
			
		||||
        COMPLEX (XQ, ddsi_octetseq_t, {
 | 
			
		||||
          const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
          for (uint32_t i = 0; i < x->length; i++) {
 | 
			
		||||
            size_t elem_off = i * elem_size;
 | 
			
		||||
            fini_generic_embeddable (x->value, &elem_off, desc + 1, desc_end, aliased);
 | 
			
		||||
          }
 | 
			
		||||
        }, ddsrt_free (x->value));
 | 
			
		||||
        while (desc + 1 != desc_end && *++desc != XSTOP) { }
 | 
			
		||||
        break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -362,6 +399,26 @@ static void fini_generic_partial (void * __restrict dst, size_t * __restrict dst
 | 
			
		|||
#undef COMPLEX
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t pserop_memalign (enum pserop op)
 | 
			
		||||
{
 | 
			
		||||
  switch (op)
 | 
			
		||||
  {
 | 
			
		||||
    case XO: case XQ: return alignof (ddsi_octetseq_t);
 | 
			
		||||
    case XS: return alignof (char *);
 | 
			
		||||
    case XG: return alignof (ddsi_guid_t);
 | 
			
		||||
    case XK: return alignof (nn_keyhash_t);
 | 
			
		||||
    case Xb: case Xbx2: return 1;
 | 
			
		||||
    case Xo: case Xox2: return 1;
 | 
			
		||||
    case XbCOND: case XbPROP: return 1;
 | 
			
		||||
    case XE1: case XE2: case XE3: return sizeof (uint32_t);
 | 
			
		||||
    case Xi: case Xix2: case Xix3: case Xix4: return sizeof (int32_t);
 | 
			
		||||
    case Xu: case Xux2: case Xux3: case Xux4: case Xux5: return sizeof (uint32_t);
 | 
			
		||||
    case XD: case XDx2: return alignof (dds_duration_t);
 | 
			
		||||
    case XSTOP: case Xopt: assert (0);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict dstoff, struct flagset *flagset, uint64_t flag, const struct dd * __restrict dd, size_t * __restrict srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  enum pserop const * const desc_in = desc;
 | 
			
		||||
| 
						 | 
				
			
			@ -376,8 +433,7 @@ static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict ds
 | 
			
		|||
    switch (*desc)
 | 
			
		||||
    {
 | 
			
		||||
      case XSTOP:
 | 
			
		||||
        *flagset->present |= flag;
 | 
			
		||||
        return 0;
 | 
			
		||||
        goto success;
 | 
			
		||||
      case XO: { /* octet sequence */
 | 
			
		||||
        ddsi_octetseq_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_octetseq_t));
 | 
			
		||||
        if (deser_uint32 (&x->length, dd, srcoff) < 0 || dd->bufsz - *srcoff < x->length)
 | 
			
		||||
| 
						 | 
				
			
			@ -400,22 +456,6 @@ static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict ds
 | 
			
		|||
        *dstoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XZ: { /* string sequence: repeatedly read a string */
 | 
			
		||||
        ddsi_stringseq_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_stringseq_t));
 | 
			
		||||
        /* sequence of string: length <data> length <data> ..., where each length is aligned
 | 
			
		||||
           to a multiple of 4 bytes and the lengths are all at least 1, therefore all but the
 | 
			
		||||
           last entry need 8 bytes and the final one at least 5; checking this protects us
 | 
			
		||||
           against allocating large amount of memory */
 | 
			
		||||
        if (deser_uint32 (&x->n, dd, srcoff) < 0 || x->n > (dd->bufsz - *srcoff + 7) / 8)
 | 
			
		||||
          goto fail;
 | 
			
		||||
        x->strs = x->n ? ddsrt_malloc (x->n * sizeof (*x->strs)) : NULL;
 | 
			
		||||
        size_t tmpoff = 0;
 | 
			
		||||
        for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
          if (deser_generic (x->strs, &tmpoff, flagset, flag, dd, srcoff, (enum pserop []) { XS, XSTOP }) < 0)
 | 
			
		||||
            goto fail;
 | 
			
		||||
        *dstoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XE1: case XE2: case XE3: { /* enum with max allowed value */
 | 
			
		||||
        unsigned * const x = deser_generic_dst (dst, dstoff, alignof (int));
 | 
			
		||||
        const uint32_t maxval = 1 + (uint32_t) (*desc - XE1);
 | 
			
		||||
| 
						 | 
				
			
			@ -444,15 +484,6 @@ static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict ds
 | 
			
		|||
        *dstoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xl: { /* length(s): int32_t, -1 or >= 1 */
 | 
			
		||||
        int32_t * const x = deser_generic_dst (dst, dstoff, alignof (uint32_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - Xl);
 | 
			
		||||
        for (uint32_t i = 0; i < cnt; i++)
 | 
			
		||||
          if (deser_uint32 ((uint32_t *) &x[i], dd, srcoff) < 0 || (x[i] < 1 && x[i] != DDS_LENGTH_UNLIMITED))
 | 
			
		||||
            goto fail;
 | 
			
		||||
        *dstoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XD: case XDx2: { /* duration(s): int64_t <=> int32_t.uint32_t (seconds.fraction) */
 | 
			
		||||
        dds_duration_t * const x = deser_generic_dst (dst, dstoff, alignof (dds_duration_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - XD);
 | 
			
		||||
| 
						 | 
				
			
			@ -491,6 +522,12 @@ static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict ds
 | 
			
		|||
        *dstoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XbPROP: { /* "propagate" flag, boolean, implied in serialized representation */
 | 
			
		||||
        unsigned char * const x = deser_generic_dst (dst, dstoff, alignof (unsigned char));
 | 
			
		||||
        *x = 1;
 | 
			
		||||
        *dstoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XG: { /* GUID */
 | 
			
		||||
        ddsi_guid_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_guid_t));
 | 
			
		||||
        if (dd->bufsz - *srcoff < sizeof (*x))
 | 
			
		||||
| 
						 | 
				
			
			@ -510,61 +547,139 @@ static dds_return_t deser_generic (void * __restrict dst, size_t * __restrict ds
 | 
			
		|||
        *dstoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XQ: { /* non-nested but otherwise arbitrary sequence, so no nested mallocs */
 | 
			
		||||
        ddsi_octetseq_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_octetseq_t));
 | 
			
		||||
        if (deser_uint32 (&x->length, dd, srcoff) < 0 || x->length > dd->bufsz - *srcoff)
 | 
			
		||||
          goto fail;
 | 
			
		||||
        const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
        x->value = x->length ? ddsrt_malloc (x->length * elem_size) : NULL;
 | 
			
		||||
        for (uint32_t i = 0; i < x->length; i++)
 | 
			
		||||
        {
 | 
			
		||||
          size_t elem_off = i * elem_size;
 | 
			
		||||
          if (deser_generic (x->value, &elem_off, flagset, flag, dd, srcoff, desc + 1) < 0)
 | 
			
		||||
          {
 | 
			
		||||
            ddsrt_free (x->value);
 | 
			
		||||
            goto fail;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        *dstoff += sizeof (*x);
 | 
			
		||||
        while (*++desc != XSTOP) { }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xopt: { /* remainder is optional; alignment is very nearly always 4 */
 | 
			
		||||
        bool end_of_input;
 | 
			
		||||
        if (pserop_seralign_is_1 (desc[1]))
 | 
			
		||||
          end_of_input = (*srcoff + 1 > dd->bufsz);
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          *srcoff = (*srcoff + 3) & ~(size_t)3;
 | 
			
		||||
          end_of_input = (*srcoff + 4 > dd->bufsz);
 | 
			
		||||
        }
 | 
			
		||||
        if (end_of_input)
 | 
			
		||||
        {
 | 
			
		||||
          void * const x = deser_generic_dst (dst, dstoff, pserop_memalign (desc[1]));
 | 
			
		||||
          size_t rem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
          memset (x, 0, rem_size);
 | 
			
		||||
          goto success;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
success:
 | 
			
		||||
  *flagset->present |= flag;
 | 
			
		||||
  return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
  fini_generic_partial (dst, &dstoff_in, desc_in, desc, *flagset->aliased & flag);
 | 
			
		||||
  fini_generic_embeddable (dst, &dstoff_in, desc_in, desc, *flagset->aliased & flag);
 | 
			
		||||
  *flagset->present &= ~flag;
 | 
			
		||||
  *flagset->aliased &= ~flag;
 | 
			
		||||
  return DDS_RETCODE_BAD_PARAMETER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t ser_generic_size (const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
dds_return_t plist_deser_generic (void * __restrict dst, const void * __restrict src, size_t srcsize, bool bswap, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  struct dd dd = {
 | 
			
		||||
    .buf = src,
 | 
			
		||||
    .bufsz = srcsize,
 | 
			
		||||
    .bswap = bswap,
 | 
			
		||||
    .protocol_version = {0,0},
 | 
			
		||||
    .vendorid = NN_VENDORID_ECLIPSE,
 | 
			
		||||
    .factory = NULL
 | 
			
		||||
  };
 | 
			
		||||
  uint64_t present = 0, aliased = 0;
 | 
			
		||||
  struct flagset fs = { .present = &present, .aliased = &aliased, .wanted = 1 };
 | 
			
		||||
  size_t dstoff = 0, srcoff = 0;
 | 
			
		||||
  return deser_generic (dst, &dstoff, &fs, 1, &dd, &srcoff, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ser_generic_size_embeddable (size_t *dstoff, const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
#define COMPLEX(basecase_, type_, dstoff_update_) do {                  \
 | 
			
		||||
    type_ const *x = deser_generic_src (src, &srcoff, alignof (type_)); \
 | 
			
		||||
    const uint32_t cnt = 1 + (uint32_t) (*desc - (basecase_));          \
 | 
			
		||||
    for (uint32_t xi = 0; xi < cnt; xi++, x++) { dstoff_update_; }      \
 | 
			
		||||
    srcoff += cnt * sizeof (*x);                                        \
 | 
			
		||||
  } while (0)
 | 
			
		||||
#define SIMPLE1(basecase_, type_) COMPLEX (basecase_, type_, dstoff = dstoff + sizeof (*x))
 | 
			
		||||
#define SIMPLE4(basecase_, type_) COMPLEX (basecase_, type_, dstoff = align4size (dstoff) + sizeof (*x))
 | 
			
		||||
#define SIMPLE1(basecase_, type_) COMPLEX (basecase_, type_, *dstoff = *dstoff + sizeof (*x))
 | 
			
		||||
#define SIMPLE4(basecase_, type_) COMPLEX (basecase_, type_, *dstoff = align4size (*dstoff) + sizeof (*x))
 | 
			
		||||
  while (true)
 | 
			
		||||
  {
 | 
			
		||||
    switch (*desc)
 | 
			
		||||
    {
 | 
			
		||||
      case XSTOP: return dstoff;
 | 
			
		||||
      case XO: COMPLEX (XO, ddsi_octetseq_t, dstoff = align4size (dstoff) + 4 + x->length); break;
 | 
			
		||||
      case XS: COMPLEX (XS, const char *, dstoff = align4size (dstoff) + 4 + strlen (*x) + 1); break;
 | 
			
		||||
      case XZ: COMPLEX (XZ, ddsi_stringseq_t, {
 | 
			
		||||
        dstoff = align4size (dstoff) + 4;
 | 
			
		||||
        for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
          dstoff = align4size (dstoff) + 4 + strlen (x->strs[i]) + 1;
 | 
			
		||||
      }); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: COMPLEX (*desc, unsigned, dstoff = align4size (dstoff) + 4); break;
 | 
			
		||||
      case XSTOP: return;
 | 
			
		||||
      case XO: COMPLEX (XO, ddsi_octetseq_t, *dstoff = align4size (*dstoff) + 4 + x->length); break;
 | 
			
		||||
      case XS: COMPLEX (XS, const char *, *dstoff = align4size (*dstoff) + 4 + strlen (*x) + 1); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: COMPLEX (*desc, unsigned, *dstoff = align4size (*dstoff) + 4); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: SIMPLE4 (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: SIMPLE4 (Xu, uint32_t); break;
 | 
			
		||||
      case Xl: SIMPLE4 (Xl, int32_t); break;
 | 
			
		||||
      case XD: case XDx2: SIMPLE4 (XD, dds_duration_t); break;
 | 
			
		||||
      case Xo: case Xox2: SIMPLE1 (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: SIMPLE1 (Xb, unsigned char); break;
 | 
			
		||||
      case XbCOND: SIMPLE1 (XbCOND, unsigned char); break;
 | 
			
		||||
      case XG: SIMPLE1 (XG, ddsi_guid_t); break;
 | 
			
		||||
      case XK: SIMPLE1 (XK, nn_keyhash_t); break;
 | 
			
		||||
      case XbPROP: /* "propagate" boolean: when 'false'; no serialisation; no size; force early out */
 | 
			
		||||
               COMPLEX (XbPROP, unsigned char, if (! *x) return); break;
 | 
			
		||||
      case XQ: COMPLEX (XQ, ddsi_octetseq_t, {
 | 
			
		||||
        const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
        *dstoff = align4size (*dstoff) + 4;
 | 
			
		||||
        for (uint32_t i = 0; i < x->length; i++)
 | 
			
		||||
          ser_generic_size_embeddable (dstoff, x->value, i * elem_size, desc + 1);
 | 
			
		||||
      }); while (*++desc != XSTOP) { } break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
#undef SIMPLE
 | 
			
		||||
#undef SIMPLE4
 | 
			
		||||
#undef SIMPLE1
 | 
			
		||||
#undef COMPLEX
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t ser_generic (struct nn_xmsg *xmsg, nn_parameterid_t pid, const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
static size_t ser_generic_size (const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  char * const data = nn_xmsg_addpar (xmsg, pid, ser_generic_size (src, srcoff, desc));
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
  ser_generic_size_embeddable (&dstoff, src, srcoff, desc);
 | 
			
		||||
  return dstoff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t ser_generic_count (const ddsi_octetseq_t *src, size_t elem_size, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  /* This whole thing exists solely for dealing with the vile "propagate" boolean, which must come first in an
 | 
			
		||||
     element, or one can't deserialize it at all.  Therefore, if desc doesn't start with XbPROP, all "length"
 | 
			
		||||
     elements are included in the output */
 | 
			
		||||
  if (*desc != XbPROP)
 | 
			
		||||
    return src->length;
 | 
			
		||||
  /* and if it does start with XbPROP, only those for which it is true are included */
 | 
			
		||||
  uint32_t count = 0;
 | 
			
		||||
  for (uint32_t i = 0; i < src->length; i++)
 | 
			
		||||
    if (src->value[i * elem_size])
 | 
			
		||||
      count++;
 | 
			
		||||
  return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t ser_generic_embeddable (char * const data, size_t *dstoff, const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  while (true)
 | 
			
		||||
  {
 | 
			
		||||
    switch (*desc)
 | 
			
		||||
| 
						 | 
				
			
			@ -573,134 +688,151 @@ static dds_return_t ser_generic (struct nn_xmsg *xmsg, nn_parameterid_t pid, con
 | 
			
		|||
        return 0;
 | 
			
		||||
      case XO: { /* octet sequence */
 | 
			
		||||
        ddsi_octetseq_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_octetseq_t));
 | 
			
		||||
        char * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        char * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        *((uint32_t *) p) = x->length;
 | 
			
		||||
        if (x->length) memcpy (p + 4, x->value, x->length);
 | 
			
		||||
        dstoff += 4 + x->length;
 | 
			
		||||
        *dstoff += 4 + x->length;
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XS: { /* string */
 | 
			
		||||
        char const * const * const x = deser_generic_src (src, &srcoff, alignof (char *));
 | 
			
		||||
        const uint32_t size = (uint32_t) (strlen (*x) + 1);
 | 
			
		||||
        char * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        char * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        *((uint32_t *) p) = size;
 | 
			
		||||
        memcpy (p + 4, *x, size);
 | 
			
		||||
        dstoff += 4 + size;
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XZ: { /* string sequence */
 | 
			
		||||
        ddsi_stringseq_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_stringseq_t));
 | 
			
		||||
        char * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        *((uint32_t *) p) = x->n;
 | 
			
		||||
        dstoff += 4;
 | 
			
		||||
        for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
        {
 | 
			
		||||
          char * const q = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
          const uint32_t size = (uint32_t) (strlen (x->strs[i]) + 1);
 | 
			
		||||
          *((uint32_t *) q) = size;
 | 
			
		||||
          memcpy (q + 4, x->strs[i], size);
 | 
			
		||||
          dstoff += 4 + size;
 | 
			
		||||
        }
 | 
			
		||||
        *dstoff += 4 + size;
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XE1: case XE2: case XE3: { /* enum */
 | 
			
		||||
        unsigned const * const x = deser_generic_src (src, &srcoff, alignof (unsigned));
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        *p = (uint32_t) *x;
 | 
			
		||||
        dstoff += 4;
 | 
			
		||||
        *dstoff += 4;
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: { /* int32_t(s) */
 | 
			
		||||
        int32_t const * const x = deser_generic_src (src, &srcoff, alignof (int32_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - Xi);
 | 
			
		||||
        int32_t * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        int32_t * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        for (uint32_t i = 0; i < cnt; i++)
 | 
			
		||||
          p[i] = x[i];
 | 
			
		||||
        dstoff += cnt * sizeof (*x);
 | 
			
		||||
        *dstoff += cnt * sizeof (*x);
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5:  { /* uint32_t(s) */
 | 
			
		||||
        uint32_t const * const x = deser_generic_src (src, &srcoff, alignof (uint32_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - Xu);
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        for (uint32_t i = 0; i < cnt; i++)
 | 
			
		||||
          p[i] = x[i];
 | 
			
		||||
        dstoff += cnt * sizeof (*x);
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case Xl: { /* int32_t(s) */
 | 
			
		||||
        int32_t const * const x = deser_generic_src (src, &srcoff, alignof (uint32_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - Xu);
 | 
			
		||||
        int32_t * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        for (uint32_t i = 0; i < cnt; i++)
 | 
			
		||||
          p[i] = x[i];
 | 
			
		||||
        dstoff += cnt * sizeof (*x);
 | 
			
		||||
        *dstoff += cnt * sizeof (*x);
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XD: case XDx2: { /* duration(s): int64_t <=> int32_t.uint32_t (seconds.fraction) */
 | 
			
		||||
        dds_duration_t const * const x = deser_generic_src (src, &srcoff, alignof (dds_duration_t));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - XD);
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, &dstoff);
 | 
			
		||||
        uint32_t * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        for (uint32_t i = 0; i < cnt; i++)
 | 
			
		||||
        {
 | 
			
		||||
          ddsi_duration_t tmp = nn_to_ddsi_duration (x[i]);
 | 
			
		||||
          p[2 * i + 0] = (uint32_t) tmp.seconds;
 | 
			
		||||
          p[2 * i + 1] = tmp.fraction;
 | 
			
		||||
        }
 | 
			
		||||
        dstoff += 2 * cnt * sizeof (uint32_t);
 | 
			
		||||
        *dstoff += 2 * cnt * sizeof (uint32_t);
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xo: case Xox2: { /* octet(s) */
 | 
			
		||||
        unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char));
 | 
			
		||||
        const uint32_t cnt = 1 + (uint32_t) (*desc - Xo);
 | 
			
		||||
        char * const p = data + dstoff;
 | 
			
		||||
        char * const p = data + *dstoff;
 | 
			
		||||
        memcpy (p, x, cnt);
 | 
			
		||||
        dstoff += cnt;
 | 
			
		||||
        *dstoff += cnt;
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xb: case Xbx2: case XbCOND: { /* boolean(s) */
 | 
			
		||||
        unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char));
 | 
			
		||||
        const uint32_t cnt = (*desc == Xbx2) ? 2 : 1; /* <<<< beware! */
 | 
			
		||||
        char * const p = data + dstoff;
 | 
			
		||||
        char * const p = data + *dstoff;
 | 
			
		||||
        memcpy (p, x, cnt);
 | 
			
		||||
        dstoff += cnt;
 | 
			
		||||
        *dstoff += cnt;
 | 
			
		||||
        srcoff += cnt * sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XbPROP: { /* "propagate" boolean: don't serialize, skip it and everything that follows if false */
 | 
			
		||||
        unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char));
 | 
			
		||||
        if (! *x) return 0;
 | 
			
		||||
        srcoff++;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XG: { /* GUID */
 | 
			
		||||
        ddsi_guid_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_guid_t));
 | 
			
		||||
        const ddsi_guid_t xn = nn_hton_guid (*x);
 | 
			
		||||
        char * const p = data + dstoff;
 | 
			
		||||
        char * const p = data + *dstoff;
 | 
			
		||||
        memcpy (p, &xn, sizeof (xn));
 | 
			
		||||
        dstoff += sizeof (xn);
 | 
			
		||||
        *dstoff += sizeof (xn);
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XK: { /* keyhash */
 | 
			
		||||
        nn_keyhash_t const * const x = deser_generic_src (src, &srcoff, alignof (nn_keyhash_t));
 | 
			
		||||
        char * const p = data + dstoff;
 | 
			
		||||
        char * const p = data + *dstoff;
 | 
			
		||||
        memcpy (p, x, sizeof (*x));
 | 
			
		||||
        dstoff += sizeof (*x);
 | 
			
		||||
        *dstoff += sizeof (*x);
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case XQ: {
 | 
			
		||||
        ddsi_octetseq_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_octetseq_t));
 | 
			
		||||
        char * const p = ser_generic_align4 (data, dstoff);
 | 
			
		||||
        *dstoff += 4;
 | 
			
		||||
        if (x->length == 0)
 | 
			
		||||
          *((uint32_t *) p) = 0;
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
          *((uint32_t *) p) = ser_generic_count (x, elem_size, desc + 1);
 | 
			
		||||
          for (uint32_t i = 0; i < x->length; i++)
 | 
			
		||||
            ser_generic_embeddable (data, dstoff, x->value, i * elem_size, desc + 1);
 | 
			
		||||
        }
 | 
			
		||||
        srcoff += sizeof (*x);
 | 
			
		||||
        while (*++desc != XSTOP) { }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case Xopt:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t unalias_generic (void * __restrict dst, size_t * __restrict dstoff, const enum pserop * __restrict desc)
 | 
			
		||||
static dds_return_t ser_generic (struct nn_xmsg *xmsg, nn_parameterid_t pid, const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  char * const data = nn_xmsg_addpar (xmsg, pid, ser_generic_size (src, srcoff, desc));
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
  return ser_generic_embeddable (data, &dstoff, src, srcoff, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dds_return_t plist_ser_generic (void **dst, size_t *dstsize, const void *src, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  const size_t srcoff = 0;
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
  dds_return_t ret;
 | 
			
		||||
  *dstsize = ser_generic_size (src, srcoff, desc);
 | 
			
		||||
  if ((*dst = ddsrt_malloc (*dstsize == 0 ? 1 : *dstsize)) == NULL)
 | 
			
		||||
    return DDS_RETCODE_OUT_OF_RESOURCES;
 | 
			
		||||
  ret = ser_generic_embeddable (*dst, &dstoff, src, srcoff, desc);
 | 
			
		||||
  assert (dstoff == *dstsize);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t unalias_generic (void * __restrict dst, size_t * __restrict dstoff, bool gen_seq_aliased, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
#define COMPLEX(basecase_, type_, ...) do {                      \
 | 
			
		||||
    type_ *x = deser_generic_dst (dst, dstoff, alignof (type_)); \
 | 
			
		||||
| 
						 | 
				
			
			@ -717,21 +849,36 @@ static dds_return_t unalias_generic (void * __restrict dst, size_t * __restrict
 | 
			
		|||
        return 0;
 | 
			
		||||
      case XO: COMPLEX (XO, ddsi_octetseq_t, if (x->value) { x->value = ddsrt_memdup (x->value, x->length); }); break;
 | 
			
		||||
      case XS: COMPLEX (XS, char *, if (*x) { *x = ddsrt_strdup (*x); }); break;
 | 
			
		||||
      case XZ: COMPLEX (XZ, ddsi_stringseq_t, if (x->n) {
 | 
			
		||||
        x->strs = ddsrt_memdup (x->strs, x->n * sizeof (*x->strs));
 | 
			
		||||
        for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
          x->strs[i] = ddsrt_strdup (x->strs[i]);
 | 
			
		||||
      }); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: COMPLEX (*desc, unsigned, (void) 0); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: SIMPLE (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: SIMPLE (Xu, uint32_t); break;
 | 
			
		||||
      case Xl: SIMPLE (Xl, int32_t); break;
 | 
			
		||||
      case XD: case XDx2: SIMPLE (XD, dds_duration_t); break;
 | 
			
		||||
      case Xo: case Xox2: SIMPLE (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break;
 | 
			
		||||
      case XbCOND: SIMPLE (XbCOND, unsigned char); break;
 | 
			
		||||
      case XbPROP: SIMPLE (XbPROP, unsigned char); break;
 | 
			
		||||
      case XG: SIMPLE (XG, ddsi_guid_t); break;
 | 
			
		||||
      case XK: SIMPLE (XK, nn_keyhash_t); break;
 | 
			
		||||
      case XQ: COMPLEX (XQ, ddsi_octetseq_t, if (x->length) {
 | 
			
		||||
        const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
        if (gen_seq_aliased)
 | 
			
		||||
        {
 | 
			
		||||
          /* The memory for the elements of a generic sequence are owned by the plist, the only aliased bits
 | 
			
		||||
             are the strings (XS) and octet sequences (XO) embedded in the elements.  So in principle, an
 | 
			
		||||
             unalias operation on a generic sequence should only operate on the elements of the sequence,
 | 
			
		||||
             not on the sequence buffer itself.
 | 
			
		||||
 | 
			
		||||
             However, the "mergein_missing" operation (and consequently the copy & dup operations) memcpy the
 | 
			
		||||
             source, then pretend it is aliased.  In this case, the sequence buffer is aliased, rather than
 | 
			
		||||
             private, and hence a new copy needs to be allocated. */
 | 
			
		||||
          x->value = ddsrt_memdup (x->value, x->length * elem_size);
 | 
			
		||||
        }
 | 
			
		||||
        for (uint32_t i = 0; i < x->length; i++) {
 | 
			
		||||
          size_t elem_off = i * elem_size;
 | 
			
		||||
          unalias_generic (x->value, &elem_off, gen_seq_aliased, desc + 1);
 | 
			
		||||
        }
 | 
			
		||||
      }); while (*++desc != XSTOP) { } break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -739,13 +886,19 @@ static dds_return_t unalias_generic (void * __restrict dst, size_t * __restrict
 | 
			
		|||
#undef COMPLEX
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dds_return_t plist_unalias_generic (void * __restrict dst, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
  return unalias_generic (dst, &dstoff, false, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool unalias_generic_required (const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  while (*desc != XSTOP)
 | 
			
		||||
  {
 | 
			
		||||
    switch (*desc++)
 | 
			
		||||
    {
 | 
			
		||||
      case XO: case XS: case XZ:
 | 
			
		||||
      case XO: case XS: case XQ:
 | 
			
		||||
        return true;
 | 
			
		||||
      default:
 | 
			
		||||
        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -762,10 +915,16 @@ static bool fini_generic_required (const enum pserop * __restrict desc)
 | 
			
		|||
 | 
			
		||||
static dds_return_t fini_generic (void * __restrict dst, size_t * __restrict dstoff, struct flagset *flagset, uint64_t flag, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  fini_generic_partial (dst, dstoff, desc, NULL, *flagset->aliased & flag);
 | 
			
		||||
  fini_generic_embeddable (dst, dstoff, desc, NULL, *flagset->aliased & flag);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void plist_fini_generic (void * __restrict dst, const enum pserop *desc, bool aliased)
 | 
			
		||||
{
 | 
			
		||||
  size_t dstoff = 0;
 | 
			
		||||
  fini_generic_embeddable (dst, &dstoff, desc, NULL, aliased);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dds_return_t valid_generic (const void *src, size_t srcoff, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
#define COMPLEX(basecase_, type_, cond_stmts_) do {                     \
 | 
			
		||||
| 
						 | 
				
			
			@ -783,23 +942,29 @@ static dds_return_t valid_generic (const void *src, size_t srcoff, const enum ps
 | 
			
		|||
      case XSTOP: return 0;
 | 
			
		||||
      case XO: SIMPLE (XO, ddsi_octetseq_t, (x->length == 0) == (x->value == NULL)); break;
 | 
			
		||||
      case XS: SIMPLE (XS, const char *, *x != NULL); break;
 | 
			
		||||
      case XZ: COMPLEX (XZ, ddsi_stringseq_t, {
 | 
			
		||||
        if ((x->n == 0) != (x->strs == NULL))
 | 
			
		||||
          return DDS_RETCODE_BAD_PARAMETER;
 | 
			
		||||
        for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
          if (x->strs[i] == NULL)
 | 
			
		||||
            return DDS_RETCODE_BAD_PARAMETER;
 | 
			
		||||
      }); break;
 | 
			
		||||
      case XE1: case XE2: case XE3: SIMPLE (*desc, unsigned, *x <= 1 + (unsigned) *desc - XE1); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: TRIVIAL (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: TRIVIAL (Xu, uint32_t); break;
 | 
			
		||||
      case Xl: SIMPLE (Xl, int32_t, *x == DDS_LENGTH_UNLIMITED || *x > 1); break;
 | 
			
		||||
      case XD: case XDx2: SIMPLE (XD, dds_duration_t, *x >= 0); break;
 | 
			
		||||
      case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: SIMPLE (Xb, unsigned char, *x == 0 || *x == 1); break;
 | 
			
		||||
      case XbCOND: SIMPLE (XbCOND, unsigned char, *x == 0 || *x == 1); break;
 | 
			
		||||
      case XbPROP: SIMPLE (XbPROP, unsigned char, *x == 0 || *x == 1); break;
 | 
			
		||||
      case XG: TRIVIAL (XG, ddsi_guid_t); break;
 | 
			
		||||
      case XK: TRIVIAL (XK, nn_keyhash_t); break;
 | 
			
		||||
      case XQ: COMPLEX (XQ, ddsi_octetseq_t, {
 | 
			
		||||
        if ((x->length == 0) != (x->value == NULL))
 | 
			
		||||
          return DDS_RETCODE_BAD_PARAMETER;
 | 
			
		||||
        if (x->length) {
 | 
			
		||||
          const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
          dds_return_t ret;
 | 
			
		||||
          for (uint32_t i = 0; i < x->length; i++) {
 | 
			
		||||
            if ((ret = valid_generic (x->value, i * elem_size, desc + 1)) != 0)
 | 
			
		||||
              return ret;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }); while (*++desc != XSTOP) { } break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -833,19 +998,9 @@ static bool equal_generic (const void *srcx, const void *srcy, size_t srcoff, co
 | 
			
		|||
      case XS:
 | 
			
		||||
        SIMPLE (XS, const char *, strcmp (*x, *y) == 0);
 | 
			
		||||
        break;
 | 
			
		||||
      case XZ:
 | 
			
		||||
        COMPLEX (XZ, ddsi_stringseq_t, {
 | 
			
		||||
          if (x->n != y->n)
 | 
			
		||||
            return false;
 | 
			
		||||
          for (uint32_t i = 0; i < x->n; i++)
 | 
			
		||||
            if (strcmp (x->strs[i], y->strs[i]) != 0)
 | 
			
		||||
              return false;
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
      case XE1: case XE2: case XE3: TRIVIAL (*desc, unsigned); break;
 | 
			
		||||
      case Xi: case Xix2: case Xix3: case Xix4: TRIVIAL (Xi, int32_t); break;
 | 
			
		||||
      case Xu: case Xux2: case Xux3: case Xux4: case Xux5: TRIVIAL (Xu, uint32_t); break;
 | 
			
		||||
      case Xl: TRIVIAL (Xl, int32_t); break;
 | 
			
		||||
      case XD: case XDx2: TRIVIAL (XD, dds_duration_t); break;
 | 
			
		||||
      case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break;
 | 
			
		||||
      case Xb: case Xbx2: TRIVIAL (Xb, unsigned char); break;
 | 
			
		||||
| 
						 | 
				
			
			@ -857,8 +1012,21 @@ static bool equal_generic (const void *srcx, const void *srcy, size_t srcoff, co
 | 
			
		|||
            return true;
 | 
			
		||||
        });
 | 
			
		||||
        break;
 | 
			
		||||
      case XG: SIMPLE (XG, ddsi_guid_t, memcmp (x, y, sizeof (*x))); break;
 | 
			
		||||
      case XK: SIMPLE (XK, nn_keyhash_t, memcmp (x, y, sizeof (*x))); break;
 | 
			
		||||
      case XbPROP: TRIVIAL (XbPROP, unsigned char); break;
 | 
			
		||||
      case XG: SIMPLE (XG, ddsi_guid_t, memcmp (x, y, sizeof (*x)) == 0); break;
 | 
			
		||||
      case XK: SIMPLE (XK, nn_keyhash_t, memcmp (x, y, sizeof (*x)) == 0); break;
 | 
			
		||||
      case XQ: COMPLEX (XQ, ddsi_octetseq_t, {
 | 
			
		||||
        if (x->length != y->length)
 | 
			
		||||
          return false;
 | 
			
		||||
        if (x->length) {
 | 
			
		||||
          const size_t elem_size = ser_generic_srcsize (desc + 1);
 | 
			
		||||
          for (uint32_t i = 0; i < x->length; i++) {
 | 
			
		||||
            if (!equal_generic (x->value, y->value, i * elem_size, desc + 1))
 | 
			
		||||
              return false;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }); while (*++desc != XSTOP) { } break;
 | 
			
		||||
      case Xopt: break;
 | 
			
		||||
    }
 | 
			
		||||
    desc++;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -867,6 +1035,11 @@ static bool equal_generic (const void *srcx, const void *srcy, size_t srcoff, co
 | 
			
		|||
#undef COMPLEX
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool plist_equal_generic (const void *srcx, const void *srcy, const enum pserop * __restrict desc)
 | 
			
		||||
{
 | 
			
		||||
  return equal_generic (srcx, srcy, 0, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define membersize(type, member) sizeof (((type *) 0)->member)
 | 
			
		||||
#define ENTRY(PFX_, NAME_, member_, flag_, validate_, ...)                               \
 | 
			
		||||
  { PID_##NAME_, flag_, PFX_##_##NAME_, #NAME_, offsetof (struct nn_plist, member_),     \
 | 
			
		||||
| 
						 | 
				
			
			@ -951,6 +1124,7 @@ static dds_return_t dvx_reader_favours_ssm (void * __restrict dst, const struct
 | 
			
		|||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Standardized parameters -- QoS _MUST_ come first (nn_plist_init_tables verifies this) because
 | 
			
		||||
   it allows early-out when processing a dds_qos_t instead of an nn_plist_t */
 | 
			
		||||
static const struct piddesc piddesc_omg[] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -966,6 +1140,9 @@ static const struct piddesc piddesc_omg[] = {
 | 
			
		|||
  QP  (DEADLINE,                            deadline, XD),
 | 
			
		||||
  QP  (LATENCY_BUDGET,                      latency_budget, XD),
 | 
			
		||||
  QP  (LIVELINESS,                          liveliness, XE2, XD),
 | 
			
		||||
  /* Property list used to be of type [(String,String]), security changed into ([String,String],Maybe [(String,[Word8])]),
 | 
			
		||||
     the "Xopt" here is to allow both forms on input, with an assumed empty second sequence if the old form was received */
 | 
			
		||||
  QP  (PROPERTY_LIST,                       property, XQ, XbPROP, XS, XS, XSTOP, Xopt, XQ, XbPROP, XS, XO, XSTOP),
 | 
			
		||||
  /* Reliability encoding does not follow the rules (best-effort/reliable map to 1/2 instead of 0/1 */
 | 
			
		||||
  { PID_RELIABILITY, PDF_QOS | PDF_FUNCTION, QP_RELIABILITY, "RELIABILITY",
 | 
			
		||||
    offsetof (struct nn_plist, qos.reliability), membersize (struct nn_plist, qos.reliability),
 | 
			
		||||
| 
						 | 
				
			
			@ -978,7 +1155,7 @@ static const struct piddesc piddesc_omg[] = {
 | 
			
		|||
  QP  (OWNERSHIP,                           ownership, XE1),
 | 
			
		||||
  QP  (OWNERSHIP_STRENGTH,                  ownership_strength, Xi),
 | 
			
		||||
  QP  (PRESENTATION,                        presentation, XE2, Xbx2),
 | 
			
		||||
  QP  (PARTITION,                           partition, XZ),
 | 
			
		||||
  QP  (PARTITION,                           partition, XQ, XS, XSTOP),
 | 
			
		||||
  QP  (TIME_BASED_FILTER,                   time_based_filter, XD),
 | 
			
		||||
  QP  (TRANSPORT_PRIORITY,                  transport_priority, Xi),
 | 
			
		||||
  PP  (PROTOCOL_VERSION,                    protocol_version, Xox2),
 | 
			
		||||
| 
						 | 
				
			
			@ -1038,7 +1215,7 @@ static const struct piddesc piddesc_eclipse[] = {
 | 
			
		|||
  QP  (PRISMTECH_READER_LIFESPAN,           reader_lifespan, Xb, XD),
 | 
			
		||||
  QP  (PRISMTECH_WRITER_DATA_LIFECYCLE,     writer_data_lifecycle, Xb),
 | 
			
		||||
  QP  (PRISMTECH_READER_DATA_LIFECYCLE,     reader_data_lifecycle, XDx2),
 | 
			
		||||
  QP  (PRISMTECH_SUBSCRIPTION_KEYS,         subscription_keys, XbCOND, XZ),
 | 
			
		||||
  QP  (PRISMTECH_SUBSCRIPTION_KEYS,         subscription_keys, XbCOND, XQ, XS, XSTOP),
 | 
			
		||||
  { PID_PAD, PDF_QOS, QP_CYCLONE_IGNORELOCAL, "CYCLONE_IGNORELOCAL",
 | 
			
		||||
    offsetof (struct nn_plist, qos.ignorelocal), membersize (struct nn_plist, qos.ignorelocal),
 | 
			
		||||
    { .desc = { XE2, XSTOP } }, 0 },
 | 
			
		||||
| 
						 | 
				
			
			@ -1057,7 +1234,7 @@ static const struct piddesc piddesc_prismtech[] = {
 | 
			
		|||
  QP  (PRISMTECH_READER_LIFESPAN,           reader_lifespan, Xb, XD),
 | 
			
		||||
  QP  (PRISMTECH_WRITER_DATA_LIFECYCLE,     writer_data_lifecycle, Xb),
 | 
			
		||||
  QP  (PRISMTECH_READER_DATA_LIFECYCLE,     reader_data_lifecycle, XDx2),
 | 
			
		||||
  QP  (PRISMTECH_SUBSCRIPTION_KEYS,         subscription_keys, XbCOND, XZ),
 | 
			
		||||
  QP  (PRISMTECH_SUBSCRIPTION_KEYS,         subscription_keys, XbCOND, XQ, XS, XSTOP),
 | 
			
		||||
  PP  (PRISMTECH_BUILTIN_ENDPOINT_SET,      prismtech_builtin_endpoint_set, Xu),
 | 
			
		||||
  PP  (PRISMTECH_PARTICIPANT_VERSION_INFO,  prismtech_participant_version_info, Xux5, XS),
 | 
			
		||||
  PP  (PRISMTECH_EXEC_NAME,                 exec_name, XS),
 | 
			
		||||
| 
						 | 
				
			
			@ -1141,8 +1318,8 @@ static const struct piddesc_index piddesc_vendor_index[] = {
 | 
			
		|||
/* List of entries that require unalias, fini processing;
 | 
			
		||||
   initialized by nn_plist_init_tables; will assert when
 | 
			
		||||
   table too small or too large */
 | 
			
		||||
static const struct piddesc *piddesc_unalias[18];
 | 
			
		||||
static const struct piddesc *piddesc_fini[18];
 | 
			
		||||
static const struct piddesc *piddesc_unalias[19];
 | 
			
		||||
static const struct piddesc *piddesc_fini[19];
 | 
			
		||||
static ddsrt_once_t table_init_control = DDSRT_ONCE_INIT;
 | 
			
		||||
 | 
			
		||||
static nn_parameterid_t pid_without_flags (nn_parameterid_t pid)
 | 
			
		||||
| 
						 | 
				
			
			@ -1331,7 +1508,7 @@ static void plist_or_xqos_unalias (void * __restrict dst, size_t shift)
 | 
			
		|||
    if ((*fs->present & entry->present_flag) && (*fs->aliased & entry->present_flag))
 | 
			
		||||
    {
 | 
			
		||||
      if (!(entry->flags & PDF_FUNCTION))
 | 
			
		||||
        unalias_generic (dst, &dstoff, entry->op.desc);
 | 
			
		||||
        unalias_generic (dst, &dstoff, false, entry->op.desc);
 | 
			
		||||
      else if (entry->op.f.unalias)
 | 
			
		||||
        entry->op.f.unalias (dst, &dstoff);
 | 
			
		||||
      *fs->aliased &= ~entry->present_flag;
 | 
			
		||||
| 
						 | 
				
			
			@ -1389,15 +1566,15 @@ static void plist_or_xqos_mergein_missing (void * __restrict dst, const void * _
 | 
			
		|||
      if (!(*fs_dst->present & entry->present_flag) && (*fs_src->present & mask & entry->present_flag))
 | 
			
		||||
      {
 | 
			
		||||
        /* bitwise copy, mark as aliased & unalias; have to unalias fields one-by-one rather than
 | 
			
		||||
         do this for all fields and call "unalias" on the entire object because fields that are
 | 
			
		||||
         already present may be aliased, and it would be somewhat impolite to change that.
 | 
			
		||||
           do this for all fields and call "unalias" on the entire object because fields that are
 | 
			
		||||
           already present may be aliased, and it would be somewhat impolite to change that.
 | 
			
		||||
 | 
			
		||||
         Note: dst & src have the same type, so offset in src is the same;
 | 
			
		||||
         Note: unalias may have to look at */
 | 
			
		||||
           Note: dst & src have the same type, so offset in src is the same;
 | 
			
		||||
           Note: unalias may have to look at */
 | 
			
		||||
        memcpy ((char *) dst + dstoff, (const char *) src + dstoff, entry->size);
 | 
			
		||||
        *fs_dst->present |= entry->present_flag;
 | 
			
		||||
        if (!(entry->flags & PDF_FUNCTION))
 | 
			
		||||
          unalias_generic (dst, &dstoff, entry->op.desc);
 | 
			
		||||
          unalias_generic (dst, &dstoff, true, entry->op.desc);
 | 
			
		||||
        else if (entry->op.f.unalias)
 | 
			
		||||
          entry->op.f.unalias (dst, &dstoff);
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -2228,7 +2405,7 @@ const unsigned char *nn_plist_findparam_native_unchecked (const void *src, nn_pa
 | 
			
		|||
  const nn_parameter_t *par = src;
 | 
			
		||||
  while (par->parameterid != pid)
 | 
			
		||||
  {
 | 
			
		||||
    if (pid == PID_SENTINEL)
 | 
			
		||||
    if (par->parameterid == PID_SENTINEL)
 | 
			
		||||
      return NULL;
 | 
			
		||||
    par = (const nn_parameter_t *) ((const char *) (par + 1) + par->length);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -2763,6 +2940,29 @@ void nn_log_xqos (uint32_t cat, const struct ddsrt_log_cfg *logcfg, const dds_qo
 | 
			
		|||
  });
 | 
			
		||||
  DO (PRISMTECH_ENTITY_FACTORY, { LOGB1 ("entity_factory=%u", xqos->entity_factory.autoenable_created_entities); });
 | 
			
		||||
  DO (CYCLONE_IGNORELOCAL, { LOGB1 ("ignorelocal=%u", xqos->ignorelocal.value); });
 | 
			
		||||
  DO (PROPERTY_LIST, {
 | 
			
		||||
    LOGB0 ("property_list={");
 | 
			
		||||
    DDS_CLOG (cat, logcfg, "value={");
 | 
			
		||||
    for (uint32_t i = 0; i < xqos->property.value.n; i++) {
 | 
			
		||||
      DDS_CLOG (cat, logcfg, "%s{%s,%s,%u}",
 | 
			
		||||
                                      (i == 0) ? "" : ",",
 | 
			
		||||
                                      xqos->property.value.props[i].name,
 | 
			
		||||
                                      xqos->property.value.props[i].value,
 | 
			
		||||
                                      xqos->property.value.props[i].propagate);
 | 
			
		||||
    }
 | 
			
		||||
    DDS_CLOG (cat, logcfg, "}");
 | 
			
		||||
    DDS_CLOG (cat, logcfg, "binary_value={");
 | 
			
		||||
    for (uint32_t i = 0; i < xqos->property.binary_value.n; i++) {
 | 
			
		||||
      DDS_CLOG (cat, logcfg, "%s{%s,(%u,%p),%u}",
 | 
			
		||||
                                      (i == 0) ? "" : ",",
 | 
			
		||||
                                      xqos->property.binary_value.props[i].name,
 | 
			
		||||
                                      xqos->property.binary_value.props[i].value.length,
 | 
			
		||||
                                      xqos->property.binary_value.props[i].value.value,
 | 
			
		||||
                                      xqos->property.binary_value.props[i].propagate);
 | 
			
		||||
    }
 | 
			
		||||
    DDS_CLOG (cat, logcfg, "}");
 | 
			
		||||
    DDS_CLOG (cat, logcfg, "}");
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
#undef PRINTARG_DUR
 | 
			
		||||
#undef FMT_DUR
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/core/ddsi/tests/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/core/ddsi/tests/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
#
 | 
			
		||||
# Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
 | 
			
		||||
#
 | 
			
		||||
# This program and the accompanying materials are made available under the
 | 
			
		||||
# terms of the Eclipse Public License v. 2.0 which is available at
 | 
			
		||||
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
 | 
			
		||||
# v. 1.0 which is available at
 | 
			
		||||
# http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 | 
			
		||||
#
 | 
			
		||||
include(CUnit)
 | 
			
		||||
 | 
			
		||||
set(ddsi_test_sources
 | 
			
		||||
    "plist_generic.c"
 | 
			
		||||
    "plist.c")
 | 
			
		||||
 | 
			
		||||
add_cunit_executable(cunit_ddsi ${ddsi_test_sources})
 | 
			
		||||
target_include_directories(
 | 
			
		||||
  cunit_ddsi PRIVATE
 | 
			
		||||
  "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../ddsi/include/>")
 | 
			
		||||
target_link_libraries(cunit_ddsi PRIVATE ddsc)
 | 
			
		||||
							
								
								
									
										170
									
								
								src/core/ddsi/tests/plist.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/core/ddsi/tests/plist.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright(c) 2019 ADLINK Technology Limited and others
 | 
			
		||||
 *
 | 
			
		||||
 * This program and the accompanying materials are made available under the
 | 
			
		||||
 * terms of the Eclipse Public License v. 2.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
 | 
			
		||||
 * v. 1.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "CUnit/Theory.h"
 | 
			
		||||
#include "dds/ddsrt/heap.h"
 | 
			
		||||
#include "dds/ddsrt/string.h"
 | 
			
		||||
#include "dds/ddsrt/endian.h"
 | 
			
		||||
#include "dds/ddsi/q_xqos.h"
 | 
			
		||||
#include "dds/ddsi/q_plist.h"
 | 
			
		||||
 | 
			
		||||
CU_Test (ddsi_plist, unalias_copy_merge)
 | 
			
		||||
{
 | 
			
		||||
  /* one int, one string and one string sequence covers most cases */
 | 
			
		||||
  nn_plist_t p0, p0memcpy;
 | 
			
		||||
  char *p0strs[3];
 | 
			
		||||
  nn_plist_init_empty (&p0);
 | 
			
		||||
  p0.present = PP_PRISMTECH_PROCESS_ID | PP_ENTITY_NAME;
 | 
			
		||||
  p0.aliased = PP_ENTITY_NAME;
 | 
			
		||||
  p0.process_id = 0x12345678;
 | 
			
		||||
  p0.entity_name = "nemo";
 | 
			
		||||
  p0.qos.present = QP_PARTITION;
 | 
			
		||||
  p0.qos.aliased = QP_PARTITION;
 | 
			
		||||
  p0.qos.partition.n = 3;
 | 
			
		||||
  p0.qos.partition.strs = ddsrt_malloc (p0.qos.partition.n * sizeof (*p0.qos.partition.strs));
 | 
			
		||||
  p0strs[0] = p0.qos.partition.strs[0] = "aap";
 | 
			
		||||
  p0strs[1] = p0.qos.partition.strs[1] = "noot";
 | 
			
		||||
  p0strs[2] = p0.qos.partition.strs[2] = "mies";
 | 
			
		||||
  memcpy (&p0memcpy, &p0, sizeof (p0));
 | 
			
		||||
 | 
			
		||||
  /* manually alias one, so we can free it*/
 | 
			
		||||
  nn_plist_t p0alias;
 | 
			
		||||
  memcpy (&p0alias, &p0, sizeof (p0));
 | 
			
		||||
  p0alias.qos.partition.strs = ddsrt_memdup (p0alias.qos.partition.strs, p0.qos.partition.n * sizeof (*p0.qos.partition.strs));
 | 
			
		||||
  nn_plist_fini (&p0alias);
 | 
			
		||||
  CU_ASSERT (memcmp (&p0, &p0memcpy, sizeof (p0)) == 0);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.entity_name, "nemo");
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[0], p0strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[1], p0strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[2], p0strs[2]);
 | 
			
		||||
 | 
			
		||||
  /* copy an aliased one; the original must be unchanged, the copy unaliased */
 | 
			
		||||
  nn_plist_t p1;
 | 
			
		||||
  nn_plist_init_empty (&p1);
 | 
			
		||||
  nn_plist_copy (&p1, &p0);
 | 
			
		||||
  CU_ASSERT (memcmp (&p0, &p0memcpy, sizeof (p0)) == 0);
 | 
			
		||||
  CU_ASSERT (p1.present == p0.present);
 | 
			
		||||
  CU_ASSERT (p1.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p1.qos.present == p0.qos.present);
 | 
			
		||||
  CU_ASSERT (p1.qos.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p1.process_id == p0.process_id);
 | 
			
		||||
  CU_ASSERT (p1.entity_name != p0.entity_name);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p1.entity_name, p0.entity_name);
 | 
			
		||||
  CU_ASSERT (p1.qos.partition.n == p0.qos.partition.n);
 | 
			
		||||
  CU_ASSERT (p1.qos.partition.strs != p0.qos.partition.strs);
 | 
			
		||||
  CU_ASSERT (p1.qos.partition.strs[0] != p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT (p1.qos.partition.strs[1] != p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT (p1.qos.partition.strs[2] != p0.qos.partition.strs[2]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p1.qos.partition.strs[0], p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p1.qos.partition.strs[1], p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p1.qos.partition.strs[2], p0.qos.partition.strs[2]);
 | 
			
		||||
 | 
			
		||||
  /* merge-in missing ones from an aliased copy: original must remain unchanged;
 | 
			
		||||
     existing ones should stay without touching "aliased" only new ones are
 | 
			
		||||
     added as unaliased ones */
 | 
			
		||||
  nn_plist_t p2, p2memcpy;
 | 
			
		||||
  nn_plist_init_empty (&p2);
 | 
			
		||||
  p2.present = PP_ENTITY_NAME;
 | 
			
		||||
  p2.aliased = PP_ENTITY_NAME;
 | 
			
		||||
  p2.entity_name = "omen";
 | 
			
		||||
  memcpy (&p2memcpy, &p2, sizeof (p2));
 | 
			
		||||
  nn_plist_mergein_missing (&p2, &p0, p0.present, p0.qos.present);
 | 
			
		||||
  CU_ASSERT (memcmp (&p0, &p0memcpy, sizeof (p0)) == 0);
 | 
			
		||||
  CU_ASSERT (p2.present == p0.present);
 | 
			
		||||
  CU_ASSERT (p2.aliased == p2memcpy.aliased);
 | 
			
		||||
  CU_ASSERT (p2.qos.present == p0.qos.present);
 | 
			
		||||
  CU_ASSERT (p2.qos.aliased == p2memcpy.qos.aliased);
 | 
			
		||||
  CU_ASSERT (p2.process_id == p0.process_id);
 | 
			
		||||
  CU_ASSERT (p2.entity_name == p2memcpy.entity_name);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p2.entity_name, "omen");
 | 
			
		||||
  CU_ASSERT (p2.qos.partition.n == p0.qos.partition.n);
 | 
			
		||||
  CU_ASSERT (p2.qos.partition.strs != p0.qos.partition.strs);
 | 
			
		||||
  CU_ASSERT (p2.qos.partition.strs[0] != p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT (p2.qos.partition.strs[1] != p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT (p2.qos.partition.strs[2] != p0.qos.partition.strs[2]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p2.qos.partition.strs[0], p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p2.qos.partition.strs[1], p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p2.qos.partition.strs[2], p0.qos.partition.strs[2]);
 | 
			
		||||
 | 
			
		||||
  /* unalias of p0, partition.strs mustn't change, because it, unlike its elements, wasn't aliased */
 | 
			
		||||
  nn_plist_unalias (&p0);
 | 
			
		||||
  CU_ASSERT (p0.present == p0memcpy.present);
 | 
			
		||||
  CU_ASSERT (p0.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p0.qos.present == p0memcpy.qos.present);
 | 
			
		||||
  CU_ASSERT (p0.qos.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p0.process_id == p0memcpy.process_id);
 | 
			
		||||
  CU_ASSERT (p0.entity_name != p0memcpy.entity_name);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.entity_name, p0memcpy.entity_name);
 | 
			
		||||
  CU_ASSERT (p0.qos.partition.n == p0memcpy.qos.partition.n);
 | 
			
		||||
  CU_ASSERT (p0.qos.partition.strs == p0memcpy.qos.partition.strs);
 | 
			
		||||
  CU_ASSERT (p0.qos.partition.strs[0] != p0strs[0]);
 | 
			
		||||
  CU_ASSERT (p0.qos.partition.strs[1] != p0strs[1]);
 | 
			
		||||
  CU_ASSERT (p0.qos.partition.strs[2] != p0strs[2]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[0], p0strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[1], p0strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p0.qos.partition.strs[2], p0strs[2]);
 | 
			
		||||
  memcpy (&p0memcpy, &p0, sizeof (p0));
 | 
			
		||||
 | 
			
		||||
  /* copy an aliased one; the original must be unchanged, the copy unaliased */
 | 
			
		||||
  nn_plist_t p3;
 | 
			
		||||
  nn_plist_init_empty (&p3);
 | 
			
		||||
  nn_plist_copy (&p3, &p0);
 | 
			
		||||
  CU_ASSERT (memcmp (&p0, &p0memcpy, sizeof (p0)) == 0);
 | 
			
		||||
  CU_ASSERT (p3.present == p0.present);
 | 
			
		||||
  CU_ASSERT (p3.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p3.qos.present == p0.qos.present);
 | 
			
		||||
  CU_ASSERT (p3.qos.aliased == 0);
 | 
			
		||||
  CU_ASSERT (p3.process_id == p0.process_id);
 | 
			
		||||
  CU_ASSERT (p3.entity_name != p0.entity_name);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p3.entity_name, p0.entity_name);
 | 
			
		||||
  CU_ASSERT (p3.qos.partition.n == p0.qos.partition.n);
 | 
			
		||||
  CU_ASSERT (p3.qos.partition.strs != p0.qos.partition.strs);
 | 
			
		||||
  CU_ASSERT (p3.qos.partition.strs[0] != p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT (p3.qos.partition.strs[1] != p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT (p3.qos.partition.strs[2] != p0.qos.partition.strs[2]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p3.qos.partition.strs[0], p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p3.qos.partition.strs[1], p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p3.qos.partition.strs[2], p0.qos.partition.strs[2]);
 | 
			
		||||
 | 
			
		||||
  /* merge-in missing ones from an aliased copy: original must remain unchanged;
 | 
			
		||||
     existing ones should stay without touching "aliased" only new ones are
 | 
			
		||||
     added as unaliased ones */
 | 
			
		||||
  nn_plist_t p4, p4memcpy;
 | 
			
		||||
  nn_plist_init_empty (&p4);
 | 
			
		||||
  p4.present = PP_ENTITY_NAME;
 | 
			
		||||
  p4.aliased = PP_ENTITY_NAME;
 | 
			
		||||
  p4.entity_name = "omen";
 | 
			
		||||
  memcpy (&p4memcpy, &p4, sizeof (p4));
 | 
			
		||||
  nn_plist_mergein_missing (&p4, &p0, p0.present, p0.qos.present);
 | 
			
		||||
  CU_ASSERT (memcmp (&p0, &p0memcpy, sizeof (p0)) == 0);
 | 
			
		||||
  CU_ASSERT (p4.present == p0.present);
 | 
			
		||||
  CU_ASSERT (p4.aliased == p4memcpy.aliased);
 | 
			
		||||
  CU_ASSERT (p4.qos.present == p0.qos.present);
 | 
			
		||||
  CU_ASSERT (p4.qos.aliased == p4memcpy.qos.aliased);
 | 
			
		||||
  CU_ASSERT (p4.process_id == p0.process_id);
 | 
			
		||||
  CU_ASSERT (p4.entity_name == p4memcpy.entity_name);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p4.entity_name, "omen");
 | 
			
		||||
  CU_ASSERT (p4.qos.partition.n == p0.qos.partition.n);
 | 
			
		||||
  CU_ASSERT (p4.qos.partition.strs != p0.qos.partition.strs);
 | 
			
		||||
  CU_ASSERT (p4.qos.partition.strs[0] != p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT (p4.qos.partition.strs[1] != p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT (p4.qos.partition.strs[2] != p0.qos.partition.strs[2]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p4.qos.partition.strs[0], p0.qos.partition.strs[0]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p4.qos.partition.strs[1], p0.qos.partition.strs[1]);
 | 
			
		||||
  CU_ASSERT_STRING_EQUAL (p4.qos.partition.strs[2], p0.qos.partition.strs[2]);
 | 
			
		||||
 | 
			
		||||
  nn_plist_fini (&p0);
 | 
			
		||||
  nn_plist_fini (&p1);
 | 
			
		||||
  nn_plist_fini (&p2);
 | 
			
		||||
  nn_plist_fini (&p3);
 | 
			
		||||
  nn_plist_fini (&p4);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										314
									
								
								src/core/ddsi/tests/plist_generic.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								src/core/ddsi/tests/plist_generic.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,314 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright(c) 2019 ADLINK Technology Limited and others
 | 
			
		||||
 *
 | 
			
		||||
 * This program and the accompanying materials are made available under the
 | 
			
		||||
 * terms of the Eclipse Public License v. 2.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
 | 
			
		||||
 * v. 1.0 which is available at
 | 
			
		||||
 * http://www.eclipse.org/org/documents/edl-v10.php.
 | 
			
		||||
 *
 | 
			
		||||
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "CUnit/Theory.h"
 | 
			
		||||
#include "dds/ddsrt/heap.h"
 | 
			
		||||
#include "dds/ddsrt/endian.h"
 | 
			
		||||
#include "dds/ddsi/q_xqos.h"
 | 
			
		||||
#include "dds/ddsi/ddsi_plist_generic.h"
 | 
			
		||||
 | 
			
		||||
struct desc {
 | 
			
		||||
  const enum pserop desc[20];
 | 
			
		||||
  const void *data;
 | 
			
		||||
  size_t exp_sersize;
 | 
			
		||||
  const unsigned char *exp_ser;
 | 
			
		||||
 | 
			
		||||
  /* XbPROP means expectation after deser may be different from input, if exp_data
 | 
			
		||||
     is NULL, use "data", else use "exp_data" */
 | 
			
		||||
  const void *exp_data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct desc_invalid {
 | 
			
		||||
  const enum pserop desc[20];
 | 
			
		||||
  size_t sersize;
 | 
			
		||||
  const unsigned char *ser;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if DDSRT_ENDIAN == DDSRT_BIG_ENDIAN
 | 
			
		||||
#define SER32(v) \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) >> 24), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 16) & 0xff), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 8) & 0xff), \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) & 0xff)
 | 
			
		||||
#define SER32BE(v) SER32(v)
 | 
			
		||||
#else
 | 
			
		||||
#define SER32(v) \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) & 0xff), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 8) & 0xff), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 16) & 0xff), \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) >> 24)
 | 
			
		||||
#define SER32BE(v) \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) >> 24), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 16) & 0xff), \
 | 
			
		||||
  (unsigned char)(((uint32_t)(v) >> 8) & 0xff), \
 | 
			
		||||
  (unsigned char)((uint32_t)(v) & 0xff)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef unsigned char raw[];
 | 
			
		||||
typedef uint32_t raw32[];
 | 
			
		||||
typedef ddsi_octetseq_t oseq;
 | 
			
		||||
 | 
			
		||||
struct desc descs[] = {
 | 
			
		||||
  { {XSTOP}, (raw){0}, 0, (raw){0} },
 | 
			
		||||
  { {XO,XSTOP}, &(oseq){0, NULL },       4, (raw){SER32(0)} },
 | 
			
		||||
  { {XO,XSTOP}, &(oseq){1, (raw){3} },   5, (raw){SER32(1), 3} },
 | 
			
		||||
  { {XS,XSTOP}, &(char *[]){""},         5, (raw){SER32(1), 0} },
 | 
			
		||||
  { {XS,XSTOP}, &(char *[]){"meow"},     9, (raw){SER32(5), 'm','e','o','w',0} },
 | 
			
		||||
  { {XE1,XSTOP}, (raw32){1},             4, (raw){SER32(1)} },
 | 
			
		||||
  { {XE2,XSTOP}, (raw32){2},             4, (raw){SER32(2)} },
 | 
			
		||||
  { {XE3,XSTOP}, (raw32){3},             4, (raw){SER32(3)} },
 | 
			
		||||
  { {Xi,XSTOP},   (raw32){1},            4, (raw){SER32(1)} },
 | 
			
		||||
  { {Xix2,XSTOP}, (raw32){2,3},          8, (raw){SER32(2), SER32(3)} },
 | 
			
		||||
  { {Xix3,XSTOP}, (raw32){4,5,6},       12, (raw){SER32(4), SER32(5), SER32(6)} },
 | 
			
		||||
  { {Xix4,XSTOP}, (raw32){7,8,9,10},    16, (raw){SER32(7), SER32(8), SER32(9), SER32(10)} },
 | 
			
		||||
  { {Xu,XSTOP},   (raw32){1},            4, (raw){SER32(1)} },
 | 
			
		||||
  { {Xux2,XSTOP}, (raw32){2,3},          8, (raw){SER32(2), SER32(3)} },
 | 
			
		||||
  { {Xux3,XSTOP}, (raw32){4,5,6},       12, (raw){SER32(4), SER32(5), SER32(6)} },
 | 
			
		||||
  { {Xux4,XSTOP}, (raw32){7,8,9,10},    16, (raw){SER32(7), SER32(8), SER32(9), SER32(10)} },
 | 
			
		||||
  { {Xux5,XSTOP}, (raw32){7,8,9,10,11}, 20, (raw){SER32(7), SER32(8), SER32(9), SER32(10), SER32(11)} },
 | 
			
		||||
  { {XD,XSTOP},   (uint64_t[]){314159265358979324},
 | 
			
		||||
    /* note: fractional part depends on rounding rule used for converting nanoseconds to NTP time
 | 
			
		||||
       Cyclone currently rounds up, so we have to do that too */
 | 
			
		||||
    8, (raw){SER32(314159265), SER32(1541804457)} },
 | 
			
		||||
  { {XD,XSTOP},   (uint64_t[]){DDS_NEVER},
 | 
			
		||||
    8, (raw){SER32(INT32_MAX), SER32(UINT32_MAX)} },
 | 
			
		||||
  { {XDx2,XSTOP}, (uint64_t[]){314159265358979324, 271828182845904524},
 | 
			
		||||
    16, (raw){SER32(314159265), SER32(1541804457), SER32(271828182), SER32(3633132267)} },
 | 
			
		||||
  { {Xo,XSTOP},   (raw){31},             1, (raw){31} },
 | 
			
		||||
  { {Xox2,XSTOP}, (raw){31,13},          2, (raw){31,13} },
 | 
			
		||||
  { {Xb,XSTOP},   (raw){1},              1, (raw){1} },
 | 
			
		||||
  { {Xbx2,XSTOP}, (raw){1,0},            2, (raw){1,0} },
 | 
			
		||||
  { {XG,XSTOP},   (raw32){3,4,5,0x1c1}, 16, (raw){SER32BE(3), SER32BE(4), SER32BE(5), SER32BE(0x1c1) } },
 | 
			
		||||
  { {XK,XSTOP},   (raw){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},
 | 
			
		||||
    16, (raw){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16} },
 | 
			
		||||
  { {XQ,Xo,XSTOP,XSTOP}, &(oseq){3, (raw){1,2,3}},
 | 
			
		||||
    7, (raw){SER32(3), 1,2,3} },
 | 
			
		||||
  { {XQ,XS,XSTOP,XSTOP}, &(ddsi_stringseq_t){2, (char*[]){"tree","flower"}},
 | 
			
		||||
    27, (raw){SER32(2), SER32(5),'t','r','e','e',0, 0,0,0, SER32(7), 'f','l','o','w','e','r',0} },
 | 
			
		||||
  { {Xb,XQ,XbPROP,XS,Xo,XSTOP,XSTOP},
 | 
			
		||||
    &(struct{char b; oseq seq;}){1, {5, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
      {0,"apple",1}, {1,"orange",2}, {0,"cherry",3}, {1,"fig",4}, {1,"prune",5}}}},
 | 
			
		||||
    43, (raw){1, 0,0,0, SER32(3),
 | 
			
		||||
      SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
      SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
      SER32(6), 'p','r','u','n','e',0, 5
 | 
			
		||||
    },
 | 
			
		||||
    &(struct{char b; oseq seq;}){1, {3, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
      {1,"orange",2}, {1,"fig",4}, {1,"prune",5}}}},
 | 
			
		||||
  },
 | 
			
		||||
  { {Xb,XQ,XbPROP,XS,Xo,XSTOP, Xopt,XQ,XbPROP,XS,Xo,XSTOP, XSTOP},
 | 
			
		||||
    &(struct{char b; oseq seq, seq2;}){1,
 | 
			
		||||
      {5, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
        {0,"apple",1}, {1,"orange",2}, {0,"cherry",3}, {1,"fig",4}, {1,"prune",5}}},
 | 
			
		||||
      {2, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
        {1,"oak",8}, {0,"beech",9}}}
 | 
			
		||||
    },
 | 
			
		||||
    57, (raw){1, 0,0,0,
 | 
			
		||||
      SER32(3),
 | 
			
		||||
      SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
      SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
      SER32(6), 'p','r','u','n','e',0, 5,
 | 
			
		||||
      0,
 | 
			
		||||
      SER32(1),
 | 
			
		||||
      SER32(4), 'o','a','k',0, 8
 | 
			
		||||
    },
 | 
			
		||||
    &(struct{char b; oseq seq, seq2;}){1,
 | 
			
		||||
      {3, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
        {1,"orange",2}, {1,"fig",4}, {1,"prune",5}}},
 | 
			
		||||
      {1,  (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
        {1,"oak",8}}}
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CU_Test (ddsi_plist_generic, ser_and_deser)
 | 
			
		||||
{
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t u;
 | 
			
		||||
    void *p;
 | 
			
		||||
    char buf[256];
 | 
			
		||||
  } mem;
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < sizeof (descs) / sizeof (descs[0]); i++)
 | 
			
		||||
  {
 | 
			
		||||
    size_t memsize;
 | 
			
		||||
    void *ser;
 | 
			
		||||
    size_t sersize;
 | 
			
		||||
    dds_return_t ret;
 | 
			
		||||
    ret = plist_ser_generic (&ser, &sersize, descs[i].data, descs[i].desc);
 | 
			
		||||
    if (ret != DDS_RETCODE_OK)
 | 
			
		||||
      CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
 | 
			
		||||
    if (sersize != descs[i].exp_sersize)
 | 
			
		||||
      CU_ASSERT (sersize == descs[i].exp_sersize);
 | 
			
		||||
    /* if sizes don't match, still check prefix */
 | 
			
		||||
    size_t cmpsize = (sersize < descs[i].exp_sersize) ? sersize : descs[i].exp_sersize;
 | 
			
		||||
    if (memcmp (ser, descs[i].exp_ser, cmpsize) != 0)
 | 
			
		||||
    {
 | 
			
		||||
      printf ("memcmp i = %zu\n", i);
 | 
			
		||||
      for (size_t k = 0; k < cmpsize; k++)
 | 
			
		||||
        printf ("  %3zu  %02x  %02x\n", k, ((unsigned char *)ser)[k], descs[i].exp_ser[k]);
 | 
			
		||||
      CU_ASSERT (!(bool)"memcmp");
 | 
			
		||||
    }
 | 
			
		||||
    /* check */
 | 
			
		||||
    memsize = plist_memsize_generic (descs[i].desc);
 | 
			
		||||
    if (memsize > sizeof (mem))
 | 
			
		||||
      CU_ASSERT_FATAL (memsize <= sizeof (mem));
 | 
			
		||||
    /* memset to zero for used part so padding is identical to compiler inserted padding,
 | 
			
		||||
       but to something unlikely for the remainder */
 | 
			
		||||
    memset (mem.buf, 0, memsize);
 | 
			
		||||
    memset (mem.buf + memsize, 0xee, sizeof (mem) - memsize);
 | 
			
		||||
    ret = plist_deser_generic (&mem, ser, sersize, false, descs[i].desc);
 | 
			
		||||
    if (ret != DDS_RETCODE_OK)
 | 
			
		||||
      CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
 | 
			
		||||
    /* the compare function should be happy with it */
 | 
			
		||||
    if (!plist_equal_generic (descs[i].exp_data ? descs[i].exp_data : descs[i].data, &mem, descs[i].desc))
 | 
			
		||||
      CU_ASSERT (!(bool)"plist_equal_generic");
 | 
			
		||||
    /* content should be identical except when an XO, XS or XQ is present (because the first two
 | 
			
		||||
       alias the serialised form and XQ to freshly allocated memory), so we do a limited check */
 | 
			
		||||
    bool can_memcmp = true;
 | 
			
		||||
    for (const enum pserop *op = descs[i].desc; *op != XSTOP && can_memcmp; op++)
 | 
			
		||||
      if (*op == XS || *op == XO || *op == XQ)
 | 
			
		||||
        can_memcmp = false;
 | 
			
		||||
    if (can_memcmp && memcmp (descs[i].exp_data ? descs[i].exp_data : descs[i].data, &mem, memsize) != 0)
 | 
			
		||||
      CU_ASSERT (!(bool)"memcmp");
 | 
			
		||||
    /* rely on mem checkers to find memory leaks, incorrect free, etc. */
 | 
			
		||||
    plist_fini_generic (&mem, descs[i].desc, true);
 | 
			
		||||
    ddsrt_free (ser);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CU_Test (ddsi_plist_generic, unalias)
 | 
			
		||||
{
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t u;
 | 
			
		||||
    void *p;
 | 
			
		||||
    char buf[256];
 | 
			
		||||
  } mem;
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < sizeof (descs) / sizeof (descs[0]); i++)
 | 
			
		||||
  {
 | 
			
		||||
    void *ser;
 | 
			
		||||
    size_t sersize;
 | 
			
		||||
    dds_return_t ret;
 | 
			
		||||
    (void) plist_ser_generic (&ser, &sersize, descs[i].data, descs[i].desc);
 | 
			
		||||
    (void) plist_deser_generic (&mem, ser, sersize, false, descs[i].desc);
 | 
			
		||||
    /* after unaliasing, the data should be valid even when the serialised form has been overwritten or freed */
 | 
			
		||||
    ret = plist_unalias_generic (&mem, descs[i].desc);
 | 
			
		||||
    CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
 | 
			
		||||
    memset (ser, 0xee, sersize);
 | 
			
		||||
    ddsrt_free (ser);
 | 
			
		||||
    if (!plist_equal_generic (descs[i].exp_data ? descs[i].exp_data : descs[i].data, &mem, descs[i].desc))
 | 
			
		||||
      CU_ASSERT (!(bool)"plist_equal_generic");
 | 
			
		||||
    plist_fini_generic (&mem, descs[i].desc, false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct desc_invalid descs_invalid[] = {
 | 
			
		||||
  { {Xb,XSTOP},   1, (raw){2} }, // 2 is not a valid boolean
 | 
			
		||||
  { {XS,XSTOP},   8, (raw){SER32(5), 'm','e','o','w',0} }, // short input
 | 
			
		||||
  { {XS,XSTOP},   8, (raw){SER32(4), 'm','e','o','w',0} }, // not terminated
 | 
			
		||||
  { {XG,XSTOP},  15, (raw){SER32BE(3), SER32BE(4), SER32BE(5), SER32BE(0x100) } }, // short input
 | 
			
		||||
  { {XK,XSTOP},  15, (raw){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} }, // short input
 | 
			
		||||
  { {XQ,Xo,XSTOP,XSTOP}, 7, (raw){SER32(4), 1,2,3} }, // short input
 | 
			
		||||
  { {XQ,XS,XSTOP,XSTOP}, // padding missing, short input
 | 
			
		||||
    24, (raw){SER32(2), SER32(5),'t','r','e','e',0, SER32(7), 'f','l','o','w','e','r',0} },
 | 
			
		||||
  { {Xb,XQ,XbPROP,XS,Xo,XSTOP,XSTOP},
 | 
			
		||||
    43, (raw){1, 0,0,0, SER32(3),
 | 
			
		||||
      SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
      SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
      SER32(7), 'p','r','u','n','e',0, 5 // string not terminated
 | 
			
		||||
    } },
 | 
			
		||||
  { {Xb, XQ,XbPROP,XS,Xo,XSTOP, XQ,XbPROP,XS,Xo,XSTOP, XSTOP},
 | 
			
		||||
    43, (raw){1, 0,0,0,
 | 
			
		||||
      /* first sequence is valid */
 | 
			
		||||
      SER32(3),
 | 
			
		||||
      SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
      SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
      SER32(6), 'p','r','u','n','e',0, 5,
 | 
			
		||||
      /* second sequence is invalid */
 | 
			
		||||
      0, /* pad */
 | 
			
		||||
      SER32(3),
 | 
			
		||||
      SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
      SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
      SER32(7), 'p','r','u','n','e',0, 5 // string not terminated
 | 
			
		||||
    } }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CU_Test (ddsi_plist_generic, invalid_input)
 | 
			
		||||
{
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t u;
 | 
			
		||||
    void *p;
 | 
			
		||||
    char buf[256];
 | 
			
		||||
  } mem;
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < sizeof (descs_invalid) / sizeof (descs_invalid[0]); i++)
 | 
			
		||||
  {
 | 
			
		||||
    dds_return_t ret;
 | 
			
		||||
    ret = plist_deser_generic (&mem, descs_invalid[i].ser, descs_invalid[i].sersize, false, descs_invalid[i].desc);
 | 
			
		||||
    if (ret == DDS_RETCODE_OK)
 | 
			
		||||
      CU_ASSERT_FATAL (ret != DDS_RETCODE_OK);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CU_Test (ddsi_plist_generic, optional)
 | 
			
		||||
{
 | 
			
		||||
  union {
 | 
			
		||||
    uint64_t u;
 | 
			
		||||
    void *p;
 | 
			
		||||
    char buf[256];
 | 
			
		||||
  } mem;
 | 
			
		||||
 | 
			
		||||
  enum pserop ser_desc[] = {Xb,XQ,XbPROP,XS,Xo,XSTOP,XSTOP};
 | 
			
		||||
  enum pserop deser_desc[] = {Xb,XQ,XbPROP,XS,Xo,XSTOP, Xopt,XQ,XbPROP,XS,Xo,XSTOP, XSTOP};
 | 
			
		||||
  const void *data = &(struct{char b; oseq seq;}){
 | 
			
		||||
    1, {5, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
      {0,"apple",1}, {1,"orange",2}, {0,"cherry",3}, {1,"fig",4}, {1,"prune",5}}}};
 | 
			
		||||
  size_t exp_sersize = 43;
 | 
			
		||||
  const unsigned char *exp_ser = (raw){
 | 
			
		||||
    1, 0,0,0, SER32(3),
 | 
			
		||||
    SER32(7), 'o','r','a','n','g','e',0, 2,
 | 
			
		||||
    SER32(4), 'f','i','g',0, 4, 0,0,0,
 | 
			
		||||
    SER32(6), 'p','r','u','n','e',0, 5
 | 
			
		||||
  };
 | 
			
		||||
  const void *exp_data = &(struct{char b; oseq seq; oseq seq2;}){
 | 
			
		||||
    1, {3, (unsigned char *)(struct{char b;char *s;uint8_t o;}[]){
 | 
			
		||||
      {1,"orange",2}, {1,"fig",4}, {1,"prune",5}}},
 | 
			
		||||
    {0, NULL}};
 | 
			
		||||
 | 
			
		||||
  size_t memsize;
 | 
			
		||||
  void *ser;
 | 
			
		||||
  size_t sersize;
 | 
			
		||||
  dds_return_t ret;
 | 
			
		||||
  ret = plist_ser_generic (&ser, &sersize, data, ser_desc);
 | 
			
		||||
  CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
 | 
			
		||||
  CU_ASSERT (sersize == exp_sersize);
 | 
			
		||||
  /* if sizes don't match, still check prefix */
 | 
			
		||||
  size_t cmpsize = (sersize < exp_sersize) ? sersize : exp_sersize;
 | 
			
		||||
  if (memcmp (ser, exp_ser, cmpsize) != 0)
 | 
			
		||||
  {
 | 
			
		||||
    printf ("ddsi_plist_generic_optional: memcmp\n");
 | 
			
		||||
    for (size_t k = 0; k < cmpsize; k++)
 | 
			
		||||
      printf ("  %3zu  %02x  %02x\n", k, ((unsigned char *)ser)[k], exp_ser[k]);
 | 
			
		||||
    CU_ASSERT (!(bool)"memcmp");
 | 
			
		||||
  }
 | 
			
		||||
  /* check */
 | 
			
		||||
  memsize = plist_memsize_generic (deser_desc);
 | 
			
		||||
  CU_ASSERT_FATAL (memsize <= sizeof (mem));
 | 
			
		||||
  memset (&mem, 0xee, sizeof (mem));
 | 
			
		||||
  ret = plist_deser_generic (&mem, ser, sersize, false, deser_desc);
 | 
			
		||||
  CU_ASSERT_FATAL (ret == DDS_RETCODE_OK);
 | 
			
		||||
  /* the compare function should be happy with it */
 | 
			
		||||
  CU_ASSERT (plist_equal_generic (exp_data, &mem, deser_desc));
 | 
			
		||||
  plist_fini_generic (&mem, deser_desc, true);
 | 
			
		||||
  ddsrt_free (ser);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue