File xcdr1_codec.hpp
File List > astutedds > cdr > xcdr1_codec.hpp
Go to the documentation of this file
#ifndef ASTUTEDDS_XCDR1_CODEC_HPP
#define ASTUTEDDS_XCDR1_CODEC_HPP
#include "cdr_types.hpp"
#include <cstring>
#include <iostream>
namespace astutedds::cdr {
class XCDR1Encoder {
public:
XCDR1Encoder(std::vector<uint8_t>& buffer, bool big_endian = false)
: buffer_(buffer), swap_bytes_((!big_endian) != is_little_endian()) {
// Write encapsulation header
EncapsulationHeader header;
header.encoding = big_endian ? EncodingKind::CDR_BE : EncodingKind::CDR_LE;
header.options = 0;
write_encapsulation_header(header);
}
// Encode primitive types
void encode_uint8(uint8_t value) { write_aligned(&value, sizeof(value), 1); }
void encode_int8(int8_t value) { write_aligned(&value, sizeof(value), 1); }
void encode_uint16(uint16_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 2);
}
void encode_int16(int16_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 2);
}
void encode_uint32(uint32_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 4);
}
void encode_int32(int32_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 4);
}
void encode_uint64(uint64_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 8);
}
void encode_int64(int64_t value) {
if (swap_bytes_) value = swap_endian(value);
write_aligned(&value, sizeof(value), 8);
}
void encode_float(float value) {
uint32_t bits;
std::memcpy(&bits, &value, sizeof(value));
if (swap_bytes_) bits = swap_endian(bits);
write_aligned(&bits, sizeof(bits), 4);
}
void encode_double(double value) {
uint64_t bits;
std::memcpy(&bits, &value, sizeof(value));
if (swap_bytes_) bits = swap_endian(bits);
write_aligned(&bits, sizeof(bits), 8);
}
void encode_bool(bool value) {
uint8_t byte = value ? 1 : 0;
write_aligned(&byte, sizeof(byte), 1);
}
// Encode string (null-terminated)
void encode_string(const std::string& str) {
uint32_t length = static_cast<uint32_t>(str.size() + 1); // Include null
encode_uint32(length);
buffer_.insert(buffer_.end(), str.begin(), str.end());
buffer_.push_back(0); // Null terminator
// Pad to 4-byte alignment
size_t current_pos = buffer_.size();
size_t aligned_pos = align_offset(current_pos, 4);
if (aligned_pos > current_pos) {
buffer_.resize(aligned_pos, 0);
}
}
// Encode sequence length
void encode_sequence_length(uint32_t length) {
encode_uint32(length);
}
size_t size() const { return buffer_.size(); }
private:
void write_encapsulation_header(const EncapsulationHeader& header) {
// Per CDR encapsulation rules, kind/options are always written in network byte order (big-endian),
// independent of the payload endianness.
const uint16_t encoding_value = static_cast<uint16_t>(header.encoding);
const uint16_t options_value = header.options;
buffer_.resize(4);
buffer_[0] = static_cast<uint8_t>((encoding_value >> 8) & 0xFF);
buffer_[1] = static_cast<uint8_t>(encoding_value & 0xFF);
buffer_[2] = static_cast<uint8_t>((options_value >> 8) & 0xFF);
buffer_[3] = static_cast<uint8_t>(options_value & 0xFF);
}
void write_aligned(const void* data, size_t size, size_t alignment) {
size_t current_pos = buffer_.size();
size_t aligned_pos = align_offset(current_pos, alignment);
// Add padding
if (aligned_pos > current_pos) {
buffer_.resize(aligned_pos, 0);
}
// Write data
size_t old_size = buffer_.size();
buffer_.resize(old_size + size);
std::memcpy(&buffer_[old_size], data, size);
}
std::vector<uint8_t>& buffer_;
bool swap_bytes_;
};
class XCDR1Decoder {
public:
XCDR1Decoder(const std::vector<uint8_t>& buffer)
: buffer_(buffer), position_(0) {
// Read and validate encapsulation header (minimum 4 bytes required)
if (buffer_.size() < 4) {
throw std::runtime_error("Buffer too small for CDR encapsulation header (minimum 4 bytes required)");
}
// Encapsulation encoding is always in big-endian (network byte order)
uint16_t encoding_value = (buffer_[0] << 8) | buffer_[1];
EncodingKind encoding = static_cast<EncodingKind>(encoding_value);
bool data_is_big_endian = (encoding == EncodingKind::CDR_BE ||
encoding == EncodingKind::PL_CDR_BE);
// Swap bytes if data and machine endianness differ
bool machine_is_little_endian = is_little_endian();
swap_bytes_ = (data_is_big_endian == machine_is_little_endian);
position_ = 4; // Skip encapsulation header
}
XCDR1Decoder(std::vector<uint8_t>&&) = delete;
// Decode primitive types
uint8_t decode_uint8() { return read_aligned<uint8_t>(1); }
int8_t decode_int8() { return read_aligned<int8_t>(1); }
uint16_t decode_uint16() {
uint16_t value = read_aligned<uint16_t>(2);
return swap_bytes_ ? swap_endian(value) : value;
}
int16_t decode_int16() {
int16_t value = read_aligned<int16_t>(2);
return swap_bytes_ ? swap_endian(value) : value;
}
uint32_t decode_uint32() {
uint32_t value = read_aligned<uint32_t>(4);
return swap_bytes_ ? swap_endian(value) : value;
}
int32_t decode_int32() {
int32_t value = read_aligned<int32_t>(4);
return swap_bytes_ ? swap_endian(value) : value;
}
uint64_t decode_uint64() {
uint64_t value = read_aligned<uint64_t>(8);
return swap_bytes_ ? swap_endian(value) : value;
}
int64_t decode_int64() {
int64_t value = read_aligned<int64_t>(8);
return swap_bytes_ ? swap_endian(value) : value;
}
float decode_float() {
uint32_t bits = read_aligned<uint32_t>(4);
if (swap_bytes_) bits = swap_endian(bits);
float value;
std::memcpy(&value, &bits, sizeof(value));
return value;
}
double decode_double() {
uint64_t bits = read_aligned<uint64_t>(8);
if (swap_bytes_) bits = swap_endian(bits);
double value;
std::memcpy(&value, &bits, sizeof(value));
return value;
}
bool decode_bool() {
return read_aligned<uint8_t>(1) != 0;
}
// Decode string
std::string decode_string() {
uint32_t length = decode_uint32();
#ifdef ASTUTEDDS_XCDR_DIAGNOSTICS
std::cerr << "[DECODE_STRING] After decode_uint32: length=" << length << ", pos=" << position_ << ", bufsize=" << buffer_.size() << std::endl;
#endif
if (length == 0) {
return "";
}
if (position_ + length > buffer_.size()) {
#ifdef ASTUTEDDS_XCDR_DIAGNOSTICS
std::cerr << "[DECODE_STRING] Buffer underflow: pos=" << position_ << " + len=" << length << " = " << (position_ + length) << " > bufsize=" << buffer_.size() << std::endl;
#endif
throw std::runtime_error("Buffer underflow reading string");
}
std::string result(reinterpret_cast<const char*>(&buffer_[position_]), length - 1);
#ifdef ASTUTEDDS_XCDR_DIAGNOSTICS
std::cerr << "[DECODE_STRING] Created string: '" << result << "', length=" << (length-1) << std::endl;
#endif
position_ += length;
#ifdef ASTUTEDDS_XCDR_DIAGNOSTICS
std::cerr << "[DECODE_STRING] After adding length: pos=" << position_ << std::endl;
#endif
// Align to 4-byte boundary after string data
position_ = align_offset(position_, 4);
#ifdef ASTUTEDDS_XCDR_DIAGNOSTICS
std::cerr << "[DECODE_STRING] After alignment: pos=" << position_ << std::endl;
#endif
return result;
}
// Decode sequence length
uint32_t decode_sequence_length() {
return decode_uint32();
}
size_t position() const { return position_; }
bool at_end() const { return position_ >= buffer_.size(); }
private:
template<typename T>
T read_aligned(size_t alignment) {
position_ = align_offset(position_, alignment);
if (position_ + sizeof(T) > buffer_.size()) {
throw std::runtime_error("Buffer underflow");
}
T value;
std::memcpy(&value, &buffer_[position_], sizeof(T));
position_ += sizeof(T);
return value;
}
const std::vector<uint8_t>& buffer_;
size_t position_;
bool swap_bytes_;
};
} // namespace astutedds::cdr
#endif // ASTUTEDDS_XCDR1_CODEC_HPP