Adds immediate migration results

This commit is contained in:
torsten committed 2026-08-18 22:12:24 +02:00
1 parent ba4121728b
commit 6a2a0667ab
2395 files changed
+357322 -2524

No files matched your search

@@ -0,0 +1,558 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
#define RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
#include "rti/flat/BuilderHelper.hpp"
#include "rti/flat/Builder.hpp"
#include "xcdr/xcdr_interpreter.h"
namespace rti { namespace flat {
/*
* Parameters describing a member to be added or built.
*
* The information encoded by this structure is used to set the appropriate
* bits in the header that precedes each member of a mutable type.
*/
struct MemberParams {
/*
* Indicates whether the member is part of the key.
*/
bool key;
/*
* Indicates whether the member is the discriminator of a union.
*/
bool discriminator;
/*
* Indicates whether the member has been marked as "must understand".
*/
bool must_understand;
MemberParams() : key(false), discriminator(false), must_understand(false)
{ }
/*
* Check whether the current XCDR2 compliance mask allows the
* specified features.
*
* This function will cache the value of the compliance mask to avoid
* repeated nested function calls at runtime.
*
* @param check_mask The feature bits to check in the compliance mask.
* @param reload_mask If true, force reloading of the compliance mask
* from the global configuration.
* @return true if all the bits in check_mask are set in the compliance
* mask.
*/
static bool xcdr2_compliant(
const RTIXCdrXTypesComplianceMask check_mask,
const bool reload_mask)
{
static bool g_compliance_mask_set = false;
static RTIXCdrXTypesComplianceMask g_compliance_mask = 0;
// Initialize the compliance mask on first use
// There could be a data race here but the worst that can happen is
// that the compliance mask is read more than once.
// The goal is to avoid nested function calls on each and every call.
if (reload_mask || !g_compliance_mask_set) {
g_compliance_mask =
RTIXCdrInterpreter_getGlobalXtypeComplianceMask();
g_compliance_mask_set = true;
}
return (g_compliance_mask & check_mask);
}
/*
* Check if the must_understand bit should be set in the header for this
* member.
*
* The bit will always be set if the must_understand attribute is true.
* Additionally, if the current XCDR2 compliance mask allows it, the bit
* will also be set for key members and discriminators.
*
* @return true if the must_understand bit should be set.
*/
bool has_must_understand_bit() const
{
return must_understand
|| (xcdr2_compliant(
RTI_XCDR_XTYPES_MUST_UNDERSTAND_IN_KEY_AND_DISCRIMINATOR_BIT,
false)
&& (key || discriminator));
}
};
/*
* Provides the common functionality for builders of aggregated types
*
* Extends and specializes AbstractBuilder to add or override the functionality
* required to build an aggregated type:
*
* - Add or build members, prepending the member header
* - Add dheader at the beginning
* - Add parameter-list-end header at the end
*/
/**
* @ingroup RTIFlatBuildersModule
* @brief Base class of struct and union builders.
*
* This class contains implementation details and doesn't add any public function
* to AbstractBuilder. See MyFlatMutableBuilder for a concrete example of a
* struct builder.
*/
class AggregationBuilder : public AbstractBuilder {
protected:
AggregationBuilder() :
dheader_position_(NULL),
emheader_position_(NULL)
{
}
// Create a new top-level builder
AggregationBuilder(
unsigned char *initial_buffer,
offset_t size,
bool initialize_members) :
AbstractBuilder(initial_buffer, size, initialize_members),
dheader_position_(NULL),
emheader_position_(NULL)
{
dheader_position_ = stream().serialize_dheader();
if (dheader_position_ == NULL) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(invalidate());
}
}
public:
// For internal use and testing
//
// Modifies the encapsulation set in the constructor with a custom value.
// This function can only be called after the construction of a non-nested
// Builder, before any member is built or added.
//
void set_encapsulation_impl(RTIXCdrEncapsulationId encapsulation_id)
{
// Only a top-level Builder can change the encapsulation
RTI_FLAT_CHECK_PRECONDITION(!is_nested(), return);
char *current_position = (char *) owned_stream_.current_position();
// This operation is only valid right after construction, when no
// member has been added yet
RTI_FLAT_CHECK_PRECONDITION(
current_position == dheader_position_ + RTI_XCDR_DHEADER_SIZE,
return);
owned_stream_.skip_back(
RTI_XCDR_DHEADER_SIZE + RTI_XCDR_ENCAPSULATION_HEADER_SIZE);
if (!RTIXCdrFlatSample_initializeEncapsulationAndStream(
(char *) owned_stream_.buffer(),
&owned_stream_.c_stream(),
encapsulation_id,
owned_stream_.total_size())) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
}
stream().serialize_dheader();
}
protected:
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
AggregationBuilder(AggregationBuilder&& other) = default;
AggregationBuilder& operator=(AggregationBuilder&& other)
{
if (this == &other) {
return *this;
}
finish_untyped_impl();
AbstractBuilder::operator=(static_cast<AbstractBuilder&&>(other));
dheader_position_ = other.dheader_position_;
emheader_position_ = other.emheader_position_;
return *this;
}
#else
public:
// Enable the safe-move-constructor idiom without C++11 move constructors
struct UntypedAggregationBuilderMoveProxy : AbstractBuilderMoveProxy {
char * dheader_position_;
char * emheader_position_;
};
operator UntypedAggregationBuilderMoveProxy () throw() // move-constructor idiom
{
UntypedAggregationBuilderMoveProxy other;
move_to(other);
return other;
}
protected:
void move_from(UntypedAggregationBuilderMoveProxy& other)
{
AbstractBuilder::move_from(other);
dheader_position_ = other.dheader_position_;
emheader_position_ = other.emheader_position_;
}
void move_to(UntypedAggregationBuilderMoveProxy& other)
{
AbstractBuilder::move_to(other);
other.dheader_position_ = dheader_position_;
other.emheader_position_ = emheader_position_;
}
#endif
protected:
virtual ~AggregationBuilder()
{
// AggregationBuilder::finish_untyped_impl() is specialized
// with respect to AbstractBuilder::finish_untyped_impl()
finish_untyped_impl();
}
template <typename T>
bool add_primitive_member(
member_id_t id,
T value,
const MemberParams& params)
{
RTI_FLAT_BUILDER_CHECK_VALID(return false);
rti::xcdr::Stream::Memento stream_memento(stream());
if (!begin_emheader(id, detail::primitive_lc_code<T>::value, params)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
}
// no need to call finish_emheader()
if (!stream().serialize_no_align(value)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
}
stream_memento.discard(); // success: do not restore the current position
return true;
}
template <typename T>
bool add_primitive_member(member_id_t id, T value)
{
static const MemberParams default_params;
return add_primitive_member(id, value, default_params);
}
template <typename OffsetType>
OffsetType add_member(member_id_t id, const MemberParams& params)
{
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
// save state in case of error
rti::xcdr::Stream::Memento stream_memento(stream());
return add_member<OffsetType>(id, stream_memento, params);
}
template <typename OffsetType>
OffsetType add_member(member_id_t id)
{
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
// save state in case of error
rti::xcdr::Stream::Memento stream_memento(stream());
static const MemberParams default_params;
return add_member<OffsetType>(id, stream_memento, default_params);
}
template <typename OffsetType>
OffsetType add_member(
member_id_t id,
rti::xcdr::Stream::Memento& stream_memento)
{
static const MemberParams default_params;
return add_member<OffsetType>(id, stream_memento, default_params);
}
template <typename OffsetType>
OffsetType add_member(
member_id_t id,
rti::xcdr::Stream::Memento& stream_memento,
const MemberParams& params)
{
// fixed-size non-primitive members may have a dheader or not; when they
// do the LC code is 5, otherwise it is 4.
if (!begin_emheader(
id,
detail::is_fixed_type_w_dheader<OffsetType>::value() ? 5
: 4,
params)) {
return OffsetType();
}
OffsetType result = AbstractBuilder::add_element<OffsetType>();
finish_emheader();
stream_memento.discard(); // success - do not restore the previous state
return result;
}
template <typename NestedBuilder>
NestedBuilder build_member(member_id_t id, const MemberParams& params)
{
RTI_FLAT_BUILDER_CHECK_VALID(return NestedBuilder());
// save state in case of error
rti::xcdr::Stream::Memento stream_memento(stream());
return build_member<NestedBuilder>(id, stream_memento, params);
}
template <typename NestedBuilder>
NestedBuilder build_member(member_id_t id)
{
static const MemberParams default_params;
return build_member<NestedBuilder>(id, default_params);
}
template <typename NestedBuilder>
NestedBuilder build_member(
member_id_t id,
rti::xcdr::Stream::Memento& stream_memento)
{
static const MemberParams default_params;
return build_member<NestedBuilder>(id, stream_memento, default_params);
}
template <typename NestedBuilder>
NestedBuilder build_member(
member_id_t id,
rti::xcdr::Stream::Memento& stream_memento,
const MemberParams& params)
{
if (!begin_emheader(
id,
static_cast<RTIXCdrUnsignedLong>(
detail::lc_code<NestedBuilder>::value()),
params)) {
return NestedBuilder();
}
return AbstractBuilder::build_element_no_align<NestedBuilder>(
stream_memento);
}
unsigned char* finish_sample_impl()
{
RTI_FLAT_BUILDER_CHECK_VALID(return NULL);
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return NULL);
RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(return NULL);
if (!stream().finish_dheader(dheader_position_)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return NULL);
}
unsigned char *return_sample = stream().buffer();
invalidate();
return return_sample;
}
// Same as finish_impl() but it doesn't throw exceptions or return an Offset
void finish_untyped_impl()
{
if (is_valid() && is_nested()) {
stream().finish_dheader(dheader_position_);
AbstractBuilder::finish_untyped_impl();
}
}
template <typename OffsetType>
OffsetType finish_impl()
{
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return OffsetType());
stream().finish_dheader(dheader_position_);
return AbstractBuilder::finish_impl<OffsetType>();
}
public:
// For internal use; need to use rti::flat::discard_sample(writer, builder)
unsigned char * discard_sample_impl()
{
RTI_FLAT_BUILDER_CHECK_VALID(return NULL);
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return NULL);
RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(return NULL);
unsigned char *buffer = this->buffer();
invalidate();
return buffer;
}
private:
void finish_member() // override noexcept
{
if (!is_valid()) {
return;
}
finish_emheader();
AbstractBuilder::finish_member();
}
private:
bool begin_emheader(
member_id_t id,
RTIXCdrUnsignedLong lc,
const MemberParams& params)
{
RTI_FLAT_BUILDER_CHECK_NOT_BOUND(return false);
if (!stream().serialize_emheader(
emheader_position_,
id,
lc,
params.has_must_understand_bit())) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return false);
}
return true;
}
void finish_emheader() // noexcept
{
if (emheader_position_ != NULL) {
stream().finish_emheader(emheader_position_);
emheader_position_ = NULL;
}
}
protected:
// Create a nested builder
AggregationBuilder(nested_tag_t, AbstractBuilder& parent, unsigned int alignment)
: AbstractBuilder(nested_tag_t(), parent, alignment),
dheader_position_(NULL),
emheader_position_(NULL)
{
dheader_position_ = stream().serialize_dheader();
if (dheader_position_ == NULL) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(invalidate());
}
}
private:
char * dheader_position_;
char * emheader_position_;
};
// Specializes an AggregationBuilder to enforce that only one member can
// be set at a time
/**
* @ingroup RTIFlatBuildersModule
* @brief Base class of builders for user-defined mutable unions.
*
* Union builders can only add or build a single member.
*
* This class contains implementation details and doesn't add any public function
* to AbstractBuilder.
*/
template <typename Discriminator>
class UnionBuilder : public AggregationBuilder {
protected:
UnionBuilder()
{
}
// Create a new top-level builder
UnionBuilder(
unsigned char *initial_buffer,
offset_t size,
bool initialize_members) :
AggregationBuilder(initial_buffer, size, initialize_members)
{
}
template <typename T>
bool add_primitive_member(member_id_t id, Discriminator disc, T value)
{
RTI_FLAT_BUILDER_CHECK_VALID(return false);
// Always start at the beginning and override any member or discriminator
// that may have been set
move_to_discriminator();
rti::xcdr::Stream::Memento stream_memento(stream());
if (!add_discriminator(disc)) {
return false;
}
if (!AggregationBuilder::add_primitive_member(id, value)) {
return false;
}
stream_memento.discard(); // success: do not restore the current position
return true;
}
template <typename OffsetType>
OffsetType add_member(member_id_t id, Discriminator disc)
{
RTI_FLAT_BUILDER_CHECK_VALID(return OffsetType());
move_to_discriminator();
rti::xcdr::Stream::Memento stream_memento(stream());
if (!add_discriminator(disc)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return OffsetType());
}
return AggregationBuilder::add_member<OffsetType>(id, stream_memento);
}
template <typename NestedBuilder>
NestedBuilder build_member(member_id_t id, Discriminator disc)
{
RTI_FLAT_BUILDER_CHECK_VALID(return NestedBuilder());
move_to_discriminator();
rti::xcdr::Stream::Memento stream_memento(stream());
add_discriminator(disc);
return AggregationBuilder::build_member<NestedBuilder>(id, stream_memento);
}
// Create a nested builder
UnionBuilder(nested_tag_t, AbstractBuilder& parent, unsigned int alignment)
: AggregationBuilder(nested_tag_t(), parent, alignment)
{
}
private:
void move_to_discriminator()
{
stream().current_position(begin_position() + RTI_XCDR_DHEADER_SIZE);
}
bool add_discriminator(Discriminator disc)
{
MemberParams params;
params.discriminator = true;
return AggregationBuilder::add_primitive_member(0, disc, params);
}
};
} }
#endif // RTI_DDS_FLAT_AGGREGATIONBUILDERS_HPP_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,220 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_BUILDERHELPER_HPP_
#define RTI_DDS_FLAT_BUILDERHELPER_HPP_
namespace rti { namespace flat {
template <typename T>
class PrimitiveSequenceBuilder;
template <typename ElementBuilder>
class MutableSequenceBuilder;
template <typename ElementOffset>
class FinalSequenceBuilder;
template <typename ElementBuilder, unsigned int N>
class MutableArrayBuilder;
class StringBuilder;
namespace detail {
//
// lc_code:
//
template <size_t PrimitiveSize>
struct primitive_lc_code_helper;
template <>
struct primitive_lc_code_helper<1> {
enum {
single = 0, // LC = 1 => member length is 1
sequence = 5 // LC = 5 => member length is also NEXTINT
};
};
template <>
struct primitive_lc_code_helper<2> {
enum {
single = 1, // LC = 1 => member length is 2
sequence = 4 // LC = 4 => member length is provided as an additional int
};
};
template <>
struct primitive_lc_code_helper<4> {
enum {
single = 2, // LC = 2 => member length is 4
sequence = 6 // LC = 6 => member length is 4 * NEXTINT
};
};
template <>
struct primitive_lc_code_helper<8> {
enum {
single = 3, // LC = 3 => member length is 8
sequence = 7 // LC = 7 => member length is 8 * NEXTINT
};
};
template <typename T>
struct primitive_lc_code {
enum { value = primitive_lc_code_helper<sizeof(T)>::single };
};
// In general, use LC = 5, for mutable aggregation types. Final aggregation
// types don't use this 'lc_code' trait type. The other cases are specialized
// after this.
template <typename T, typename Enable = void>
struct lc_code {
static int value()
{
return 5; // LC = 5 => member length is provided in NEXTINT/
}
};
// For collections of complex types and enums, if we serialize the DHeader
// (XCDR2 compliant behavior) we use LC = 5; if we don't (legacy RTI behavior)
// we use LC = 4, since we codify the length in the member header.
template <typename T>
struct lc_code<MutableSequenceBuilder<T> > {
static int value()
{
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
}
};
template <typename T>
struct lc_code<FinalSequenceBuilder<T> > {
static int value()
{
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
}
};
template <typename T, unsigned int N>
struct lc_code<MutableArrayBuilder<T, N> > {
static int value()
{
return is_dheader_required_in_non_primitive_collections() ? 5 : 4;
}
};
#if defined(RTI_FLAT_DATA_NO_EXCEPTIONS)
// Traditional C++: no support for std::is_enum<T>
template <typename T>
struct lc_code<PrimitiveSequenceBuilder<T> > {
static int value()
{
return primitive_lc_code_helper<sizeof(T)>::sequence;
}
};
#else
template <typename T>
struct lc_code<
PrimitiveSequenceBuilder<T>,
typename std::enable_if<!std::is_enum<T>::value>::type> {
static int value()
{
return primitive_lc_code_helper<sizeof(T)>::sequence;
}
};
template <typename T>
struct lc_code<
PrimitiveSequenceBuilder<T>,
typename std::enable_if<std::is_enum<T>::value>::type> {
static int value()
{
return is_dheader_required_in_enum_collections()
? 5
: primitive_lc_code_helper<sizeof(T)>::sequence;
}
};
#endif
//
// Initialization of final types
//
template <typename Offset>
struct final_offset_initializer {
static bool initialize(Offset& offset)
{
return RTI_XCDR_TRUE == RTIXCdrFlatData_initializeSample(
(char *) offset.get_buffer(),
offset.get_buffer_size(),
rti::xcdr::type_programs<rti::flat::Sample<Offset> >::get());
}
};
// This will be specialized for enums in generated code (traditional C++)
// and for enum classes in FlatDataTraits.hpp (modern C++)
template <typename T, typename Enable = void>
struct default_primitive_value {
static T get()
{
return (T) 0;
}
};
template <typename T, unsigned int N>
struct final_offset_initializer<rti::flat::PrimitiveArrayOffset<T, N> > {
static bool initialize(rti::flat::PrimitiveArrayOffset<T, N>& array)
{
T default_value = default_primitive_value<T>::get();
if (default_value == (T) 0) { // don't need to deal with endianness
memset(array.get_buffer(), 0, array.get_buffer_size());
} else {
for (unsigned int i = 0; i < array.element_count(); i++) {
array.set_element(i, default_value);
}
}
return true;
}
};
template <typename T, unsigned int N>
struct final_offset_initializer<rti::flat::FinalAlignedArrayOffset<T, N> > {
static bool initialize(rti::flat::FinalAlignedArrayOffset<T, N>& array)
{
for (unsigned int i = 0; i < N; i++) {
T offset = array.get_element(i);
if (offset.is_null()) {
return false;
}
if (!final_offset_initializer<T>::initialize(offset)) {
return false;
}
}
return true;
}
};
} } }
#endif // RTI_DDS_FLAT_BUILDERHELPER_HPP_
@@ -0,0 +1,179 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
#define RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
//
// Enable debug assertions (RTI_FLAT_ASSERT) in debug mode
//
#if !defined(NDEBUG)
#define RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
#endif
//
// Error handling without C++ exceptions (traditional C++ and Micro C++ APIs)
//
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
// TODO: use MSG
#ifdef RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
#define RTI_FLAT_ASSERT(COND, ACTION) \
if (!(COND)) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Assertion failure: " #COND); \
ACTION; \
}
#else
#define RTI_FLAT_ASSERT(COND, ACTION)
#endif
// CR decission: Log error messages. Export function for each log kind (serialize, deserialize, precondition, skip)
// pass type/field (if possible) to serialization errors
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION) \
if (this->is_null()) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "null offset"); \
ACTION; \
}
#define RTI_FLAT_OFFSET_PRECONDITION_ERROR(MSG, ACTION) \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, MSG); \
ACTION
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION) \
if (!(COND)) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Precondition failure: " #COND); \
ACTION; \
}
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION) \
if (this->bind_position_) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "Builder is currently bound - call finish() first"); \
ACTION; \
}
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION) \
if (!this->is_nested()) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This is not a member builder; call finish_sample() instead"); \
ACTION; \
}
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION) \
if (this->is_nested()) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This is a member builder; call finish() instead"); \
ACTION; \
}
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION) \
if (!this->is_valid()) { \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, "This Builder is not valid"); \
ACTION; \
}
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION) \
if (!BUILDER.is_valid()) { \
this->set_failure(); \
RTIXCdrFlatData_logCreationError(__FILE__, __LINE__, "Builder"); \
ACTION; \
}
#define RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(ACTION) \
this->set_failure(); \
RTIXCdrFlatData_logBuilderOutOfResources(__FILE__, __LINE__); \
ACTION
#define RTI_FLAT_BUILDER_PRECONDITION_ERROR(MSG, ACTION) \
this->set_failure(); \
RTIXCdrFlatData_logPreconditionError(__FILE__, __LINE__, MSG); \
ACTION
//
// Error handling with C++ exceptions (modern C++ API)
//
#else // !RTI_FLAT_DATA_NO_EXCEPTIONS
#ifdef RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS
#define RTI_FLAT_ASSERT(COND, ACTION) \
if (!(COND)) throw dds::core::PreconditionNotMetError("Assertion failure: " #COND)
#else
#define RTI_FLAT_ASSERT(COND, ACTION)
#endif
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION) \
if (this->is_null()) throw dds::core::NullReferenceError("null offset")
#define RTI_FLAT_OFFSET_PRECONDITION_ERROR(MSG, ACTION) \
throw dds::core::PreconditionNotMetError(MSG)
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION) \
if (!(COND)) throw dds::core::PreconditionNotMetError("Precondition failure: " #COND)
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION) \
if (bind_position_) \
throw dds::core::PreconditionNotMetError( \
"Builder is currently bound - call finish() first")
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION) \
if (!is_nested()) \
throw dds::core::PreconditionNotMetError( \
"This is not a member builder; call finish_sample() instead")
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION) \
if (is_nested()) \
throw dds::core::PreconditionNotMetError( \
"This is a member builder; call finish() instead")
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION) \
if (!is_valid()) \
throw dds::core::PreconditionNotMetError("This Builder is not valid")
// With exceptions, nothing to check (exception would have been thrown)
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION)
#define RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(ACTION) \
throw dds::core::OutOfResourcesError("builder reached end of buffer")
#define RTI_FLAT_BUILDER_PRECONDITION_ERROR(MSG, ACTION) \
throw dds::core::PreconditionNotMetError(MSG)
#endif // RTI_FLAT_DATA_NO_EXCEPTIONS
//
// Error checking (other than assertions) in release mode is enabled unless
// the sources are compiled with -DRTI_FLAT_DATA_DISABLE_ERROR_CHECKING
//
// In that case, most error checks that deal with preconditions due to incorrect
// usage of the API are disabled in release mode. This would lead to undefined
// behavior in case of bad usage, but can improve performance.
//
#if defined(RTI_FLAT_DATA_ENABLE_DEBUG_ASSERTIONS) \
|| !defined(RTI_FLAT_DATA_DISABLE_ERROR_CHECKING)
#define RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
#endif
#ifndef RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
#define RTI_FLAT_OFFSET_CHECK_NOT_NULL(ACTION)
#define RTI_FLAT_ASSERT(COND, ACTION)
#define RTI_FLAT_CHECK_PRECONDITION(COND, ACTION)
#define RTI_FLAT_BUILDER_CHECK_NOT_BOUND(ACTION)
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH(ACTION)
#define RTI_FLAT_BUILDER_CHECK_CAN_FINISH_SAMPLE(ACTION)
#define RTI_FLAT_BUILDER_CHECK_VALID(ACTION)
#define RTI_FLAT_BUILDER_CHECK_CREATE_BUILDER(BUILDER, ACTION)
#endif // RTI_FLAT_DATA_ENABLE_ERROR_CHECKING
namespace rti { namespace flat { namespace detail {
} } }
#endif // RTI_DDS_FLAT_EXCEPTIONHELPER_HPP_
@@ -0,0 +1,451 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_FLATSAMPLE_HPP_
#define RTI_DDS_FLAT_FLATSAMPLE_HPP_
// Note: FlatSampleImpl.hpp needs to be included in addition to this header.
#include <cstdlib>
#include <memory>
#include "xcdr/xcdr_stream_impl.h"
#include "xcdr/xcdr_flat_data.h"
#include "xcdr/xcdr_interpreter.h"
#include "rti/xcdr/Interpreter.hpp" // for type_programs
/**
* @defgroup RTIFlatSampleModule FlatData Samples
* @ingroup RTIFlatDataModule
* @brief A Sample represents an instance of the IDL topic-type and contains
* the data in serialized format.
*
* All FlatData topic-types are instantiations of rti::flat::Sample.
* This documentation uses the following three example types to illustrate the
* different ways FlatData IDL types map to C++:
* - \ref MyFlatFinal, the type generated for a final IDL struct
* - \ref MyFlatMutable, the type generated for a mutable IDL struct
* - \ref MyFlatUnion, the type generated for an IDL union
*/
namespace rti { namespace flat {
struct fixed_size_type_tag_t {};
struct variable_size_type_tag_t {};
typedef RTIXCdrUnsignedLong offset_t;
typedef RTIXCdrUnsignedLong member_id_t;
// The untemplated base of Sample<T>
class SampleBase {
public:
unsigned char* get_buffer()
{
return &data[0];
}
const unsigned char* get_buffer() const
{
return &data[0];
}
unsigned char* get_root_buffer()
{
return get_buffer() + RTI_XCDR_ENCAPSULATION_SIZE;
}
/*i
* @brief Provides the address of the top-level type
*/
const unsigned char* get_root_buffer() const
{
return get_buffer() + RTI_XCDR_ENCAPSULATION_SIZE;
}
void initialize_stream(
RTIXCdrStream& stream,
offset_t offset,
offset_t serialized_size) const
{
RTIXCdrFlatData_initializeStream(
&stream,
const_cast<unsigned char*>(get_buffer()),
offset,
serialized_size);
}
protected: // xlc compiler fails when the default constructor is private
#if !defined(RTI_FLAT_DATA_CXX11_DELETED_FUNCTIONS)
SampleBase() {} // Samples can only be created via Sample::create_data
SampleBase(const SampleBase&) {}
SampleBase& operator=(const SampleBase&);
#else
SampleBase() = delete;
SampleBase(const SampleBase&) = delete;
SampleBase& operator=(const SampleBase&) = delete;
#endif
// The actual size of data (and therefore, the Sample object) is variable,
// and equal to the serialized size of the IDL Sample
unsigned char data[1];
};
/**
* @ingroup RTIFlatSampleModule
* @brief The generic definition of FlatData topic-types
*
* @tparam OffsetType The Offset to the beginning of the data Sample (the root),
* for example MyFlatFinalOffset, or MyFlatMutableOffset.
*
* For a FlatData IDL type, \nddsgen generates an instantiation of this class,
* such as MyFlatFinal or MyFlatMutable. That's the <b>topic-type</b> used to
* declare a \idref_Topic and write or read FlatData samples.
*
* A FlatData Sample owns an inline buffer that contains the serialized data.
* To access the Sample data members we use \ref RTIFlatOffsetsModule "Offsets",
* iterators that point to the position of a member in the Sample buffer. The
* root() is the Offset to the top-level element (for example MyFlatMutableOffset).
* From there MyFlatMutableOffset's member functions provide access to its
* members.
*
* The method to create a Sample varies depending on whether the type is final
* or mutable.
*
* Samples of <b>final</b> FlatData types always have the same size, and
* can be obtained directly with \idref_FooDataWriter_get_loan().
*
* Samples of <b>mutable</b> FlatData types need to be "built" using the Builder
* returned by rti::flat::build_data() (MyFlatMutableBuilder in this example).
* A Builder allows adding each member individually in any order, and sizing
* sequences as needed.
*
* See \ref PublishingFlatData for code snippets.
*
* Samples created with \idref_FooDataWriter_get_loan() or rti::flat::build_data()
* are DataWriter-managed and do not need to be explicitly deleted. See
* \idref_FooDataWriter_get_loan() for an explanation of the lifecycle of
* DataWriter-managed samples.
*
* Samples received with a \idref_FooDataReader can be examined by obtaining the
* root(), but cannot be modified. See \ref SubscribingFlatData.
*
* FlatData samples are not value types. They don't provide copy constructors
* or assignment operators, because of their definition as an inline buffer that
* contains the serialized Sample at all times. A Sample can be cloned with
* the static function clone(). A cloned Sample is unmanaged and has to be
* deleted with the static function delete_data().
*/
template <typename OffsetType>
class Sample : public SampleBase {
public:
/**
* @brief The related Offset type
*
* This is the template parameter, OffsetType
*/
typedef OffsetType Offset;
/**
* @brief The related read-only Offset type
*
* For example, MyFlatMutableOffset::ConstOffset.
*/
typedef typename OffsetType::ConstOffset ConstOffset;
/**
* @brief Provides the Offset to the top-level type
*
* @return An Offset that allows reading and modifying the members of this
* Sample, for example MyFlatFinalOffset or MyFlatMutableOffset.
*
* This overload allows modifying the Sample.
*
* Example:
* \code
* MyFlatFinal *my_sample = ...; // for example, created with DataWriter::get_loan()
* MyFlatFinalOffset my_sample_root = my_sample->root();
* my_sample_root.my_primitive(33);
* auto my_member_offset = my_sample_root.my_complex();
* // ... modify my_member_offset
* \endcode
*/
Offset root()
{
return root_impl(typename Offset::offset_kind());
}
/**
* @brief Provides the Offset to the top-level type
*
* @return An Offset that allows reading the members of this Sample.
* For example, MyFlatFinalOffset::ConstOffset or MyFlatMutableOffset::ConstOffset.
*
* This overload doesn't allow modifying the Sample.
*
* Example:
* \code
* const MyFlatMutable *my_sample = ...; // for example, read from a DataReader
* MyFlatMutableOffset::ConstOffset my_sample_root = my_sample->root();
* std::cout << my_sample_root.my_primitive() << std::endl;
* auto my_member_offset = my_sample_root.my_mutable_seq();
* // ... read my_member_offset
* \endcode
*/
ConstOffset root() const
{
return root_impl(typename ConstOffset::offset_kind());
}
/** @private
* @brief Returns the endianness in which this Sample is serialized
*/
RTIXCdrEndian endian() const
{
RTIXCdrStream tmp_stream;
initialize_stream(tmp_stream, 0, RTI_XCDR_ENCAPSULATION_SIZE);
return tmp_stream._endian;
}
/** @private
* @brief Casts a byte buffer into a Sample of this type
*/
static Sample<OffsetType>* from_buffer(unsigned char *buffer)
{
return reinterpret_cast<Sample<OffsetType>*>(buffer);
}
/**
* @brief Creates an unmanaged FlatData Sample
*
* @note In general on the publication side it is recommended to create
* DataWriter-managed samples using \idref_FooDataWriter_get_loan
* (for final types) and rti::flat::build_data() (for mutable types).
*
* If the topic-type is final, the returned Sample can be initialized
* by obtaining the root().
*
* If the topic-type is mutable, the resulting Sample has no members, and
* root() returns a \ref rti::flat::OffsetBase::is_null "null" Offset.
* The result of create_data() must be used to
* create a Builder. This is a rare use case, and rti::flat::build_data()
* should be used in most situations.
*
* In all cases, the returned Sample must be explicitly deleted with
* delete_data().
*/
static Sample<OffsetType>* create_data(); // impl in FlatSampleImpl.hpp
/**
* @brief Clones a Sample, creating an unmanaged Sample
*
* @warning When this Sample has been created with \idref_FooDataWriter_get_loan
* or rti::flat::build_data(), the clone this function creates cannot be
* used in \idref_FooDataWriter_write, because the DataWriter is managing
* the lifecycle of its samples.
*
* Creates a new Sample and copies the underlying serialized buffer.
*
* delete_data() must be called to release the Sample.
*/
Sample<OffsetType>* clone() const
{
unsigned char *buffer = RTIXCdrFlatData_cloneSample(
get_buffer(),
sample_size());
if (buffer == NULL) {
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
throw std::bad_alloc();
#else
return NULL;
#endif
}
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
}
/**
* @brief Releases an unmanaged Sample
*
* Unmanaged samples are those created with create_data() or clone().
*
* @pre \p sample cannot have been created with \idref_FooDataWriter_get_loan
* or rti::flat::build_data().
*
* @post \p sample becomes invalid and shouldn't be used
*/
static void delete_data(rti::flat::Sample<OffsetType>* sample)
{
RTIXCdrFlatData_deleteSample(sample);
}
/** @private
*/
offset_t buffer_size() const
{
return sample_size() + RTI_XCDR_ENCAPSULATION_SIZE;
}
/** @private
*/
offset_t sample_size() const
{
return sample_size_impl(typename OffsetType::offset_kind());
}
private:
offset_t sample_size_impl(fixed_size_type_tag_t) const
{
return OffsetType::serialized_size(0);
}
offset_t sample_size_impl(variable_size_type_tag_t) const
{
return RTIXCdrFlatSample_getMutableSampleSize(
get_buffer(),
RTI_XCDR_ENCAPSULATION_SIZE);
}
Offset root_impl(fixed_size_type_tag_t)
{
return Offset(this, RTI_XCDR_ENCAPSULATION_SIZE);
}
Offset root_impl(variable_size_type_tag_t)
{
return Offset(this, RTI_XCDR_ENCAPSULATION_SIZE, sample_size());
}
ConstOffset root_impl(fixed_size_type_tag_t) const
{
return ConstOffset(this, RTI_XCDR_ENCAPSULATION_SIZE);
}
ConstOffset root_impl(variable_size_type_tag_t) const
{
return ConstOffset(this, RTI_XCDR_ENCAPSULATION_SIZE, sample_size());
}
};
template <typename T>
struct flat_type_traits;
} }
#ifdef DOXYGEN_DOCUMENTATION_ONLY
/**
* @ingroup RTIFlatSampleModule
* @brief Represents an arbitrary user-defined FlatData final IDL struct
*
* This documentation uses the following example IDL definition of MyFlatFinal:
*
* \code
* @language_binding(FLAT_DATA)
* @final
* struct MyFlatFinal {
* long my_primitive;
* FlatFinalBar my_complex; // Another arbitrary final FlatData type
* long my_primitive_array[10];
* FlatFinalBar my_complex_array[10];
* };
* \endcode
*
* For this type, \nddsgen generates:
* - MyFlatFinal (this instantiation of Sample),
* - MyFlatFinalOffset.
*
* Note that, as a final FlatData type, MyFlatFinal can only contain
* fixed-size types, such as primitives, other final FlatData structs, and arrays
* of fixed-size types.
*
* Samples of MyFlatFinal are created with \idref_FooDataWriter_get_loan.
* After creating a final Sample, modify its values using the MyFlatFinalOffset
* returned by \ref rti::flat::Sample::root "root()".
*/
typedef rti::flat::Sample<MyFlatFinalOffset> MyFlatFinal;
/**
* @ingroup RTIFlatSampleModule
* @brief Represents an arbitrary user-defined flat mutable IDL struct
*
* This documentation uses the following example IDL definition of MyFlatMutable:
*
* \code
* @language_binding(FLAT_DATA)
* @mutable
* struct MyFlatMutable {
* long my_primitive;
* @optional long my_optional_primitive;
* long my_primitive_array[10];
* sequence<long, 10> my_primitive_seq;
*
* MyFlatFinal my_final;
* MyFlatFinal my_final_array[10];
* sequence<MyFlatFinal, 10> my_final_seq;
*
* FlatMutableBar my_mutable;
* FlatMutableBar my_mutable_array[10];
* sequence<FlatMutableBar, 10> my_mutable_seq;
*
* string<255> my_string;
* sequence<string<255>, 10> my_string_seq;
* };
* \endcode
*
* For this type, \nddsgen generates:
* - MyFlatMutable (this instantiation of Sample),
* - MyFlatMutableOffset,
* - MyFlatMutableBuilder.
*
* As a mutable FlatData type, MyFlatMutable is not restricted to fixed-size
* members. Samples of MyFlatMutable are created with rti::flat::build_data(), which
* returns a MyFlatMutableBuilder. Once built, a mutable Sample can't change
* in size, but the value of members that already exist can be changed using
* the MyFlatMutableOffset returned by \ref rti::flat::Sample::root "root()".
*/
typedef rti::flat::Sample<MyFlatMutableOffset> MyFlatMutable;
/**
* @ingroup RTIFlatSampleModule
* @brief Represents an arbitrary user-defined flat mutable IDL union
*
* This documentation uses the following example IDL definition of MyFlatUnion:
*
* \code
* @language_binding(FLAT_DATA)
* @mutable
* union MyFlatUnion switch (long) {
* case 0:
* long my_primitive;
* case 1:
* case 2:
* MyFlatMutable my_mutable;
* case 3:
* MyFlatFinal my_final;
* };
* \endcode
*
* @note FlatData unions can only be <b>mutable</b> since unions are,
* by definition, variable-size types.
*
* For this type, \nddsgen generates:
* - MyFlatUnion (this instantiation of Sample),
* - MyFlatUnionOffset,
* - MyFlatUnionBuilder.
*
*/
typedef rti::flat::Sample<MyFlatUnionOffset> MyFlatUnion;
#endif
#endif // RTI_DDS_FLAT_FLATSAMPLE_HPP_
@@ -0,0 +1,76 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
#define RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
// This file includes the implementation of some templates that need to
// appear after some types are defined in the generated code. This is a
// a restriction on some platforms such as pentiumInty11.pcx64 and other Intys,
// sparcSol2.10gcc3.4.2.
// For example, type_programs<T> needs to be defined (as an specialization
// for T) before its used in create_data().
#include "rti/flat/FlatSample.hpp"
namespace rti { namespace flat {
namespace detail {
template <typename OffsetType>
Sample<OffsetType>* create_data_impl(fixed_size_type_tag_t)
{
unsigned char *buffer = RTIXCdrFlatData_createSampleFinal(
OffsetType::serialized_size(0),
rti::xcdr::type_programs<Sample<OffsetType> >::get());
if (buffer == NULL) {
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
throw std::bad_alloc();
#else
return NULL;
#endif
}
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
}
template <typename OffsetType>
Sample<OffsetType>* create_data_impl(variable_size_type_tag_t)
{
unsigned char *buffer = RTIXCdrFlatData_createSampleMutable(
rti::xcdr::type_programs<Sample<OffsetType> >::get());
if (buffer == NULL) {
#ifndef RTI_FLAT_DATA_NO_EXCEPTIONS
throw std::bad_alloc();
#else
return NULL;
#endif
}
return reinterpret_cast<rti::flat::Sample<OffsetType> *>(buffer);
}
}
// Declared in FlatSample.hpp
template <typename OffsetType>
Sample<OffsetType>* Sample<OffsetType>::create_data()
{
return detail::create_data_impl<OffsetType>(
typename OffsetType::offset_kind());
}
} }
#endif // RTI_DDS_FLAT_FLATSAMPLEIMPL_HPP_
@@ -0,0 +1,264 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_FLATTYPETRAITS_HPP_
#define RTI_DDS_FLAT_FLATTYPETRAITS_HPP_
#include "rti/flat/Offset.hpp"
#include "rti/flat/SequenceOffsets.hpp"
#include "rti/flat/AggregationBuilders.hpp"
namespace rti { namespace flat {
/**
* @ingroup RTIFlatSampleModule
* @brief Given a Sample, an Offset or a Builder, it allows obtaining the other types.
*
* @tparam T One of the following:
* \li A Sample type, such as \ref MyFlatMutable
* \li An Offset type, such as MyFlatMutableOffset
* \li A Builder type, such as MyFlatMutableBuilder
*
* <p>Given \p T, this type provides the following typedefs:
* \li \p flat_type_traits<T>::offset, T's related offset type
* (undefined if T itself is an Offset)
* \li \p flat_type_traits<T>::builder, T's related builder type
* (undefined if T itself is a Builder, or the topic-type is not mutable)
* \li \p flat_type_traits<T>::flat_type, T's related Sample type
* (undefined if T itself is a Sample type)
* \li \p flat_type_traits<T>::plain_type, T's equivalent definition as a plain
* (non-FlatData) type.
*
* For example, for \p T = \ref MyFlatMutable, flat_type_traits is defined as follows:
*
* \code
* template <>
* struct flat_type_traits<MyFlatMutable> {
* typedef MyFlatMutablePlainHelper plain_type;
* typedef MyFlatMutableOffset offset;
* typedef MyFlatMutableBuilder builder;
* };
* \endcode
*
* Or if \p T = MyFlatMutableOffset:
*
* \code
* template <>
* struct flat_type_traits<MyFlatMutableOffset> {
* typedef MyFlatMutable flat_type;
* typedef MyFlatMutablePlainHelper plain_type;
* typedef MyFlatMutableBuilder builder;
* };
* \endcode
*
* @see rti::flat::plain_cast()
*/
// For aggregation types, this template is to be specialized in generated code
template <typename T>
struct flat_type_traits;
template <typename T, unsigned int N>
struct flat_type_traits<FinalArrayOffset<T, N> > {
typedef typename flat_type_traits<T>::plain_type plain_type;
};
template <typename T, unsigned int N>
struct flat_type_traits<PrimitiveArrayOffset<T, N> > {
typedef T plain_type;
};
template <typename T>
struct flat_type_traits<PrimitiveSequenceOffset<T> > {
typedef T plain_type;
};
template <typename T, unsigned int N>
struct flat_type_traits<FinalAlignedArrayOffset<T, N> > {
typedef typename flat_type_traits<T>::plain_type plain_type;
};
template <typename T, unsigned int N>
struct flat_type_traits<MutableArrayOffset<T, N> > {
typedef typename flat_type_traits<T>::plain_type plain_type;
};
template <typename T>
struct flat_type_traits<SequenceOffset<T> > {
typedef typename flat_type_traits<T>::plain_type plain_type;
};
template <>
struct flat_type_traits<StringOffset > :
flat_type_traits<PrimitiveSequenceOffset<char> > {
};
template <typename T>
struct flat_type_traits<PrimitiveOffset<T> > {
typedef T plain_type;
};
/**
* @ingroup RTIFlatOffsetsModule
* @brief Casts into an equivalent plain C++ type
*
* Some FlatData types can be cast to their equivalent <b>plain</b> definition as
* a regular non-FlatData C++ type. This is a more efficient way to access
* the data. This function casts, if possible, the member pointed to by the
* offset argument to an equivalent plain C++ type. Any changes made through
* the plain type are reflected in the FlatData sample.
*
* @tparam OffsetType The Offset type
*
* @pre plain_cast requires the type to meet the following restrictions:
* - The type must be a final struct, or an array or sequence of members of a
* type that meets these restrictions (including primitive types)
* - The type must be defined in a way such that the packing of the plain C++ type
* doesn't differ from the padding in XCDR2.
* - The type may not inherit from another type.
* - In addition, the sample must be serialized in the native endianness.
* For example, if a subscribing application on a little-endian platform
* receives a sample published from a big-endian system, it is not possible
* to plain_cast the sample or any of its members, except if the member is a
* primitive array or sequence of 1-byte elements.
*
* \ref rti::flat::OffsetBase::is_cpp_compatible "offset.is_cpp_compatible()"
* indicates if the member meets the requirements. If the type doesn't, this
* function \if CPP2_LANGUAGE_ONLY throws dds::core::PreconditionNotMetError.
* \else returns NULL. \endif
*
* @param offset The offset to the member to cast.
* @return The data that \p offset referred to, cast as a plain C++ type with the
* same definition. If \p offset is an array or sequence member, the returned pointer
* represents a C++ array with the same number of elements.
*
* The following table summarizes the possible parameters to this function (assuming
* that they meet the previous requirements), and the return type in each case:
*
* | \p OffsetType | return type
* | ------------- | -----------
* | Offset to a final struct, such as `MyFlatFinalOffset` | `MyFlatFinalPlainHelper*` (pointer to a single element). This is a type with the same IDL definition, but without `@language_binding(FLAT_DATA)`.
* | Offset to an array of final structs, such as `FinalArrayOffset<MyFlatFinalOffset, N>` | `MyFlatFinalPlainHelper*` (array of `N` elements)
* | Offset to a sequence of final structs, such as `SequenceOffset<MyFlatFinalOffset>` | `MyFlatFinalPlainHelper*` (array of `offset.element_count()` elements)
* | Offset to an array of primitive types, such as `PrimitiveArrayOffset<int32_t, N>` | `int32_t*` (array of `N` elements)
* | Offset to a sequence of primitive types, such as `PrimitiveSequenceOffset<int32_t>` | `int32_t*` (array of `offset.element_count()` elements)
*
*
* Due to the performance advantages that \p plain_cast offers, it is recommended
* to define FlatData types in a way that their largest member(s) can be
* \p plain_cast.
*
* The following example shows how to use \p plain_cast to cast a MyFlatFinalOffset
* into the type with the same IDL definition as \ref MyFlatFinal, but without
* the \p \@language_binding(FLAT_DATA) annotation:
*
* \code
* MyFlatMutable *my_sample = ...;
* auto my_root = my_sample->root();
* auto my_final = my_root.my_final();
*
* // Get the plain C++ type and modify an array
* auto my_final_plain = rti::flat::plain_cast(my_final);
* my_final_plain->my_primitive(3); // this is using the plain C++ setter
* my_final_plain->my_complex_array()[1].x(20); // this is a std::array
*
* // The change to my_complex_array is reflected in the FlatData sample
* std::cout << my_final.my_primitive() << std::endl; // 3
* std::cout << my_final.my_complex_array().get_element(1).x() << std::endl; // 20
* \endcode
*
* This example shows how to use plain_cast to efficiently build a sequence
* of final elements and a sequence of integers, both members of a mutable type:
*
* \code
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
*
* // 1)
* //
* // Build a sequence of 5 elements with add_n() instead of 5 calls to
* // add_next()
* rti::flat::FinalSequenceBuilder<MyFlatFinalOffset> seq_builder =
* builder.build_my_final_seq();
* seq_builder.add_n(5);
* // Finish the member, getting the offset to the member
* rti::flat::SequenceOffset<MyFlatFinalOffset> seq_offset = seq_builder.finish();
*
* // Cast it to an array of plain C++ type and initialize it.
* // This way to initialize it is more efficient than add_next().
* auto seq_elements = rti::flat::plain_cast(seq_offset);
* for (int i = 0; i < 5; i++) {
* seq_elements[i].my_primitive(i);
* // ...
* }
*
* // 2)
* //
* // Build a sequence of 1000 integers
* auto seq_builder = builder.build_my_primitive_seq();
* // make space for 1000 elements, but leave them uninitialized
* seq_builder.add_n(1000);
* auto seq_offset = seq_builder.finish();
*
* // Initialize the elements:
* int32_t *elements = rti::flat::plain_cast(seq_offset);
* for (int i = 0; i < 1000; i++) {
* elements[i] = ...;
* }
*
* // ... continue building the sample using 'builder'
* \endcode
*
*/
template <typename OffsetType>
typename flat_type_traits<OffsetType>::plain_type* plain_cast(OffsetType& offset)
{
RTI_FLAT_CHECK_PRECONDITION(offset.is_cpp_compatible(), return NULL);
return reinterpret_cast<typename flat_type_traits<OffsetType>::plain_type*>(
offset.get_buffer());
}
/**
* @ingroup RTIFlatOffsetsModule
* @brief Const version of plain_cast
*/
template <typename OffsetType>
const typename flat_type_traits<OffsetType>::plain_type* plain_cast(const OffsetType& offset)
{
RTI_FLAT_CHECK_PRECONDITION(offset.is_cpp_compatible(), return NULL);
return reinterpret_cast<const typename flat_type_traits<OffsetType>::plain_type*>(
offset.get_buffer());
}
namespace detail {
template <typename OffsetKind>
struct offset_kind_is_fixed_size {
enum { value = 0 };
};
template <>
struct offset_kind_is_fixed_size<rti::flat::fixed_size_type_tag_t> {
enum { value = 1 };
};
}
// Utility to determine if a Flat Data type is fixed size (final) or variable
// size (mutable)
template <typename T>
struct is_fixed_size_type
: detail::offset_kind_is_fixed_size<typename T::Offset::offset_kind>
{
};
} }
#endif // RTI_DDS_FLAT_FLATSAMPLE_HPP_
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,952 @@
/*
(c) Copyright, Real-Time Innovations, 2018-2024.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
#define RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
#include "rti/flat/Builder.hpp"
//
// Builders for sequences, strings, and arrays.
//
// Type hierarchy (- indicates an abstract builder; * a concrete, usable builder)
//
// - AbstractListBuilder
// * MutableArrayBuilder
// - AbstractSequenceBuilder
// * MutableSequenceBuilder
// * FinalSequenceBuilder
// * PrimitiveSequenceBuilder
// * StringBuilder
//
// Notes:
// - there's no "FinalArrayBuilder" because that's a fixed-size type and
// doesn't need a builder (it's constructed via an "add" operation, not a "build"
// operation).
// - MutableSequenceBuilder "builds" its elements
// - FinalSequenceBuilder "adds" its elements
//
namespace rti { namespace flat {
namespace detail {
template <bool IsForEnum>
struct DHeaderGeneratorImpl {
static bool reserve_dheader(rti::xcdr::Stream& stream)
{
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
return stream.serialize_dheader() != NULL;
} else {
return true;
}
}
static bool finish_dheader(
rti::xcdr::Stream& stream,
unsigned char *dheader_position)
{
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
return stream.finish_dheader((char *) dheader_position);
} else {
return true;
}
}
static bool move_after_dheader(
rti::xcdr::Stream& stream,
unsigned char *begin_position)
{
stream.current_position(begin_position);
if (collection_dheader_traits<IsForEnum>::dheader_required()) {
return stream.skip(RTI_XCDR_DHEADER_SIZE);
} else {
return true;
}
}
};
struct NoDHeaderGenerator {
static bool reserve_dheader(rti::xcdr::Stream&)
{
return true;
}
static bool finish_dheader(rti::xcdr::Stream&, unsigned char *)
{
return true;
}
static bool move_after_dheader(
rti::xcdr::Stream& stream,
unsigned char *begin_position)
{
stream.current_position(begin_position);
return true;
}
};
typedef DHeaderGeneratorImpl<false> DHeaderGenerator;
typedef DHeaderGeneratorImpl<true> DHeaderGeneratorForEnum;
// Maybe specialized for enums to select DHeaderGenerator
template <typename T, typename Enable = void>
struct primitive_sequence_dheader_gen {
typedef NoDHeaderGenerator type;
};
} // namespace detail
/*i
* Extends and specializes AbstractBuilder to add or override the
* functionality required to build a collection of consecutive elements
* (sequence or array):
*
* - Add or build elements
* - Keep track of the element count
* - Handle element alignment
*
*/
/**
* @ingroup RTIFlatBuildersModule
* @brief Base class of all array and sequence builders
*/
template <typename DHeaderGen>
class AbstractListBuilder : public AbstractBuilder {
protected:
AbstractListBuilder() : element_count_(0)
{ }
AbstractListBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment)
: AbstractBuilder(nested_tag_t(), parent, alignment),
element_count_(0)
{
DHeaderGen::reserve_dheader(stream());
}
template <typename ElementBuilder>
ElementBuilder build_next()
{
rti::xcdr::Stream::Memento stream_memento(stream());
return build_element<ElementBuilder>(stream_memento);
}
template <typename ElementOffset>
ElementOffset add_next()
{
RTI_FLAT_BUILDER_CHECK_VALID(return ElementOffset());
// save state in case of error
rti::xcdr::Stream::Memento stream_memento(stream());
if (!stream().align(ElementOffset::required_alignment)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return ElementOffset());
}
ElementOffset element = add_element<ElementOffset>();
if (element.is_null()) {
return ElementOffset();
}
// Success: clear memento, increment element count
stream_memento.discard();
element_count_++;
return element;
}
template <typename ElementOffset>
void add_n(unsigned int count)
{
RTI_FLAT_BUILDER_CHECK_VALID(return);
if (count == 0) {
return;
}
if (!stream().align(ElementOffset::required_alignment)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
}
offset_t elements_size = ElementOffset::serialized_size(0)
+ ElementOffset::serialized_size_w_padding() * (count - 1);
if (!stream().skip(elements_size)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return);
}
element_count_ += count;
}
/**
* @brief Returns the current number of elements that have been added
*/
unsigned int element_count() const
{
return element_count_;
}
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
AbstractListBuilder(AbstractListBuilder&& other) = default;
AbstractListBuilder& operator=(AbstractListBuilder&& other)
{
if (this == &other) {
return *this;
}
finish_untyped_impl();
AbstractBuilder::operator=(static_cast<AbstractBuilder&&>(other));
element_count_ = other.element_count_;
other.element_count_ = 0;
return *this;
}
#else
public:
// Enables the safe-move-constructor idiom without C++11 move constructors
struct AbstractListBuilderMoveProxy : AbstractBuilderMoveProxy {
unsigned int element_count_;
};
operator AbstractListBuilderMoveProxy () throw() // move-constructor idiom
{
AbstractListBuilderMoveProxy other;
move_to(other);
return other;
}
protected:
void move_from(AbstractListBuilderMoveProxy& other)
{
AbstractBuilder::move_from(other);
element_count_ = other.element_count_;
}
void move_to(AbstractListBuilderMoveProxy& other)
{
AbstractBuilder::move_to(other);
other.element_count_ = element_count_;
}
#endif
private:
friend class AbstractBuilder;
virtual void finish_member() // override
{
RTI_FLAT_BUILDER_CHECK_VALID(return);
// Increment element count after the nested element builder finishes,
// instead of on build_next(). This makes it easier to roll back the
// state if the element building doesn't complete.
element_count_++;
AbstractBuilder::finish_member();
}
protected:
bool finish_dheader()
{
return DHeaderGen::finish_dheader(stream(), begin_position());
}
unsigned int element_count_;
};
// --- Array: -----------------------------------------------------------------
//
// (Note: there is no FinalArrayBuilder, because a final array's size is fixed,
// and is handled directly returning its offset with add_*; on the other hand,
// sequences are never fixed-size.)
/**
* @ingroup RTIFlatBuildersModule
* @brief Builds an array member of variable-size elements
*
* @tparam ElementBuilder The Builder type for the elements of the array
* @tparam N The array bound; the exact number of elements this array builder
* must build. For multidimensional arrays, \p N is the product
* of each dimension bound.
*
* Each element of this array needs to be built using the ElementBuilder returned
* by build_next(). N elements exactly must be built. Unlike a sequence Builder,
* it is not possible to finish an array with less than N elements.
*
* This example shows how to use a MutableArrayBuilder to build an array member
* of MyFlatMutableBuilder:
*
* \code
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
* auto array_builder = builder.build_my_mutable_array();
* for (int i = 0; i < 10; i++) {
* FlatMutableBarBuilder element_builder = array_builder.build_next();
* // ... build element
* element_builder.finish();
* }
* array_builder.finish();
* \endcode
*
* Note that Builder types are not necessary for arrays of fixed-size elements,
* since they are added at once (see MyFlatMutableBuilder::add_my_final_array()).
*/
template <typename ElementBuilder, unsigned int N>
class MutableArrayBuilder : public AbstractListBuilder<detail::DHeaderGenerator> {
public:
/**
* @brief The related Offset type
*/
typedef MutableArrayOffset<typename ElementBuilder::Offset, N> Offset;
typedef AbstractListBuilder<detail::DHeaderGenerator> Base;
MutableArrayBuilder()
{ }
// The destructor does the same as finish_impl() but doesn't need to return
// an Offset
~MutableArrayBuilder()
{
finish_untyped_impl();
}
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
MutableArrayBuilder(MutableArrayBuilder&& other) = default;
MutableArrayBuilder& operator=(MutableArrayBuilder&& other)
{
finish_untyped_impl();
Base::operator=(static_cast<Base&&>(other));
return *this;
}
#endif
void finish_untyped_impl()
{
if (!Base::is_valid() || !Base::is_nested()) {
return;
}
Base::finish_dheader();
Base::finish_untyped_impl();
}
private:
friend class AbstractBuilder; // to allow access to the constructor
MutableArrayBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment = 0)
: AbstractListBuilder(nested_tag_t(), parent, alignment)
{
}
public:
/**
* @brief Begins building the next element
*
* Before calling build_next() to create a new element, the element Builder
* returned by a previous call to build_next must have been finished.
*/
ElementBuilder build_next()
{
if (element_count() == N) {
RTI_FLAT_BUILDER_PRECONDITION_ERROR(
"Array builder build_next: too many elements",
return ElementBuilder());
}
return AbstractListBuilder::build_next<ElementBuilder>();
}
/**
* @brief Finishes building the array
*
* @pre build_next() must have been succesfully called \p N times exactly.
* @return The Offset to the member that has been built.
* @see discard()
*/
Offset finish()
{
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return Offset());
if (element_count() != N) {
RTI_FLAT_BUILDER_PRECONDITION_ERROR(
"Cannot finish array builder: too few elements",
return Offset());
}
if (!finish_dheader()) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return Offset());
}
return finish_impl<Offset>();
}
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
MutableArrayBuilder, AbstractListBuilder, AbstractListBuilderMoveProxy)
};
// --- Sequences: -------------------------------------------------------------
// Specializes AbstractListBuilder to handle the length header serialization
// common to all sequences (which arrays don't have)
/**
* @ingroup RTIFlatBuildersModule
* @brief Base class of Builders for sequence members.
*
* This class contains only implementation details and doesn't add any public
* function to AbstractListBuilder.
*/
template <typename DHeaderGen>
class AbstractSequenceBuilder : public AbstractListBuilder<DHeaderGen> {
public:
typedef AbstractListBuilder<DHeaderGen> Base;
using Base::element_count;
using Base::is_nested;
using Base::is_valid;
using Base::stream;
using typename Base::nested_tag_t;
#if !defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
using typename Base::AbstractListBuilderMoveProxy;
#endif
protected:
AbstractSequenceBuilder()
{
}
AbstractSequenceBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment)
: Base(nested_tag_t(), parent, alignment)
{
// leave space for sequence length
if (!stream().check_size(sizeof(rti::xcdr::length_t))) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(this->invalidate());
} else {
// No need to align; the base constructor already aligned to 4 the
// begin_position
stream().template serialize_fast<rti::xcdr::length_t>(0);
}
}
// The destructor does the same as finish_impl() but doesn't need to return
// an Offset
~AbstractSequenceBuilder()
{
finish_untyped_impl();
}
#if defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
AbstractSequenceBuilder(AbstractSequenceBuilder&& other) = default;
AbstractSequenceBuilder& operator=(AbstractSequenceBuilder&& other)
{
finish_untyped_impl();
Base::operator=(static_cast<Base&&>(other));
return *this;
}
#endif
void finish_untyped_impl()
{
if (!Base::is_valid() || !Base::is_nested()) {
return;
}
Base::finish_dheader();
finish_length();
Base::finish_untyped_impl();
}
// Concrete sequences must call this method in their own finish() function
template <typename Offset>
Offset finish_impl()
{
RTI_FLAT_BUILDER_CHECK_VALID(return Offset());
RTI_FLAT_BUILDER_CHECK_CAN_FINISH(return Offset());
Base::finish_dheader();
finish_length(); // serialize the length header
return Base::template finish_impl<Offset>();
}
private:
// Serializes the sequence length when finishing the builder.
void finish_length()
{
rti::xcdr::Stream::Memento stream_memento(stream());
DHeaderGen::move_after_dheader(stream(), Base::begin_position());
// Coverity found examples where element_count() is checked, but
// that's only done in Array builders, which are unrelated to this
// class, AbstractSequenceBuilder.
// coverity[CHECKED_RETURN : FALSE]
stream().template serialize_fast<rti::xcdr::length_t>(element_count());
}
};
/**
* @ingroup RTIFlatBuildersModule
* @brief Builds a sequence member of variable-size elements
*
* @tparam ElementBuilder The Builder type for the elements of the sequence
*
* To build the elements use the ElementBuilder returned
* by build_next(). An empty sequence can be built by calling finish() without
* any call to build_next().
*
* This class doesn't enforce the sequence bound set in IDL.
*
* The following example uses a MutableSequenceBuilder to initialize a sequence
* member of MyFlatMutableBuilder with two elements:
*
* \code
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
* auto seq_builder = builder.build_my_mutable_seq();
* auto element_builder = seq_builder.build_next();
* // ... build the first element
* element_builder.finish();
* element_builder = seq_builder.build_next();
* // ... build the second element
* element_builder.finish();
*
* seq_builder.finish();
* \endcode
*/
template <typename ElementBuilder>
class MutableSequenceBuilder
: public AbstractSequenceBuilder<detail::DHeaderGenerator> {
public:
/**
* @brief The related Offset type
*/
typedef SequenceOffset<typename ElementBuilder::Offset> Offset;
MutableSequenceBuilder()
{
}
private:
typedef AbstractSequenceBuilder<detail::DHeaderGenerator> Base;
friend class AbstractBuilder; // to allow access to the constructor
MutableSequenceBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment = RTI_XCDR_DHEADER_ALIGNMENT)
: Base(nested_tag_t(), parent, alignment)
{
}
public:
/**
* @brief Begins building the next element
*
* Before calling build_next() to create a new element, the application must
* have called finish() on the previous element Builder.
*/
ElementBuilder build_next()
{
return AbstractListBuilder::build_next<ElementBuilder>();
}
/**
* @brief Finishes building the sequence
*
* @return An Offset to the member that has been built.
*
* @see discard()
*/
Offset finish()
{
return finish_impl<Offset>();
}
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(
MutableSequenceBuilder,
Base,
AbstractListBuilderMoveProxy)
};
/**
* @ingroup RTIFlatBuildersModule
* @brief Builds a sequence member of fixed-size elements
*
* @tparam ElementOffset The Offset type for the elements of the sequence
*
* To add an element, call add_next() and use the ElementOffset it returns to
* initialize the element's values. An empty sequence can be built by
* calling finish() without any call to add_next().
*
* This class doesn't enforce the sequence bound set in IDL.
*
* The following example uses a FinalSequenceBuilder to initialize a sequence
* member of MyFlatMutableBuilder with two elements:
*
* \code
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
* auto seq_builder = builder.build_my_final_seq();
* MyFlatFinalOffset element = seq_builder.add_next();
* element.my_primitive(1);
* // ... continue initializing the first element
*
* element = seq_builder.add_next();
* element.my_primitive(2);
* // ... continue initializing the second element
*
* seq_builder.finish();
* \endcode
*
* If the element type meets certain requirements, rti::flat::plain_cast()
* provides a more efficient way to initialize a sequence of final elements.
*/
template <typename ElementOffset>
class FinalSequenceBuilder
: public AbstractSequenceBuilder<detail::DHeaderGenerator> {
public:
typedef SequenceOffset<ElementOffset> Offset;
FinalSequenceBuilder()
{
}
private:
friend class AbstractBuilder; // to allow access to the constructor
FinalSequenceBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
: AbstractSequenceBuilder(nested_tag_t(), parent, alignment)
{
}
public:
/**
* @brief Adds the next element
*
* @return The Offset that can be used to set the element values
*/
ElementOffset add_next()
{
return AbstractListBuilder::add_next<ElementOffset>();
}
/**
* @brief Adds a number of elements at once
*
* This is an alternative to add_next().
*
* To initialize the elements, call finish() and use the Offset it returns
* to access the elements.
*/
FinalSequenceBuilder& add_n(unsigned int count)
{
AbstractListBuilder::add_n<ElementOffset>(count);
return *this;
}
/**
* @brief Finishes building the sequence
*
* @return An Offset to the member that has been built.
* @see discard()
*/
Offset finish()
{
return finish_impl<Offset>();
}
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
FinalSequenceBuilder, AbstractSequenceBuilder, AbstractListBuilderMoveProxy)
};
/**
* @ingroup RTIFlatBuildersModule
* @brief Builds a sequence of primitive members
*
* The elements can be added one by one with add_next() or all at once with
* add_n().
*/
template <typename T>
class PrimitiveSequenceBuilder
: public AbstractSequenceBuilder<
typename detail::primitive_sequence_dheader_gen<T>::type> {
public:
typedef PrimitiveSequenceOffset<T> Offset;
typedef AbstractSequenceBuilder<
typename detail::primitive_sequence_dheader_gen<T>::type>
Base;
using Base::is_valid;
using Base::stream;
using typename Base::nested_tag_t;
#if !defined(RTI_FLAT_DATA_CXX11_RVALUE_REFERENCES)
using typename Base::AbstractListBuilderMoveProxy;
#endif
PrimitiveSequenceBuilder()
{
}
protected:
friend class AbstractBuilder; // to allow access to the constructor
PrimitiveSequenceBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
: Base(nested_tag_t(), parent, alignment)
{
}
public:
/**
* @brief Adds the next element
*
* @param value The primitive element to add
*/
PrimitiveSequenceBuilder& add_next(T value)
{
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
if (!stream().template serialize<T>(value)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
Base::element_count_++;
return *this;
}
/**
* @brief Adds all the elements in an array
*
* @param array The array containing the values to add
* @param count The size of the array
*
* @pre array must not be NULL
*/
PrimitiveSequenceBuilder& add_n(const T *array, unsigned int count)
{
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
RTI_FLAT_ASSERT(array != NULL, return *this);
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
< count) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
if (!stream().check_size(static_cast<unsigned int>(sizeof(T)) * count)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
if (stream().needs_byte_swap() && sizeof(T) > 1) {
for (unsigned int i = 0; i < count; i++) {
stream().template serialize_fast<T>(array[i]);
}
} else {
stream().serialize_fast((void *) array, count * static_cast<unsigned int>(sizeof(T)));
}
Base::element_count_ += count;
return *this;
}
/**
* @brief Adds a number of elements with the same value
*
* This overload initializes all the elements to a value.
*
* @note This operation is O(\p count). The other overloads,
* add_n(unsigned int) and add_n(const T*, unsigned int), are more
* efficient
*
*
* @param count The number of elements to add
* @param value The value to set for each element
*/
PrimitiveSequenceBuilder& add_n(unsigned int count, T value)
{
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
< count) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
if (!stream().check_size(static_cast<unsigned int>(sizeof(T)) * count)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
for (unsigned int i = 0; i < count; i++) {
stream().template serialize_fast<T>(value);
}
Base::element_count_ += count;
return *this;
}
/**
* @brief Adds a number of uninitialized elements
*
* This operation is O(1), since it leaves the elements uninitialized.
* They can be initialized using the Offset returned by finish() and
* rti::flat::plain_cast(). For example:
*
* \code
* MyFlatMutableBuilder builder = ...;
* auto seq_builder = builder.build_my_primitive_seq();
* seq_builder.add_n(1000);
* auto seq_offset = seq_builder.finish();
* int32_t *elements = rti::flat::plain_cast(seq_offset);
* for (int i = 0; i < 1000; i++) {
* elements[i] = ...;
* }
* \endcode
*
* @param count The number of elements to add
*/
PrimitiveSequenceBuilder& add_n(unsigned int count)
{
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
if (RTIXCdrUnsignedLong_MAX / static_cast<unsigned int>(sizeof(T))
< count) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
if (!stream().skip(static_cast<unsigned int>(sizeof(T)) * count)) {
RTI_FLAT_BUILDER_OUT_OF_RESOURCES_ERROR(return *this);
}
Base::element_count_ += count;
return *this;
}
/**
* @brief Finishes building the sequence
*
* @return An Offset to the member that has been built.
* @see discard()
*/
Offset finish()
{
this->finish_dheader();
return Base::template finish_impl<Offset>();
}
private:
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
PrimitiveSequenceBuilder, Base, AbstractListBuilderMoveProxy)
};
/**
* @ingroup RTIFlatBuildersModule
* @brief Builds a string
*
* A StringBuilder only provides one method, set_string(), so it can be
* typically used as follows:
*
* \code
* MyFlatMutableBuilder my_builder = ...;
* my_builder.build_my_string().set_string("Hello!");
* \endcode
*
* Note that by relying on the builder destructor, there is no need to call
* finish() on the object returned by build_my_string().
*/
class StringBuilder : public PrimitiveSequenceBuilder<char> {
public:
typedef StringOffset Offset;
typedef PrimitiveSequenceBuilder<char> Base;
using Base::is_valid;
using Base::stream;
using typename Base::nested_tag_t;
StringBuilder()
{
}
private:
friend class AbstractBuilder; // to allow access to the constructor
StringBuilder(
nested_tag_t,
AbstractBuilder& parent,
unsigned int alignment = RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
: Base(nested_tag_t(), parent, alignment)
{
}
public:
/**
* @brief Sets the string value
*
* @param value The string value to set.
*
* @pre value must not be NULL.
*/
StringBuilder& set_string(const char *value)
{
RTI_FLAT_BUILDER_CHECK_VALID(return *this);
RTI_FLAT_ASSERT(value != NULL, return *this);
// if set_string is called more than once we override the string
// that was set before
if (Base::element_count_ != 0) {
stream().current_position(begin_position());
stream().serialize_fast<rti::xcdr::length_t>(0);
Base::element_count_ = 0;
}
unsigned int length =
static_cast<unsigned int>(RTIXCdrString_getLength(value)) + 1;
add_n(value, length); // if this fail, error has been reported
return *this;
}
/**
* @brief Finishes building the string
*
* @return An Offset to the member that has been built.
* @see discard()
*/
Offset finish()
{
RTI_FLAT_BUILDER_CHECK_VALID(return Offset());
if (element_count_ == 0) {
add_next('\0'); // build empty string if no string was built
}
Base::finish_dheader();
return finish_impl<Offset>();
}
RTI_FLAT_BUILDER_DEFINE_MOVE_OPERATIONS_IMPL(\
StringBuilder, Base, AbstractListBuilderMoveProxy)
};
// Wide strings are treated as a sequence of octets
typedef PrimitiveSequenceBuilder<unsigned char> WStringBuilder;
} }
#endif // RTI_DDS_FLAT_SEQUENCEBUILDERS_HPP_
@@ -0,0 +1,345 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
#define RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
#include <iterator>
#include "rti/flat/Offset.hpp"
namespace rti { namespace flat {
/**
* @ingroup RTIFlatOffsetsModule
* @brief Iterator for collections of Offsets
*
* @tparam E The Offset type of the elements
* @tparam OffsetKind (implementation detail)
*
* @note This type should not be declared directly. For example, for a
* SequenceOffset, use SequenceOffset::iterator (or simply \p auto).
*
* A SequenceIterator moves through the Offset to elements of a sequence or
* array \ref RTIFlatOffsetsModule "Offset", and provides the functionality of
* a standard \p forward_iterator.
*
* Note that a dereferenced SequenceIterator returns <b>by value</b> the Offset
* to the current element; it doesn't return it by reference.
*
* \if CPP_LANGUAGE_ONLY
* Since the Traditional C++ API doesn't use exceptions, it is recommended
* to use the function advance() instead of operator++ to check for errors
* \endif
*
*/
template <typename E, typename OffsetKind>
class SequenceIterator {
public:
/**
* @brief The iterator category
*/
typedef std::forward_iterator_tag iterator_category;
/**
* @brief The element type
*/
typedef E value_type;
/**
* @brief The reference type is the same as the value type, an Offset
*/
typedef value_type reference;
/**
* @brief The pointer type is the same as the value type, an Offset
*/
typedef value_type pointer;
/**
* @brief The difference type
*/
typedef std::ptrdiff_t difference_type;
/**
* @brief Constructs an invalid iterator
*/
SequenceIterator() : sample_(NULL), current_offset_(0), max_offset_(0)
{
}
SequenceIterator(
rti::flat::SampleBase *sample,
offset_t initial_offset,
offset_t max_offset)
: sample_(sample),
current_offset_(initial_offset),
max_offset_(max_offset)
{
RTI_FLAT_ASSERT(sample != NULL, return);
}
/**
* @brief Returns whether the iterator is invalid
*/
bool is_null() const
{
return sample_ == NULL;
}
/**
* @brief Returns the Offset of the current element
*
* @return The Offset to the current element. Note that this function returns
* by value, not by reference. This Offset may be
* \ref rti::flat::OffsetBase::is_null "null" if the iterator has surpassed
* the length of the collection. See \ref OffsetErrorManagement.
*/
value_type operator*() const
{
return get_impl(OffsetKind());
}
/**
* @brief Returns the Offset of the current element
*/
value_type operator->() const
{
return get_impl(OffsetKind());
}
/**
* @brief Advances to the next element, reporting any errors by returning false
*
* \if CPP_LANGUAGE_ONLY
* Since the Traditional C++ API doesn't support exceptions, this function
* is recommended instead of operator++.
* \endif
* \if CPP2_LANGUAGE_ONLY
* Unlike operator++, which throws an exception in case of error, advance()
* returns false
* \endif
*
* @return True if the function succeeds, or false if there was an error
*/
bool advance()
{
// Unlike operator++, advance() allows checking for errors when
// exceptions are disabled (traditional C++)
return advance_impl(OffsetKind());
}
/**
* @brief Advances to the next element
*
* \if CPP_LANGUAGE_ONLY
* @warning Since the Traditional C++ API doesn't use exceptions, use
* advance() instead of this operator to check for errors.
* \endif
*/
SequenceIterator& operator++()
{
bool can_advance_iter = advance_impl(OffsetKind());
RTI_FLAT_CHECK_PRECONDITION(can_advance_iter, return *this);
return *this;
}
/**
* @brief Advances to the next element
*
* \if CPP_LANGUAGE_ONLY
* @warning Since the Traditional C++ API doesn't use exceptions, use
* advance() instead of this operator to check for errors.
* \endif
*/
SequenceIterator operator++(int)
{
SequenceIterator tmp(*this);
++(*this);
return tmp;
}
/**
* @brief Compares two iterators
*/
friend bool operator<(
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return s1.get_position() < s2.get_position();
}
/**
* @brief Compares two iterators
*/
friend bool operator > (
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return s1.get_position() > s2.get_position();
}
/**
* @brief Compares two iterators
*/
friend bool operator <= (
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return s1.get_position() <= s2.get_position();
}
/**
* @brief Compares two iterators
*/
friend bool operator >= (
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return s1.get_position() >= s2.get_position();
}
/**
* @brief Compares two iterators
*/
friend bool operator == (
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return s1.get_position() == s2.get_position();
}
/**
* @brief Compares two iterators
*/
friend bool operator != (
const SequenceIterator & s1,
const SequenceIterator & s2)
{
return !(s1 == s2);
}
unsigned char * get_position() const
{
if (is_null()) {
return NULL;
} else {
return sample_->get_buffer() + current_offset_;
}
}
private:
E get_impl(variable_size_type_tag_t) const
{
if (is_null()) {
return E();
}
// The iterator points to the end of the previous element. If elements
// need padding, we have to add it here (required alignment is 4 because
// mutable types (variable_size_type_tag_t) are always aligned to 4)
offset_t offset = RTIXCdrAlignment_alignSizeUp(current_offset_, 4);
if (offset >= max_offset_) {
return E();
}
// Calculate the size of the current element; if E is a struct type,
// this will be quick (just deserialize the DHeader); if we're itearating
// over an array of sequences (E is a sequence), this will require
// skipping over the current sequence.
offset_t size = E::Helper::calculate_serialized_size(
sample_,
offset,
max_offset_);
// size 0 indicates an error; a variable-size type E cannot have size
// zero because it will always have a header (DHeader for mutable structs,
// length for sequences)
if (size == 0) {
return E();
}
return E(sample_, offset, size);
}
E get_impl(fixed_size_type_tag_t) const
{
if (is_null()) {
return E();
}
// The iterator points to the end of the previous element. If elements
// need padding, we have to add it here.
offset_t offset = RTIXCdrAlignment_alignSizeUp(
current_offset_,
E::required_alignment);
if (offset >= max_offset_) {
return E();
}
return E(sample_, offset);
}
bool advance_impl(variable_size_type_tag_t)
{
if (is_null()) {
return false;
}
current_offset_ = RTIXCdrAlignment_alignSizeUp(current_offset_, 4);
offset_t size = E::Helper::calculate_serialized_size(
sample_, // beginning of the buffer
current_offset_, // absolute offset to the current element
max_offset_); // maximum offset in the list
if (size == 0) {
return false;
}
current_offset_ += size;
if (current_offset_ > max_offset_) {
current_offset_ = max_offset_;
return false;
}
return true;
}
bool advance_impl(fixed_size_type_tag_t)
{
if (is_null()) {
return false;
}
// The iterator points to the end of the previous element. If elements
// need padding, we have to add it here. We do that to keep the end()
// iterator consistent, becuase the last element may not need padding.
current_offset_ = RTIXCdrAlignment_alignSizeUp(
current_offset_,
E::required_alignment);
current_offset_ += E::serialized_size(0);
if (current_offset_ > max_offset_) {
current_offset_ = max_offset_;
return false;
}
return true;
}
rti::flat::SampleBase *sample_;
offset_t current_offset_;
offset_t max_offset_;
};
} }
#endif // RTI_DDS_FLAT_SEQUENCEITERATOR_HPP_
@@ -0,0 +1,844 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
#define RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
#include "rti/flat/Offset.hpp"
#include "rti/flat/SequenceIterator.hpp"
namespace rti { namespace flat {
/**
* @ingroup RTIFlatOffsetsModule
* @brief Base class for Offsets to sequences and arrays of primitive types
*
* @tparam T The primitive type
*
*/
template <typename T>
class AbstractPrimitiveList : public OffsetBase {
public:
typedef T value_type;
protected:
AbstractPrimitiveList()
{
}
AbstractPrimitiveList(
rti::flat::SampleBase *sample,
offset_t offset,
offset_t sequence_size)
: OffsetBase(sample, offset, sequence_size)
{
}
public:
bool is_cpp_compatible() const
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return false);
return sizeof(T) == 1 || !stream_.needs_byte_swap();
}
/*@private
* @brief Gets direct access to the elements
*
* Returns a pointer to the elements of this sequence.
*
* @note If the Sample that contains this PrimitiveSequenceOffset has an endianess
* different from the native one, the element bytes may be swapped. This can
* happen if this Sample was published by a DataWriter running on a platform
* with a different endianess and received by a DataReader running locally.
*
* This function is not for public use. See rti::flat::plain_cast() instead.
*/
T * get_elements()
{
return reinterpret_cast<T*>(this->get_buffer());
}
/*@private
* @brief Gets direct access to the array elements
*
* (Const overload)
*/
const T * get_elements() const
{
return reinterpret_cast<const T*>(this->get_buffer());
}
/**
* @brief Returns an element by index
*
* @param i The zero-based index of the element
*
* @see rti::flat::plain_cast() for a method to access all the elements at
* once
*/
T get_element(unsigned int i) const
{
// OffsetBase::serialize only checks this as a debug assertion; in
// a sequence or array we need to do it in release mode too
if (!stream_.check_size((i + 1) * ((unsigned int) sizeof(T)))) {
return T();
}
return OffsetBase::template deserialize<T>(i * (unsigned int) sizeof(T));
}
/**
* @brief Sets an element by index
*
* @param i The zero-based index of the element to set
* @param value The value to set
*
* @return true if it was possible to set the element, or false if this
* collection has less than i - 1 elements.
*
* @see rti::flat::plain_cast() for a method to access all the elements at
* once
*/
bool set_element(unsigned int i, T value)
{
// OffsetBase::serialize only checks this as a debug assertion; in
// a sequence or array we need to do it in release mode too
if (!stream_.check_size((i + 1) * static_cast<unsigned int>(sizeof(T)))) {
return false;
}
return OffsetBase::serialize(i * static_cast<unsigned int>(sizeof(T)), value);
}
};
namespace detail {
template <typename T>
struct PrimitiveSequenceOffsetHelper;
template <typename T, typename Enable = void>
struct primitive_sequence_header_length {
static size_t value()
{
return sizeof(rti::xcdr::length_t);
}
};
} // namespace detail
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to a sequence of primitive elements
*
* @tparam T The primitive type
*
*/
template <typename T>
class PrimitiveSequenceOffset : public AbstractPrimitiveList<T> {
public:
typedef variable_size_type_tag_t offset_kind;
typedef detail::PrimitiveSequenceOffsetHelper<T> Helper;
PrimitiveSequenceOffset() : element_count_(0)
{
}
PrimitiveSequenceOffset(
rti::flat::SampleBase *sample,
offset_t offset,
offset_t serialized_size)
: AbstractPrimitiveList<T>(
// passing NULL causes the base constructors to fail
serialized_size >= static_cast<offset_t>(header_length()) ? sample : NULL,
offset + static_cast<offset_t>(header_length()),
serialized_size - static_cast<offset_t>(header_length())),
element_count_(0)
{
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
if (!this->is_null()) { // without exceptions, we need to check if the base ctor failed
#endif
rti::xcdr::Stream::Memento stream_memento(this->stream_);
this->stream_.skip_back(sizeof(rti::xcdr::length_t));
element_count_ = this->stream_.template deserialize_fast<rti::xcdr::length_t>();
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
}
#endif
}
/**
* @brief Returns the number of elements
*/
unsigned int element_count() const
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return 0);
return element_count_;
}
private:
static size_t header_length()
{
return detail::primitive_sequence_header_length<T>::value();
}
unsigned int element_count_;
};
namespace detail {
template <typename T>
struct PrimitiveSequenceOffsetHelper {
static offset_t calculate_serialized_size(
rti::flat::SampleBase *sample,
offset_t absolute_offset,
offset_t max_size)
{
RTI_FLAT_ASSERT(sample != NULL, return 0);
RTI_FLAT_ASSERT(max_size > sizeof(rti::xcdr::length_t), return 0);
PrimitiveSequenceOffset<T> tmp(sample, absolute_offset, max_size);
// elements + header
if ((RTIXCdrUnsignedLong_MAX
- static_cast<unsigned int>(sizeof(rti::xcdr::length_t)))
/ static_cast<unsigned int>(sizeof(T))
< tmp.element_count()) {
RTI_FLAT_OFFSET_PRECONDITION_ERROR(
"Serialized size of a sequence of primitive elements "
"exceeds maximum representable 32-bit unsigned integer.",
return 0);
}
return (tmp.element_count() * static_cast<unsigned int>(sizeof(T)))
+ static_cast<unsigned int>(sizeof(rti::xcdr::length_t));
}
};
} // namespace detail
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to an array of primitive elements
*
* @tparam T The primitive type
* @tparam N The array bound. For multidimensional arrays, \p N is the product
* of each dimension bound.
*
* A PrimitiveArrayOffset may meet the requirements to be cast to an array of
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
*/
template <typename T, unsigned int N>
class PrimitiveArrayOffset : public AbstractPrimitiveList<T> {
public:
typedef fixed_size_type_tag_t offset_kind;
PrimitiveArrayOffset()
{
}
PrimitiveArrayOffset(rti::flat::SampleBase *sample, offset_t offset)
: AbstractPrimitiveList<T>(
sample,
offset + static_cast<offset_t>(header_length()),
serialized_size(0))
{
}
public:
/**
* @brief Returns the number of elements, \p N
*
*/
unsigned int element_count() const
{
return N;
}
static offset_t serialized_size(offset_t)
{
return sizeof(T) * N;
}
private:
static size_t header_length()
{
return detail::primitive_sequence_header_length<T>::value()
- sizeof(rti::xcdr::length_t);
}
};
// Note: the modern C++ API defines a PrimitiveArrayOffset specializations for
// safe_enum and wchar_t
struct StringOffsetHelper;
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to a string
*
*/
class StringOffset : public PrimitiveSequenceOffset<char> {
public:
typedef variable_size_type_tag_t offset_kind;
typedef StringOffsetHelper Helper;
StringOffset()
{
}
StringOffset(
SampleBase *sample,
offset_t absolute_offset,
offset_t serialized_size)
: PrimitiveSequenceOffset<char>(
sample,
absolute_offset,
serialized_size)
{
}
/**
* @brief Gets the string
*
* The string returned can be modified as long as its size doesn't change.
*/
char * get_string()
{
return reinterpret_cast<char*>(this->get_buffer());
}
/**
* @brief Gets the string (const)
*
*/
const char * get_string() const
{
return reinterpret_cast<const char*>(this->get_buffer());
}
/**
* @brief Returns the number of characters
*
* This number doesn't include the null terminating character.
*/
unsigned int element_count() const
{
RTI_FLAT_CHECK_PRECONDITION(
PrimitiveSequenceOffset<char>::element_count() > 0,
return 0);
return PrimitiveSequenceOffset<char>::element_count() - 1;
}
};
struct StringOffsetHelper {
static offset_t calculate_serialized_size(
rti::flat::SampleBase *sample,
offset_t absolute_offset,
offset_t max_size)
{
StringOffset tmp(sample, absolute_offset, max_size);
return tmp.element_count()
+ 1 // null terminator
+ (unsigned int) sizeof(rti::xcdr::length_t); // length header
}
};
// Wide strings are treated as a sequence of octets
typedef PrimitiveSequenceOffset<unsigned char> WStringOffset;
// The base class of SequenceOffset, and FinalAlignedArrayOffset
//
// This encapsulates the functionality that allows iterating through a list
// of elements (a sequence or an array). The list must be always aligned to 4
// (that excludes FinalArrayOffset)
//
/**
* @ingroup RTIFlatOffsetsModule
* @brief Base class of Offsets to sequences and arrays of non-primitive members
*
* @tparam ElementOffset The Offset type of the elements
*/
template <typename ElementOffset>
class AbstractAlignedList : public OffsetBase {
public:
/**
* @brief The iterator type, SequenceIterator
*/
typedef SequenceIterator<ElementOffset, typename ElementOffset::offset_kind>
iterator;
protected:
AbstractAlignedList()
{
}
AbstractAlignedList(
rti::flat::SampleBase *sample,
offset_t offset,
offset_t sequence_size)
: OffsetBase(
sample,
offset,
sequence_size)
{
}
public:
bool is_cpp_compatible() const // override
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return false);
return !stream_.needs_byte_swap()
&& rti::xcdr::has_cpp_friendly_cdr_layout_collection<
typename rti::flat::flat_type_traits<ElementOffset>::flat_type>();
}
ElementOffset get_element(unsigned int i)
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
return get_element_impl<ElementOffset>(
i,
typename ElementOffset::offset_kind());
}
/**
* @brief Gets an iterator to the first Offset
*
* begin() and end() enable the use of range-for loops, for example:
* \code
* SequenceOffset<MyFlatMutableOffset> sequence_offset = my_type_offset.my_sequence();
* for (auto element : sequence_offset) {
* std::cout << element.x() << std::endl;
* }
* \endcode
*
*/
iterator begin()
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return iterator(NULL, 0, 0));
return iterator(
sample_,
absolute_offset_,
absolute_offset_ + get_buffer_size());
}
/**
* @brief Gets an iterator to the past-the-end element
*/
iterator end()
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return iterator(NULL, 0, 0));
return iterator(
sample_,
absolute_offset_ + get_buffer_size(),
absolute_offset_ + get_buffer_size());
}
private:
// for variable size types we skip element by element
template <typename E>
E get_element_impl(unsigned int i, variable_size_type_tag_t)
{
iterator it = begin();
while (it != end() && i > 0) {
if (!it.advance()) {
return E();
}
i--;
}
return *it;
}
// for fixed-size types we support random access
template <typename E>
E get_element_impl(unsigned int i, fixed_size_type_tag_t)
{
offset_t size = i * E::serialized_size_w_padding();
// Ensure that the stream has space for i + 1 elements
if (!stream_.check_size(size + E::serialized_size(0))) {
return E();
}
return E(this->sample_, this->absolute_offset_ + size);
}
};
template <typename ElementOffset>
struct SequenceOffsetHelper;
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to a sequence of non-primitive elements
*
* @tparam ElementOffset The Offset type, for example MyFlatMutableOffset,
* MyFlatFinalOffset, or StringOffset.
*
* Represents an Offset to a sequence member and allows getting an Offset to
* each of its elements.
*
* A SequenceOffset may meet the requirements to be cast to an array of
* the equivalent plain C++ element type (see rti::flat::plain_cast), if the
* \p ElementOffset is a final type.
*/
template <typename ElementOffset>
class SequenceOffset : public AbstractAlignedList<ElementOffset> {
public:
typedef variable_size_type_tag_t offset_kind;
typedef SequenceOffsetHelper<ElementOffset> Helper;
SequenceOffset() : element_count_(0)
{
}
SequenceOffset(
rti::flat::SampleBase *sample,
offset_t offset,
offset_t sequence_size)
: AbstractAlignedList<ElementOffset>(
// Member size in bytes must be > 4, otherwise let base ctor
// fail
sequence_size >= header_length() ? sample : NULL,
// The offset begins after any padding to align to 4;
// padding may be needed for example for an array of
// sequence of final types; the end position of the previous
// sequence may not be aligned
RTIXCdrAlignment_alignSizeUp(
offset,
RTI_XCDR_SEQ_LENGTH_ALIGNMENT)
+ header_length(),
sequence_size - header_length()),
element_count_(0)
{
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
if (!this->is_null()) { // without exceptions, we need to check if the base ctor failed
#endif
rti::xcdr::Stream::Memento stream_memento(this->stream_);
this->stream_.skip_back(sizeof(rti::xcdr::length_t));
element_count_ = this->stream_.template deserialize_fast<rti::xcdr::length_t>();
#ifdef RTI_FLAT_DATA_NO_EXCEPTIONS
}
#endif
}
/**
* @brief Gets the Offset to an element
*
* @param i The zero-based index to the element
* @return The Offset to the element in the i-th position
*/
ElementOffset get_element(unsigned int i)
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
if (i >= element_count_) {
return ElementOffset();
}
return AbstractAlignedList<ElementOffset>::get_element(i);
}
/**
* @brief The number of elements
*/
unsigned int element_count() const
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return 0);
return element_count_;
}
private:
static offset_t header_length()
{
return static_cast<offset_t>(
detail::collection_dheader_traits<false>::dheader_size()
+ sizeof(rti::xcdr::length_t));
}
unsigned int element_count_;
};
template <typename E>
struct SequenceOffsetHelper {
// Calculates the serialized size of a Sequence by skipping each element
static offset_t calculate_serialized_size(
rti::flat::SampleBase *sample,
offset_t absolute_offset,
offset_t max_offset)
{
// Create a SequenceOffset beginning at sample + absolute_offset,
// with a size we don't know, but no greater than
// max_offset - absolute_offset
SequenceOffset<E> tmp(
sample,
absolute_offset,
max_offset - absolute_offset);
unsigned int count = tmp.element_count();
typename SequenceOffset<E>::iterator it = tmp.begin();
for (unsigned int i = 0; i < count; i++) {
if (!it.advance()) {
return 0; // error
}
}
return detail::ptrdiff(it.get_position(), sample->get_buffer())
- absolute_offset;
}
};
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to an array of variable-size elements
*
* @tparam ElementOffset An Offset for a type of variable size, such as a
* mutable struct (MyFlatMutableOffset), union, or StringOffset.
* @tparam N The array bound. For multidimensional arrays, \p N is the product
* of each dimension bound.
*
* Represents an Offset to an array member and allows getting an Offset to
* each of its elements.
*
* @see FinalArrayOffset encapsulates arrays of fixed-size elements
*/
template <typename ElementOffset, unsigned int N>
class MutableArrayOffset : public AbstractAlignedList<ElementOffset> {
public:
typedef variable_size_type_tag_t offset_kind;
MutableArrayOffset()
{
}
MutableArrayOffset(
rti::flat::SampleBase *sample,
offset_t offset,
offset_t sequence_size)
: AbstractAlignedList<ElementOffset>(
sequence_size >= header_length() ? sample : NULL,
offset + header_length(),
sequence_size - header_length())
{
}
/**
* @brief Gets the Offset to an element
*
* @param i The zero-based index to the element
* @return The Offset to the element in the i-th position
*/
ElementOffset get_element(unsigned int i)
{
if (i >= N) {
return ElementOffset();
}
return AbstractAlignedList<ElementOffset>::get_element(i);
}
private:
static offset_t header_length()
{
return static_cast<offset_t>(
detail::collection_dheader_traits<false>::dheader_size());
}
};
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to an array of final elements
*
* @tparam ElementOffset A final struct Offset, such as MyFlatFinalOffset.
* @tparam N The array bound. For multidimensional arrays, \p N is the product
* of each dimension bound.
*
* Represents an Offset to an array member and allows getting an Offset to
* each of its elements.
*
* FinalArrayOffset and FinalAlignedArrayOffset provide the same
* interface, but have different implementation details. FinalArrayOffset is
* used when the array member is part of a final type too, whereas
* FinalAlignedArrayOffset corresponds to an array inside a mutable type.
*
* A FinalArrayOffset may meet the requirements to be cast to an array of
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
*/
template <typename ElementOffset, unsigned int N>
class FinalArrayOffset : public OffsetBase {
public:
typedef fixed_size_type_tag_t offset_kind;
FinalArrayOffset()
{
}
// Constructor used when this array is part of a fixed-size type, and
// therefore it's initial alignment is not known ahead of time
FinalArrayOffset(
rti::flat::SampleBase *sample,
offset_t absolute_offset,
offset_t first_element_size,
offset_t element_size)
: OffsetBase(
sample,
absolute_offset,
first_element_size + element_size * (N - 1)),
first_element_size_(first_element_size),
element_size_(element_size)
{
}
/**
* @brief Gets the Offset to an element
*
* @param i The zero-based index to the element
* @return The Offset to the element in the i-th position
*/
ElementOffset get_element(unsigned int i)
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
if (i >= N) {
return ElementOffset(); // null offset
}
offset_t element_offset = this->absolute_offset_;
if (i > 0) {
element_offset += this->first_element_size_
+ (i - 1) * this->element_size_;
}
return ElementOffset(this->sample_, element_offset);
}
private:
offset_t first_element_size_;
offset_t element_size_;
};
/**
* @ingroup RTIFlatOffsetsModule
* @brief Offset to an array of final elements
*
* @tparam ElementOffset A final struct Offset, such as MyFlatFinalOffset.
* @tparam N The array bound. For multidimensional arrays, \p N is the product
* of each dimension bound.
*
* Represents an Offset to an array member and allows getting an Offset to
* each of its elements.
*
* The following code shows how to use a FinalAlignedArrayOffset to initialize
* an array member with MyFlatMutableBuilder:
*
* \code
* MyFlatMutableBuilder builder = rti::flat::build_data(writer);
* auto array_offset = builder.add_my_final_array();
* for (MyFlatFinalOffset element_offset : array_offset) {
* element_offset.my_primitive(3);
* // ...
* }
*
* // Or access an element directly:
* auto element_offset = array_offset.get_element(3);
* element_offset.my_primitive(3);
* \endcode
*
* A more efficient way to access a final array, provided it complies with
* the required preconditions, is through rti::flat::plain_cast().
*
* FinalArrayOffset and FinalAlignedArrayOffset provide the same
* interface, but have different implementation details. FinalArrayOffset is
* used when the array member is part of a final type too, whereas
* FinalAlignedArrayOffset corresponds to an array inside a mutable type.
*
* A FinalAlignedArrayOffset may meet the requirements to be cast to an array of
* the equivalent plain C++ element type (see rti::flat::plain_cast()).
*
* @see MutableArrayOffset encapsulates arrays of variable-size elements
*/
template <typename ElementOffset, unsigned int N>
class FinalAlignedArrayOffset : public AbstractAlignedList<ElementOffset> {
public:
typedef fixed_size_type_tag_t offset_kind;
FinalAlignedArrayOffset()
{
}
// Constructor used when this array is part of a mutable type, and
// therefore it's aligned to 4 always
FinalAlignedArrayOffset(
rti::flat::SampleBase *sample,
offset_t absolute_offset)
: AbstractAlignedList<ElementOffset>(
sample,
absolute_offset
+ detail::collection_dheader_traits<
false>::dheader_size(),
serialized_size(0))
{ }
// serialized_size() only works when this array is part of a mutable type
// and we know it's aligned to 4
static offset_t serialized_size(offset_t)
{
// [element1][pad][element2][pad]...[elementN]
return ElementOffset::serialized_size_w_padding() * (N - 1)
+ ElementOffset::serialized_size(0);
}
/**
* @brief Gets the Offset to an element
*
* @param i The zero-based index to the element
* @return The Offset to the element in the i-th position
*
* @see rti::flat::plain_cast() for a method to access all the elements at
* once
*/
ElementOffset get_element(unsigned int i)
{
RTI_FLAT_OFFSET_CHECK_NOT_NULL(return ElementOffset());
if (i >= N) {
return ElementOffset();
}
return AbstractAlignedList<ElementOffset>::get_element(i);
}
};
namespace detail {
// Arrays of complex elements may need a DHEADER
template <typename T, unsigned int N>
struct is_fixed_type_w_dheader<FinalAlignedArrayOffset<T, N> > {
static bool value()
{
return is_dheader_required_in_non_primitive_collections();
}
};
} // namespace detail
}} // namespace rti::flat
#endif // RTI_DDS_FLAT_SEQUENCEOFFSETS_HPP_
@@ -0,0 +1,171 @@
/*
(c) Copyright, Real-Time Innovations, 2018.
All rights reserved.
No duplications, whole or partial, manual or electronic, may be made
without express written permission. Any such copies, or
revisions thereof, must display this notice unaltered.
This code contains trade secrets of Real-Time Innovations, Inc.
*/
// Note: this file shouldn't be included directly. Include the header for the
// corresponding C++ API:
//
// - Modern C++ API: "rti/topic/flat/FlatData.hpp"
// - Traditional C++ API: "ndds/ndds_flat_data_cpp.h"
#ifndef RTI_DDS_FLAT_RTIFLAT_HPP_
#define RTI_DDS_FLAT_RTIFLAT_HPP_
/**
* @defgroup RTIFlatDataModule FlatData Topic-Types
* @ingroup DDSTopicModule
* @brief @st_ext FlatData Language Binding for IDL topic-types
*
* @note For a complete description of the FlatData language binding and its
* benefits, and a tutorial, see the "Sending Large Data" chapter in the
* <b>\ndds User's Manual</b>.
*
* @note For buildable <b>code examples</b>,
* see https://community.rti.com/kb/flatdata-and-zerocopy-examples.
*
* @note The FlatData language binding is available in the Traditional C++ API
* and in the Modern C++ API.
*
* RTI FlatData&trade; is a language binding for IDL types in which the in-memory
* representation of a sample matches the wire representation. Therefore, the
* cost of serialization/deserialization is zero.
*
* To select FlatData as the language binding of a type, annotate it with
* \p \@language_binding(FLAT_DATA) in IDL or apply the attribute
* \p languageBinding="flat_data" in XML.
*
* There are some restrictions regarding the kinds of types to which the
* FlatData language binding can be applied.
*
* - For final types, the FlatData language binding can be applied only to
* fixed-size types. A fixed-size type is a type whose wire representation
* always has the same size. This includes primitive types, arrays of
* fixed-size types, and structs containing only members of fixed-size types.
* Unions are not fixed-size types.
* - For mutable types, any member is permitted.
* - Extensible types cannot be marked as FlatData.
*
* Final types are more efficient, but more restrictive. In general, a good
* compromise is to define mutable top-level types, but making sure their
* largest data members are final.
*
* When a type is marked with the FlatData language binding, its mapping into
* C++ is different than that of a regular type (plain language binding). Rather
* than a single C++ class with direct access to its members, for a FlatData
* type \nddsgen generates the following:
*
* \li A \ref RTIFlatSampleModule "Sample", the data in serialized format
* \li An \ref RTIFlatOffsetsModule "Offset", which allows reading the data
* members of that type inside a Sample, and modifying them without changing
* the size
* \li A \ref RTIFlatBuildersModule "Builder", if the type is mutable, which
* allows creating a variable-size Sample
*
* For example, for the IDL types \ref MyFlatFinal and \ref MyFlatMutable
* \nddsgen generates the following C++ types:
* \li The Sample types \ref MyFlatFinal and \ref MyFlatMutable
* \li The Offset types MyFlatFinalOffset and MyFlatMutableOffset.
* \li The Builder type MyFlatMutableBuilder.
*
* @section PublishingFlatData Publishing FlatData
* The typical way to publish FlatData samples includes the following steps:
* - Create a \idref_FooDataWriter as you would for a non-FlatData (plain)
* topic-type (see \ref DDSWriterExampleModule_writer_setup).
*
* (For final topic-types such as \ref MyFlatFinal)
* - Obtain a FlatData sample with \idref_FooDataWriter_get_loan.
* \code
* MyFlatFinal *foo_sample = writer.get_loan();
* \endcode
*
* - Initialize the sample, starting from the \ref rti::flat::Sample::root "root()" offset.
* \code
* MyFlatFinalOffset foo_root = foo_sample->root();
* foo_root.my_primitive(3);
* auto my_complex_offset = foo_root.my_complex();
* // ... initialize my_complex_offset
* \endcode
*
* (For mutable topic-types such as \ref MyFlatMutable)
* - Obtain a \ref RTIFlatBuildersModule "Builder" to create a variable-size
* sample with rti::flat::build_data().
* \code
* MyFlatMutableBuilder foo_builder = rti::flat::build_data(writer);
* \endcode
*
* - Use this builder (and possibly nested member builders) to initialize the
* members.
* \code
* foo_builder.add_my_primitive(3);
* auto my_mutable_builder = foo_builder.build_my_mutable();
* // ... build 'my_mutable' (a member with a mutable type)
* my_mutable_builder.finish(); // completes a member
* auto my_final_offset = foo_builder.add_my_final();
* // ... initialize 'my_final' (a member with a final type)
* \endcode
*
* - Obtain the completed sample with MyFlatMutableBuilder::finish_sample(). After
* that, the builder is no longer usable, and the sample size cannot change.
* \code
* MyFlatMutable *foo_sample = foo_builder.finish_sample();
* \endcode
* - Optionally, it is possible to change the values of the sample accessing
* its root(), as long as the size doesn't change. For example, if the sample
* was built with a sequence member with two elements, it is possible to modify
* any of those elements, but it's not possible to add a third element.
*
* (For both final and mutable topic-types)
* - Write the sample with \idref_FooDataWriter_write.
* \code
* writer.write(*foo_sample);
* \endcode
*
* After write, the DataWriter owns the FlatData sample. This allows avoiding
* additional copies, the main goal of the FlatData language binding. This
* means that the sample cannot be reused. The DataWriter will return it to the
* sample pool when appropriate, as described in \idref_FooDataWriter_get_loan.
*
* @section SubscribingFlatData Subscribing to FlatData
*
* To subscribe to a topic using a FlatData topic-type:
* - Create a \idref_FooDataReader normally (see
* \ref DDSReaderExampleModule_reader_setup).
* - Read or take the samples using a loaning operation, such as
* \idref_FooDataReader_take (copying read/take operations cannot be used
* with FlatData types).
* - Access the \ref rti::flat::Sample::root "root()" offset, and any of the
* sample's members from there.
*
* Note that the language binding is a local concept. It is possible
* to publish with a FlatData topic-type and subscribe to it with a <b>plain</b>
* topic-type with the same (or assignable) definition. It is also possible to
* use a plain topic-type on the publisher side and subscribe to it using FlatData.
* The DataWriter or DataReader of the plain topic-type has to use
* \idref_XCDR2_DATA_REPRESENTATION in \idref_DataRepresentationQosPolicy.
*
*/
#ifdef DOXYGEN_DOCUMENTATION_ONLY
namespace rti {
/**
* @brief \st_ext Support for \ref RTIFlatDataModule
*/
namespace flat {
}
}
#endif
#include "rti/flat/ExceptionHelper.hpp"
#include "rti/flat/FlatSample.hpp"
#include "rti/flat/FlatTypeTraits.hpp"
#include "rti/flat/SequenceBuilders.hpp"
#endif // RTI_DDS_FLAT_RTIFLAT_HPP_