Fast-CDR 源码详细分析
工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/eProsima/Fast-CDR
版本:1.0.24(configure.ac),构建类型 CMake,语言 C++11,许可证 Apache 2.0。
eProsima Fast-CDR 是一个轻量级 C++ 序列化库,提供两套 CDR(Common Data Representation)机制:标准 CDR(Cdr)与 Fast CDR(FastCdr)。在 ROS 2 Humble 工作区中,它是 Fast-DDS 的底层依赖,也是 rosidl_typesupport_fastrtps 生成代码所依赖的序列化引擎——所有通过 Fast DDS 传输的 ROS 消息,最终都经由 Fast-CDR 写入/读出字节流。
1. 总体认识
1.1 核心职责
| 能力 | 说明 |
|---|---|
| 标准 CDR 序列化 | 遵循 CORBA/DDS 规范,含字节对齐、大小端转换、Encapsulation 头 |
| Fast CDR 序列化 | eProsima 自研的简化协议,不做对齐,连续写入,性能更高 |
| 缓冲区管理 | FastBuffer 提供内部/外部两种内存模式,支持自动扩容 |
| 类型覆盖 | 基本类型、字符串、数组、序列、自定义类型(通过 serialize()/deserialize() 回调) |
| 异常安全 | 序列化失败时可通过 state 回滚到出错前位置 |
1.2 在 ROS 2 栈中的位置
| 消费者 | 使用方式 |
|---|---|
| Fast-DDS | 直接链接 libfastcdr,序列化/反序列化 RTPS payload |
| rosidl_typesupport_fastrtps | 生成代码调用 eprosima::fastcdr::Cdr 的 <</>> 与 Cdr::alignment() |
| Fast-DDS-Gen / rosidl 工具链 | 为 IDL/msg 生成 serialize(Cdr&) / deserialize(Cdr&) 成员函数 |
注意:ROS 2 默认 RMW 实现(Fast DDS)走
Cdr(标准 CDR) 路径;FastCdr主要用于 eProsima 内部或对性能更敏感、且双方约定使用 Fast CDR 协议的场景。
2. 目录结构
1 | Fast-CDR/ |
源码体量很小:约 65 个文件,核心逻辑集中在 3 个头文件 + 3 个实现文件中。
3. FastBuffer — 字节流容器
路径:include/fastcdr/FastBuffer.h、src/cpp/FastBuffer.cpp
3.1 两种构造模式
| 构造方式 | 行为 |
|---|---|
FastBuffer() |
内部分配内存(m_internalBuffer = true),析构时 free() |
FastBuffer(char* buf, size_t size) |
借用外部缓冲区,不释放 |
3.2 内存扩容策略
1 | bool FastBuffer::resize( |
要点:
- 初始分配 200 字节(
BUFFER_START_LENGTH) - 每次扩容至少增加 200 字节,或按
minSizeInc增量 - 外部缓冲区模式下
resize()返回false,序列化超出容量会抛NotEnoughMemoryException
3.3 _FastBuffer_iterator — 高效读写迭代器
迭代器封装了 memcpy 读写,避免逐字节循环:
1 | template<typename _T> |
特殊操作符:
<< iterator:切换底层 buffer 指针,保持相对偏移>> iterator:从另一个 iterator 同步位置索引memcopy/rmemcopy:批量拷贝
4. Cdr — 标准 CDR 序列化
路径:include/fastcdr/Cdr.h、src/cpp/Cdr.cpp
4.1 关键枚举与配置
1 | typedef enum |
CORBA_CDR:经典 CORBA CDR,无 Encapsulation 头DDS_CDR:DDS 扩展,含 dummy byte、encapsulation kind、options- 默认字节序由编译目标决定(
FASTCDR_IS_BIG_ENDIAN_TARGET)
4.2 Encapsulation(封装头)
DDS 消息在 payload 开头写入/读取 encapsulation,用于声明字节序和 Parameter List 标志:
1 | Cdr& Cdr::read_encapsulation() |
Encapsulation 字节布局(DDS_CDR):
1 | [ dummy:1B=0 ] [ encapsulationKind:1B ] [ options:2B ] |
4.3 字节对齐机制
标准 CDR 的核心特征:多字节类型必须按自身大小对齐。
静态对齐计算(供 rosidl 生成代码预估序列化大小):
1 | inline static size_t alignment( |
实例对齐(序列化过程中):
1 | inline size_t alignment( |
优化:若当前要序列化的类型大小 ≤ 上一个类型大小(m_lastDataSize),则无需额外对齐字节——这是 CDR 规范中的 packed 优化。
int16_t 序列化示例:
1 | Cdr& Cdr::serialize( |
4.4 大小端转换
- 构造时:
m_swapBytes = (endianness != DEFAULT_ENDIAN) - 读 encapsulation 时可能动态切换
- 多字节类型在
m_swapBytes == true时逐字节反转写入/读出 - 提供带
Endianness参数的serialize(T, Endianness)重载,临时切换字节序
4.5 字符串编码
| 类型 | 序列化格式 |
|---|---|
const char* / std::string |
uint32 length(含 \0)+ 字符数据 |
const wchar_t* / std::wstring |
uint32 char_count + 每字符 4 字节(Windows 逐字符,Linux 批量 memcopy) |
bool |
1 字节:0 或 1 |
long double |
对齐到 8 字节边界,占 16 字节(8 字节平台前 8 字节填 0) |
4.6 序列 / 数组
std::vector<T>:先写int32长度,再写元素数组- 序列化失败时通过
state回滚(保证原子性) std::vector<bool>有特殊模板特化(MSVC / 非 MSVC 分支不同)
4.7 自定义类型
非基本类型通过模板调用对象的成员函数:
1 | template<class _T> |
rosidl / Fast-DDS-Gen 为每个 struct 生成 void serialize(eprosima::fastcdr::Cdr&) const 和 void deserialize(eprosima::fastcdr::Cdr&)。
4.8 state — 序列化快照
Cdr::state 保存四个字段,用于出错回滚或嵌套序列化:
| 字段 | 含义 |
|---|---|
m_currentPosition |
当前读写位置 |
m_alignPosition |
对齐基准位置 |
m_swapBytes |
是否字节交换 |
m_lastDataSize |
上次序列化类型大小 |
5. FastCdr — 无对齐快速序列化
路径:include/fastcdr/FastCdr.h、src/cpp/FastCdr.cpp
5.1 与 Cdr 的核心差异
| 特性 | Cdr |
FastCdr |
|---|---|---|
| 字节对齐 | ✅ 按 CDR 规范 | ❌ 无对齐,紧凑排列 |
| Encapsulation | ✅ CORBA/DDS | ❌ 无 |
| 大小端 | ✅ 支持切换 | ❌ 不做字节交换 |
| 数组批量写入 | 对齐后逐元素或 memcopy | 直接 memcopy 整块 |
| state 字段 | 4 个 | 1 个(仅 position) |
| 典型用途 | DDS/ROS 2 互操作 | eProsima 内部高性能场景 |
5.2 基本类型序列化
以 int16_t 为例——无对齐,直接 memcpy:
1 | FastCdr& serialize( |
数组类型直接批量拷贝:
1 | FastCdr& FastCdr::serializeArray( |
5.3 long double 平台差异
FastCdr 对 long double 做了大量平台分支:
- 16 字节平台:直接 memcopy
- 8 字节平台:写 16 字节(前 8 字节填 0,后 8 字节为值)
- 支持
__float128:转换为 128 位浮点再写入
这与 Cdr 的处理逻辑一致,保证与 DDS XTypes 128-bit float 布局兼容。
6. 异常体系
路径:include/fastcdr/exceptions/、src/cpp/exceptions/
classDiagram
class exception {
<<std::exception>>
}
class Exception {
+raise()*
+what() const
-m_message
}
class NotEnoughMemoryException {
缓冲区空间不足
}
class BadParamException {
非法参数/格式
}
exception <|-- Exception
Exception <|-- NotEnoughMemoryException
Exception <|-- BadParamException| 异常 | 触发场景 |
|---|---|
NotEnoughMemoryException |
缓冲区剩余空间不足且无法 resize |
BadParamException |
encapsulation 格式错误、bool 非法值、空指针等 |
设计特点:
- 继承
std::exception,同时提供raise()用于 re-throw(配合 state 回滚) - 序列化复合类型(vector、sequence)在 catch 块中
setState(state_before_error)后ex.raise()
7. 构建与配置
7.1 CMake 要点
- 产物:
libfastcdr.so(默认 shared) - 版本:从
configure.ac读取 → 1.0.24 - 编译特性检测:
check_endianness()、check_type_sizes()、check_stdcxx() - 生成
config.h:C++11 支持、大小端、FASTCDR_SIZEOF_LONG_DOUBLE等
7.2 config.h 关键宏
1 | #define FASTCDR_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ |
7.3 colcon 集成
1 | { |
在 ROS 2 工作区中,fastcdr 作为 Fast-DDS 的前置依赖 被 colcon 自动构建。
7.4 DLL 导出
fastcdr_dll.h 定义 Cdr_DllAPI 宏:
- Windows 动态库:
__declspec(dllexport/dllimport) - Linux:空宏(符号默认可见)
- 支持
EPROSIMA_ALL_DYN_LINK/FASTCDR_DYN_LINK全局开关
8. 测试
8.1 SimpleTest.cpp
约 6931 行,使用 gtest,覆盖:
- 全部基本类型的 serialize/deserialize 往返
- CORBA_CDR 与 DDS_CDR 两种模式
- 大端 / 小端
- 数组、vector、嵌套 array
- encapsulation 读写
- state 回滚
- 空字符串、边界值
8.2 ResizeTest.cpp
专门测试 FastBuffer 内部缓冲区的自动扩容行为。
9. ROS 2 集成详解
9.1 rosidl_typesupport_fastrtps 生成代码
rosidl_typesupport_fastrtps_cpp 为每个 msg 生成:
get_serialized_size()— 调用Cdr::alignment()静态方法预估大小serialize()— 创建FastBuffer+Cdr,写 encapsulation,逐字段<<deserialize()— 读 encapsulation,逐字段>>
生成模板片段(msg__type_support.cpp.em):
1 | eprosima::fastcdr::Cdr::alignment(current_alignment, sizeof(uint32_t)); |
9.2 典型序列化流程(Fast DDS RMW)
1 | ROS msg 对象 |
9.3 与 CycloneDDS 的对比
| 中间件 | 序列化库 | CDR 实现 |
|---|---|---|
| Fast DDS | Fast-CDR | eprosima::fastcdr::Cdr |
| CycloneDDS | 内置 CDR | ddsc 内部实现,不依赖 Fast-CDR |
两者 payload 格式均遵循 OMG CDR 规范,但实现独立;同一 msg 在两种 RMW 间二进制 payload 通常兼容(相同 encapsulation + 对齐规则)。
10. Cdr 与 FastCdr 选型
11. 设计特点小结
| 特点 | 说明 |
|---|---|
| 双引擎 | 标准兼容(Cdr)+ 性能优化(FastCdr) |
| Header-heavy | 大量 inline 模板在头文件,.cpp 只实现复杂逻辑 |
| 零拷贝倾向 | iterator + memcopy 批量操作;外部 buffer 模式避免内存复制 |
| 异常 + state 回滚 | 复合类型序列化失败时不留半成品 |
| 跨平台 long double | 8/16 字节平台分支 + __float128 支持 |
| ROS 2 QL1 | 声明 Quality Level 1(见 QUALITY.md) |
| 轻量 | 核心库仅 ~3600 行实现代码 |
12. 推荐阅读顺序
- 缓冲区基础:
FastBuffer.h→FastBuffer.cpp— 理解内存模式与 iterator - 标准 CDR 核心:
Cdr.cpp中serialize(int16_t)、read_encapsulation()— 对齐与 encapsulation - Fast CDR 对比:
FastCdr.h中同名serialize(int16_t)— 体会无对齐差异 - ROS 2 生成代码:
rosidl_typesupport_fastrtps_cpp/resource/msg__type_support.cpp.em - Fast-DDS 使用点:
Fast-DDS/src/cpp/fastdds/dds/中搜索fastcdr::Cdr - 测试验证:
test/SimpleTest.cpp前 200 行 — 了解 API 用法模式 - 配置宏:构建后查看
build/fastcdr/include/fastcdr/config.h
13. API 速查
13.1 基本用法(Cdr)
1 |
|
13.2 基本用法(FastCdr)
1 | eprosima::fastcdr::FastBuffer fastbuffer; |
13.3 关键公开方法
| 类 | 方法 | 用途 |
|---|---|---|
FastBuffer |
getBuffer(), reserve(), resize() |
缓冲区访问与扩容 |
Cdr |
serialize_encapsulation(), read_encapsulation() |
DDS 封装头 |
Cdr |
alignment(), resetAlignment() |
对齐计算与控制 |
Cdr |
getState(), setState() |
快照/回滚 |
Cdr |
changeEndianness() |
动态切换字节序 |
Cdr/FastCdr |
getSerializedDataLength() |
已序列化字节数 |
Cdr/FastCdr |
operator<< / operator>> |
流式序列化/反序列化 |
文档基于 ROS 2 Humble 工作区中的 Fast-CDR 1.0.24 源码分析生成。
正在加载留言…