Merge pull request #260 from eboasson/plist-update

parameter list update: properties, fixes, unit tests
This commit is contained in:
eboasson 2019-09-27 13:32:46 +02:00 committed by GitHub
commit 78fc9c2e85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 976 additions and 159 deletions

View file

@ -70,6 +70,7 @@ PREPEND(hdrs_private_ddsi "${CMAKE_CURRENT_LIST_DIR}/include/dds/ddsi"
ddsi_raweth.h ddsi_raweth.h
ddsi_ipaddr.h ddsi_ipaddr.h
ddsi_mcgroup.h ddsi_mcgroup.h
ddsi_plist_generic.h
ddsi_serdata.h ddsi_serdata.h
ddsi_sertopic.h ddsi_sertopic.h
ddsi_serdata_default.h ddsi_serdata_default.h
@ -129,3 +130,8 @@ install(
DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/dds" DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/include/dds"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT dev) 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()

View 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

View file

@ -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_topicdata_qospolicy_t;
typedef ddsi_octetseq_t dds_groupdata_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 { typedef struct dds_durability_qospolicy {
dds_durability_kind_t kind; dds_durability_kind_t kind;
} dds_durability_qospolicy_t; } 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_SUBSCRIPTION_KEYS ((uint64_t)1 << 25)
#define QP_PRISMTECH_ENTITY_FACTORY ((uint64_t)1 << 27) #define QP_PRISMTECH_ENTITY_FACTORY ((uint64_t)1 << 27)
#define QP_CYCLONE_IGNORELOCAL ((uint64_t)1 << 30) #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, /* Partition QoS is not RxO according to the specification (DDS 1.2,
section 7.1.3), but communication will not take place unless it 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_subscription_keys_qospolicy_t subscription_keys;
/*x xR*/dds_reader_lifespan_qospolicy_t reader_lifespan; /*x xR*/dds_reader_lifespan_qospolicy_t reader_lifespan;
/* x */dds_ignorelocal_qospolicy_t ignorelocal; /* x */dds_ignorelocal_qospolicy_t ignorelocal;
/*xxx */dds_property_qospolicy_t property;
}; };
struct nn_xmsg; struct nn_xmsg;

View file

