4.12. Working With Sequences

4.12.1. Introduction

RTI Connext DDS Micro uses IDL as the language to define data-types. One of the constructs in IDL is the sequence: a variable-length vector where each element is of the same type. This section describes how to work with sequences; in particular, the string sequence since it has special properties.

4.12.2. Working with Sequences

4.12.2.1. Overview

Logically a sequence can be viewed as a variable-length vector with N elements, as illustrated below. Note that sequences indices are 0 based.

     +---+
  0  | T |
     +---+
  1  | T |
     +---+
  2  | T |
     +---+
       |
       |
     +---+
N-1  | T |
     +---+

There are three types of sequences in Connext DDS Micro:

  • Builtin sequences of primitive IDL types.
  • Sequences defined in IDL using the sequence keyword.
  • Sequences defined by the application.

The following builtin sequences exist (please refer to C API Reference and C++ API Reference for the complete API).

IDL Type Connext DDS Micro Type Connext DDS Micro Sequence
octet DDS_Octet DDS_OctetSeq
char DDS_Char DDS_CharSeq
boolean DDS_Boolean DDS_BooleanSeq
short DDS_Short DDS_ShortSeq
unsigned short DDS_UnsignedShort DDS_UnsignedShortSeq
long DDS_Long DDS_LongSeq
unsigned long DDS_UnsignedLong DDS_UnsignedLongSeq
enum DDS_Enum DDS_EnumSeq
wchar DDS_Wchar DDS_WcharSeq
long long DDS_LongLong DDS_LongLongSeq
unsigned long long DDS_UnsignedLongLong DDS_UnsignedLongLongSeq
float DDS_Float DDS_FloatSeq
double DDS_Double DDS_DoubleSeq
long double DDS_LongDouble DDS_LongDoubleSeq
string DDS_String DDS_StringSeq
wstring DDS_Wstring DDS_WstringSeq

The following are important properties of sequences to remember:

  • All sequences in Connext DDS Micro must be finite.
  • All sequences defined in IDL are sized based on IDL properties and must not be resized. That is, never call set_maximum() on a sequence defined in IDL. This is particularly important for string sequences.
  • Application defined sequences can be resized using set_maximum() or ensure_length().
  • There are two ways to use a DDS_StringSeq (they are type-compatible):
    • A DDS_StringSeq originating from IDL. This sequence is sized based on maximum sequence length and maximum string length.
    • A DDS_StringSeq originating from an application. In this case the sequence element memory is unmanaged.
  • All sequences have an initial length of 0.

4.12.2.2. Working with IDL Sequences

Sequences that originate from IDL are created when the IDL type they belong to is created. IDL sequences are always initialized with the maximum size specified in the IDL file. The maximum size of a type, and hence the sequence size, is used to calculate memory needs for serialization and deserialization buffers. Thus, changing the size of an IDL sequence can lead to hard to find memory corruption.

The string and wstring sequences are special in that not only is the maximum sequence size allocated, but because strings are also always of a finite maximum length, the maximum space needed for each string element is also allocated. This ensure that Connext DDS Micro can prevent memory overruns and validate input.

Some typical scenarios with a long sequence and a string sequence defined in IDL is shown below:

/* In IDL */
struct SomeIdlType
{
    // A sequence of 20 longs
    sequence<long,20> long_seq;

    // A sequence of 10 strings, each string has a maximum length of 255 bytes
    // (excluding NUL)
    sequence<string<255>,10> string_seq;
}

/* In C source */
SomeIdlType *my_sample = SomeIdlTypeTypeSupport_create_data()

DDS_LongSet_set_length(&my_sample->long_seq,5);
DDS_StringSeq_set_length(&my_sample->string_seq,5);

/* Assign the first 5 longs in long_seq */
for (i = 0; i < 5; ++i)
{
    *DDS_LongSeq_get_reference(&my_sample->long_seq,i) = i;
    snprintf(*DDS_StringSeq_get_reference(&my_sample->string_seq,0),255,"SomeString %d",i);
}

SomeIdlTypeTypeSupport_delete_data(my_sample);

/* In C++ source */
SomeIdlType *my_sample = SomeIdlTypeTypeSupport::create_data()

/* Assign the first 5 longs in long_seq */

my_sample->long_seq.length(5);
my_sample->string_seq.length(5);

for (i = 0; i < 5; ++i)
{
    /* use method */
    *DDSLongSeq_get_reference(&my_sample->long_seq,i) = i;
    snprintf(*DDSStringSeq_get_reference(&my_sample->string_seq,i),255,"SomeString %d",i);

    /* or assignment */
    my_sample->long_seq[i] = i;
    snprintf(my_sample->string_seq[i],255,"SomeString %d",i);
}

SomeIdlTypeTypeSupport::delete_data(my_sample);

Note that in the example above the sequence length is set. The maximum size for each sequence is set when my_sample is allocated.

A special case is to copy a string sequence from a sample to a string sequence defined outside of the sample. This is possible, but care must be taken to ensure that the memory is allocated properly:

Consider the IDL type from the previous example. A string sequence of equal size can be allocated as follows:

struct DDS_StringSeq app_seq = DDS_SEQUUENCE_INITIALIZER;

/* This ensures that memory for the strings are allocated upfront */
DDS_StringSeq_set_maximum_w_max(&app_seq,10,255);

DDS_StringSeq_copy(&app_seq,&my_sample->string_seq);

If instead the following code was used, memory for the string in app_seq would be allocated as needed.

struct DDS_StringSeq app_seq = DDS_SEQUUENCE_INITIALIZER;

/* This ensures that memory for the strings are allocated upfront */
DDS_StringSeq_set_maximum(&app_seq,10);

DDS_StringSeq_copy(&app_seq,&my_sample->string_seq);

4.12.2.3. Working with Application Defined Sequences

Application defined sequences work in the same way as sequences defined in IDL with two exceptions:

  • The maximum size is 0 by default. It is necessary to call set_maximum or ensure_length to allocate space.

  • DDS_StringSet_set_maximum does not allocate space for the string pointers. The memory must be allocated on a per needed basis and calls to _copy may reallocate memory as needed. Use DDS_StringSeq_set_maximum_w_max or DDS_StringSeq_ensure_length_w_max to also allocate pointers. In this case _copy will not reallocate memory.

    Note that it is not allowed to mix the use of calls that pass the max (ends in _w_max) and calls that do not. Doing so may cause memory leaks and/or memory corruption.

struct DDS_StringSeq my_seq = DDS_SEQUENCE_INITIALIZER;

DDS_StringSeq_ensure_length(&my_seq,10,20);

for (i = 0; i < 10; i++)
{
    *DDS_StringSeq_get_reference(&my_seq,i) = DDS_String_dup("test");
}

DDS_StringSeq_finalize(&my_seq);

DDS_StringSeq_finalize automatically frees memory pointed to by each element using DDS_String_free. All memory allocated to a string element should be allocated using a DDS_String function.

It is possible to assign any memory to a string sequence element if all elements are released manually first:

struct DDS_StringSeq my_seq = DDS_SEQUENCE_INITIALIZER;

DDS_StringSeq_ensure_length(&my_seq,10,20);

for (i = 0; i < 10; i++)
{
    *DDS_StringSeq_get_reference(&my_seq,i) = static_string[i];
}

/* Work with the sequence */

for (i = 0; i < 10; i++)
{
    *DDS_StringSeq_get_reference(&my_seq,i) = NULL;
}

DDS_StringSeq_finalize(&my_seq);