@ -453,6 +453,7 @@ static struct ddsi_serdata *serdata_default_from_sample_plist (const struct ddsi
#ifndef NDEBUG #ifndef NDEBUG
size_t keysize; size_t keysize;
#endif #endif
assert(rawkey);
switch (sample->keyparam) switch (sample->keyparam)
{ {
case PID_PARTICIPANT_GUID: case PID_PARTICIPANT_GUID:

View file

@ -38,6 +38,8 @@
#include "dds/ddsrt/avl.h" #include "dds/ddsrt/avl.h"
#include "dds/ddsi/q_misc.h" /* for vendor_is_... */ #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 /* I am tempted to change LENGTH_UNLIMITED to 0 in the API (with -1
supported for backwards compatibility) ... on the wire however supported for backwards compatibility) ... on the wire however
it must be -1 */ it must be -1 */
@ -88,25 +90,6 @@ struct flagset {
uint64_t wanted; 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 { struct piddesc {
nn_parameterid_t pid; /* parameter id or PID_PAD if strictly local */ nn_parameterid_t pid; /* parameter id or PID_PAD if strictly local */
uint16_t flags; /* see PDF_xxx flags */ 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 plist_offset; /* offset from start of nn_plist_t */
size_t size; /* in-memory size for copying */ size_t size; /* in-memory size for copying */
union { 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 parameters, compiler will warn if one ever tries to use more than
will fit; on non-GCC/Clang and 32-bits machines */ will fit */
const enum pserop desc[4]; const enum pserop desc[12];
struct { 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 (*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); 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); 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 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_history_qospolicy (const dds_history_qospolicy_t *q);
static dds_return_t validate_resource_limits_qospolicy (const dds_resource_limits_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; 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 { \ #define COMPLEX(basecase_, type_, cleanup_unaliased_, cleanup_always_) do { \
type_ *x = deser_generic_dst (dst, dstoff, alignof (type_)); \ 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 XSTOP: return;
case XO: COMPLEX (XO, ddsi_octetseq_t, ddsrt_free (x->value), (void) 0); break; 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 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 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 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 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 XD: case XDx2: SIMPLE (XD, dds_duration_t); break;
case Xo: case Xox2: SIMPLE (Xo, unsigned char); break; case Xo: case Xox2: SIMPLE (Xo, unsigned char); break;
case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break; case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break;
case XbCOND: SIMPLE (XbCOND, unsigned char); break; case XbCOND: SIMPLE (XbCOND, unsigned char); break;
case XG: SIMPLE (XG, ddsi_guid_t); break; case XG: SIMPLE (XG, ddsi_guid_t); break;
case XK: SIMPLE (XK, nn_keyhash_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++; desc++;
} }
@ -362,6 +399,26 @@ static void fini_generic_partial (void * __restrict dst, size_t * __restrict dst
#undef COMPLEX #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) 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; 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) switch (*desc)
{ {
case XSTOP: case XSTOP:
*flagset->present |= flag; goto success;
return 0;
case XO: { /* octet sequence */ case XO: { /* octet sequence */
ddsi_octetseq_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_octetseq_t)); 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) 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); *dstoff += sizeof (*x);
break; 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 */ case XE1: case XE2: case XE3: { /* enum with max allowed value */
unsigned * const x = deser_generic_dst (dst, dstoff, alignof (int)); unsigned * const x = deser_generic_dst (dst, dstoff, alignof (int));
const uint32_t maxval = 1 + (uint32_t) (*desc - XE1); 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); *dstoff += cnt * sizeof (*x);
break; 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) */ 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)); dds_duration_t * const x = deser_generic_dst (dst, dstoff, alignof (dds_duration_t));
const uint32_t cnt = 1 + (uint32_t) (*desc - XD); 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); *dstoff += cnt * sizeof (*x);
break; 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 */ case XG: { /* GUID */
ddsi_guid_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_guid_t)); ddsi_guid_t * const x = deser_generic_dst (dst, dstoff, alignof (ddsi_guid_t));
if (dd->bufsz - *srcoff < sizeof (*x)) 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); *dstoff += sizeof (*x);
break; 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++; desc++;
} }
success:
*flagset->present |= flag;
return 0;
fail: 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->present &= ~flag;
*flagset->aliased &= ~flag; *flagset->aliased &= ~flag;
return DDS_RETCODE_BAD_PARAMETER; 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 { \ #define COMPLEX(basecase_, type_, dstoff_update_) do { \
type_ const *x = deser_generic_src (src, &srcoff, alignof (type_)); \ type_ const *x = deser_generic_src (src, &srcoff, alignof (type_)); \
const uint32_t cnt = 1 + (uint32_t) (*desc - (basecase_)); \ const uint32_t cnt = 1 + (uint32_t) (*desc - (basecase_)); \
for (uint32_t xi = 0; xi < cnt; xi++, x++) { dstoff_update_; } \ for (uint32_t xi = 0; xi < cnt; xi++, x++) { dstoff_update_; } \
srcoff += cnt * sizeof (*x); \ srcoff += cnt * sizeof (*x); \
} while (0) } while (0)
#define SIMPLE1(basecase_, type_) COMPLEX (basecase_, type_, dstoff = 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)) #define SIMPLE4(basecase_, type_) COMPLEX (basecase_, type_, *dstoff = align4size (*dstoff) + sizeof (*x))
while (true) while (true)
{ {
switch (*desc) switch (*desc)
{ {
case XSTOP: return dstoff; case XSTOP: return;
case XO: COMPLEX (XO, ddsi_octetseq_t, dstoff = align4size (dstoff) + 4 + x->length); break; 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 XS: COMPLEX (XS, const char *, *dstoff = align4size (*dstoff) + 4 + strlen (*x) + 1); break;
case XZ: COMPLEX (XZ, ddsi_stringseq_t, { case XE1: case XE2: case XE3: COMPLEX (*desc, unsigned, *dstoff = align4size (*dstoff) + 4); break;
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 Xi: case Xix2: case Xix3: case Xix4: SIMPLE4 (Xi, int32_t); 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 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 XD: case XDx2: SIMPLE4 (XD, dds_duration_t); break;
case Xo: case Xox2: SIMPLE1 (Xo, unsigned char); break; case Xo: case Xox2: SIMPLE1 (Xo, unsigned char); break;
case Xb: case Xbx2: SIMPLE1 (Xb, unsigned char); break; case Xb: case Xbx2: SIMPLE1 (Xb, unsigned char); break;
case XbCOND: SIMPLE1 (XbCOND, unsigned char); break; case XbCOND: SIMPLE1 (XbCOND, unsigned char); break;
case XG: SIMPLE1 (XG, ddsi_guid_t); break; case XG: SIMPLE1 (XG, ddsi_guid_t); break;
case XK: SIMPLE1 (XK, nn_keyhash_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++; desc++;
} }
#undef SIMPLE #undef SIMPLE4
#undef SIMPLE1
#undef COMPLEX #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; 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) while (true)
{ {
switch (*desc) switch (*desc)
@ -573,134 +688,151 @@ static dds_return_t ser_generic (struct nn_xmsg *xmsg, nn_parameterid_t pid, con
return 0; return 0;
case XO: { /* octet sequence */ case XO: { /* octet sequence */
ddsi_octetseq_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_octetseq_t)); 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; *((uint32_t *) p) = x->length;
if (x->length) memcpy (p + 4, x->value, x->length); if (x->length) memcpy (p + 4, x->value, x->length);
dstoff += 4 + x->length; *dstoff += 4 + x->length;
srcoff += sizeof (*x); srcoff += sizeof (*x);
break; break;
} }
case XS: { /* string */ case XS: { /* string */
char const * const * const x = deser_generic_src (src, &srcoff, alignof (char *)); char const * const * const x = deser_generic_src (src, &srcoff, alignof (char *));
const uint32_t size = (uint32_t) (strlen (*x) + 1); 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; *((uint32_t *) p) = size;
memcpy (p + 4, *x, size); memcpy (p + 4, *x, size);
dstoff += 4 + 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;
}
srcoff += sizeof (*x); srcoff += sizeof (*x);
break; break;
} }
case XE1: case XE2: case XE3: { /* enum */ case XE1: case XE2: case XE3: { /* enum */
unsigned const * const x = deser_generic_src (src, &srcoff, alignof (unsigned)); 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; *p = (uint32_t) *x;
dstoff += 4; *dstoff += 4;
srcoff += sizeof (*x); srcoff += sizeof (*x);
break; break;
} }
case Xi: case Xix2: case Xix3: case Xix4: { /* int32_t(s) */ case Xi: case Xix2: case Xix3: case Xix4: { /* int32_t(s) */
int32_t const * const x = deser_generic_src (src, &srcoff, alignof (int32_t)); int32_t const * const x = deser_generic_src (src, &srcoff, alignof (int32_t));
const uint32_t cnt = 1 + (uint32_t) (*desc - Xi); 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++) for (uint32_t i = 0; i < cnt; i++)
p[i] = x[i]; p[i] = x[i];
dstoff += cnt * sizeof (*x); *dstoff += cnt * sizeof (*x);
srcoff += cnt * sizeof (*x); srcoff += cnt * sizeof (*x);
break; break;
} }
case Xu: case Xux2: case Xux3: case Xux4: case Xux5: { /* uint32_t(s) */ 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)); uint32_t const * const x = deser_generic_src (src, &srcoff, alignof (uint32_t));
const uint32_t cnt = 1 + (uint32_t) (*desc - Xu); 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++) for (uint32_t i = 0; i < cnt; i++)
p[i] = x[i]; p[i] = x[i];
dstoff += cnt * sizeof (*x); *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);
srcoff += cnt * sizeof (*x); srcoff += cnt * sizeof (*x);
break; break;
} }
case XD: case XDx2: { /* duration(s): int64_t <=> int32_t.uint32_t (seconds.fraction) */ 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)); dds_duration_t const * const x = deser_generic_src (src, &srcoff, alignof (dds_duration_t));
const uint32_t cnt = 1 + (uint32_t) (*desc - XD); 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++) for (uint32_t i = 0; i < cnt; i++)
{ {
ddsi_duration_t tmp = nn_to_ddsi_duration (x[i]); ddsi_duration_t tmp = nn_to_ddsi_duration (x[i]);
p[2 * i + 0] = (uint32_t) tmp.seconds; p[2 * i + 0] = (uint32_t) tmp.seconds;
p[2 * i + 1] = tmp.fraction; p[2 * i + 1] = tmp.fraction;
} }
dstoff += 2 * cnt * sizeof (uint32_t); *dstoff += 2 * cnt * sizeof (uint32_t);
srcoff += cnt * sizeof (*x); srcoff += cnt * sizeof (*x);
break; break;
} }
case Xo: case Xox2: { /* octet(s) */ case Xo: case Xox2: { /* octet(s) */
unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char)); unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char));
const uint32_t cnt = 1 + (uint32_t) (*desc - Xo); const uint32_t cnt = 1 + (uint32_t) (*desc - Xo);
char * const p = data + dstoff; char * const p = data + *dstoff;
memcpy (p, x, cnt); memcpy (p, x, cnt);
dstoff += cnt; *dstoff += cnt;
srcoff += cnt * sizeof (*x); srcoff += cnt * sizeof (*x);
break; break;
} }
case Xb: case Xbx2: case XbCOND: { /* boolean(s) */ case Xb: case Xbx2: case XbCOND: { /* boolean(s) */
unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char)); unsigned char const * const x = deser_generic_src (src, &srcoff, alignof (unsigned char));
const uint32_t cnt = (*desc == Xbx2) ? 2 : 1; /* <<<< beware! */ const uint32_t cnt = (*desc == Xbx2) ? 2 : 1; /* <<<< beware! */
char * const p = data + dstoff; char * const p = data + *dstoff;
memcpy (p, x, cnt); memcpy (p, x, cnt);
dstoff += cnt; *dstoff += cnt;
srcoff += cnt * sizeof (*x); srcoff += cnt * sizeof (*x);
break; 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 */ case XG: { /* GUID */
ddsi_guid_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_guid_t)); ddsi_guid_t const * const x = deser_generic_src (src, &srcoff, alignof (ddsi_guid_t));
const ddsi_guid_t xn = nn_hton_guid (*x); const ddsi_guid_t xn = nn_hton_guid (*x);
char * const p = data + dstoff; char * const p = data + *dstoff;
memcpy (p, &xn, sizeof (xn)); memcpy (p, &xn, sizeof (xn));
dstoff += sizeof (xn); *dstoff += sizeof (xn);
srcoff += sizeof (*x); srcoff += sizeof (*x);
break; break;
} }
case XK: { /* keyhash */ case XK: { /* keyhash */
nn_keyhash_t const * const x = deser_generic_src (src, &srcoff, alignof (nn_keyhash_t)); 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)); memcpy (p, x, sizeof (*x));
dstoff += sizeof (*x); *dstoff += sizeof (*x);
srcoff += sizeof (*x); srcoff += sizeof (*x);
break; 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++; 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 { \ #define COMPLEX(basecase_, type_, ...) do { \
type_ *x = deser_generic_dst (dst, dstoff, alignof (type_)); \ 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; return 0;
case XO: COMPLEX (XO, ddsi_octetseq_t, if (x->value) { x->value = ddsrt_memdup (x->value, x->length); }); break; 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 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 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 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 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 XD: case XDx2: SIMPLE (XD, dds_duration_t); break;
case Xo: case Xox2: SIMPLE (Xo, unsigned char); break; case Xo: case Xox2: SIMPLE (Xo, unsigned char); break;
case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break; case Xb: case Xbx2: SIMPLE (Xb, unsigned char); break;
case XbCOND: SIMPLE (XbCOND, 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 XG: SIMPLE (XG, ddsi_guid_t); break;
case XK: SIMPLE (XK, nn_keyhash_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++; desc++;
} }
@ -739,13 +886,19 @@ static dds_return_t unalias_generic (void * __restrict dst, size_t * __restrict
#undef COMPLEX #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) static bool unalias_generic_required (const enum pserop * __restrict desc)
{ {
while (*desc != XSTOP) while (*desc != XSTOP)
{ {
switch (*desc++) switch (*desc++)
{ {
case XO: case XS: case XZ: case XO: case XS: case XQ:
return true; return true;
default: default:
break; 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) 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; 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) static dds_return_t valid_generic (const void *src, size_t srcoff, const enum pserop * __restrict desc)
{ {
#define COMPLEX(basecase_, type_, cond_stmts_) do { \ #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 XSTOP: return 0;
case XO: SIMPLE (XO, ddsi_octetseq_t, (x->length == 0) == (x->value == NULL)); break; case XO: SIMPLE (XO, ddsi_octetseq_t, (x->length == 0) == (x->value == NULL)); break;
case XS: SIMPLE (XS, const char *, *x != 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 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 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 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 XD: case XDx2: SIMPLE (XD, dds_duration_t, *x >= 0); break;
case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break; case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break;
case Xb: case Xbx2: SIMPLE (Xb, unsigned char, *x == 0 || *x == 1); 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 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 XG: TRIVIAL (XG, ddsi_guid_t); break;
case XK: TRIVIAL (XK, nn_keyhash_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++; desc++;
} }
@ -833,19 +998,9 @@ static bool equal_generic (const void *srcx, const void *srcy, size_t srcoff, co
case XS: case XS:
SIMPLE (XS, const char *, strcmp (*x, *y) == 0); SIMPLE (XS, const char *, strcmp (*x, *y) == 0);
break; 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 XE1: case XE2: case XE3: TRIVIAL (*desc, unsigned); break;
case Xi: case Xix2: case Xix3: case Xix4: TRIVIAL (Xi, int32_t); 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 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 XD: case XDx2: TRIVIAL (XD, dds_duration_t); break;
case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break; case Xo: case Xox2: TRIVIAL (Xo, unsigned char); break;
case Xb: case Xbx2: TRIVIAL (Xb, 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; return true;
}); });
break; break;
case XG: SIMPLE (XG, ddsi_guid_t, memcmp (x, y, sizeof (*x))); break; case XbPROP: TRIVIAL (XbPROP, unsigned char); break;
case XK: SIMPLE (XK, nn_keyhash_t, memcmp (x, y, sizeof (*x))); 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++; desc++;
} }
@ -867,6 +1035,11 @@ static bool equal_generic (const void *srcx, const void *srcy, size_t srcoff, co
#undef COMPLEX #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 membersize(type, member) sizeof (((type *) 0)->member)
#define ENTRY(PFX_, NAME_, member_, flag_, validate_, ...) \ #define ENTRY(PFX_, NAME_, member_, flag_, validate_, ...) \
{ PID_##NAME_, flag_, PFX_##_##NAME_, #NAME_, offsetof (struct nn_plist, member_), \ { 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 #endif
/* Standardized parameters -- QoS _MUST_ come first (nn_plist_init_tables verifies this) because /* 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 */ it allows early-out when processing a dds_qos_t instead of an nn_plist_t */
static const struct piddesc piddesc_omg[] = { static const struct piddesc piddesc_omg[] = {
@ -966,6 +1140,9 @@ static const struct piddesc piddesc_omg[] = {
QP (DEADLINE, deadline, XD), QP (DEADLINE, deadline, XD),
QP (LATENCY_BUDGET, latency_budget, XD), QP (LATENCY_BUDGET, latency_budget, XD),
QP (LIVELINESS, liveliness, XE2, 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 */ /* 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", { PID_RELIABILITY, PDF_QOS | PDF_FUNCTION, QP_RELIABILITY, "RELIABILITY",
offsetof (struct nn_plist, qos.reliability), membersize (struct nn_plist, qos.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, ownership, XE1),
QP (OWNERSHIP_STRENGTH, ownership_strength, Xi), QP (OWNERSHIP_STRENGTH, ownership_strength, Xi),
QP (PRESENTATION, presentation, XE2, Xbx2), QP (PRESENTATION, presentation, XE2, Xbx2),
QP (PARTITION, partition, XZ), QP (PARTITION, partition, XQ, XS, XSTOP),
QP (TIME_BASED_FILTER, time_based_filter, XD), QP (TIME_BASED_FILTER, time_based_filter, XD),
QP (TRANSPORT_PRIORITY, transport_priority, Xi), QP (TRANSPORT_PRIORITY, transport_priority, Xi),
PP (PROTOCOL_VERSION, protocol_version, Xox2), 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_READER_LIFESPAN, reader_lifespan, Xb, XD),
QP (PRISMTECH_WRITER_DATA_LIFECYCLE, writer_data_lifecycle, Xb), QP (PRISMTECH_WRITER_DATA_LIFECYCLE, writer_data_lifecycle, Xb),
QP (PRISMTECH_READER_DATA_LIFECYCLE, reader_data_lifecycle, XDx2), 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", { PID_PAD, PDF_QOS, QP_CYCLONE_IGNORELOCAL, "CYCLONE_IGNORELOCAL",
offsetof (struct nn_plist, qos.ignorelocal), membersize (struct nn_plist, qos.ignorelocal), offsetof (struct nn_plist, qos.ignorelocal), membersize (struct nn_plist, qos.ignorelocal),
{ .desc = { XE2, XSTOP } }, 0 }, { .desc = { XE2, XSTOP } }, 0 },
@ -1057,7 +1234,7 @@ static const struct piddesc piddesc_prismtech[] = {
QP (PRISMTECH_READER_LIFESPAN, reader_lifespan, Xb, XD), QP (PRISMTECH_READER_LIFESPAN, reader_lifespan, Xb, XD),
QP (PRISMTECH_WRITER_DATA_LIFECYCLE, writer_data_lifecycle, Xb), QP (PRISMTECH_WRITER_DATA_LIFECYCLE, writer_data_lifecycle, Xb),
QP (PRISMTECH_READER_DATA_LIFECYCLE, reader_data_lifecycle, XDx2), 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_BUILTIN_ENDPOINT_SET, prismtech_builtin_endpoint_set, Xu),
PP (PRISMTECH_PARTICIPANT_VERSION_INFO, prismtech_participant_version_info, Xux5, XS), PP (PRISMTECH_PARTICIPANT_VERSION_INFO, prismtech_participant_version_info, Xux5, XS),
PP (PRISMTECH_EXEC_NAME, exec_name, 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; /* List of entries that require unalias, fini processing;
initialized by nn_plist_init_tables; will assert when initialized by nn_plist_init_tables; will assert when
table too small or too large */ table too small or too large */
static const struct piddesc *piddesc_unalias[18]; static const struct piddesc *piddesc_unalias[19];
static const struct piddesc *piddesc_fini[18]; static const struct piddesc *piddesc_fini[19];
static ddsrt_once_t table_init_control = DDSRT_ONCE_INIT; static ddsrt_once_t table_init_control = DDSRT_ONCE_INIT;
static nn_parameterid_t pid_without_flags (nn_parameterid_t pid) 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 ((*fs->present & entry->present_flag) && (*fs->aliased & entry->present_flag))
{ {
if (!(entry->flags & PDF_FUNCTION)) 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) else if (entry->op.f.unalias)
entry->op.f.unalias (dst, &dstoff); entry->op.f.unalias (dst, &dstoff);
*fs->aliased &= ~entry->present_flag; *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)) 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 /* 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 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. 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: dst & src have the same type, so offset in src is the same;
Note: unalias may have to look at */ Note: unalias may have to look at */
memcpy ((char *) dst + dstoff, (const char *) src + dstoff, entry->size); memcpy ((char *) dst + dstoff, (const char *) src + dstoff, entry->size);
*fs_dst->present |= entry->present_flag; *fs_dst->present |= entry->present_flag;
if (!(entry->flags & PDF_FUNCTION)) 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) else if (entry->op.f.unalias)
entry->op.f.unalias (dst, &dstoff); 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; const nn_parameter_t *par = src;
while (par->parameterid != pid) while (par->parameterid != pid)
{ {
if (pid == PID_SENTINEL) if (par->parameterid == PID_SENTINEL)
return NULL; return NULL;
par = (const nn_parameter_t *) ((const char *) (par + 1) + par->length); 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 (PRISMTECH_ENTITY_FACTORY, { LOGB1 ("entity_factory=%u", xqos->entity_factory.autoenable_created_entities); });
DO (CYCLONE_IGNORELOCAL, { LOGB1 ("ignorelocal=%u", xqos->ignorelocal.value); }); 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 PRINTARG_DUR
#undef FMT_DUR #undef FMT_DUR

View 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
View 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);
}

View 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);
}