Reassembler 类
概述
Reassembler 类负责接收字节流中可能乱序、可能重叠且受容量限制的子字符串,并将它们按正确顺序重组。一旦从当前期望的索引开始的连续字节序列可用,它就会被写入到一个输出的 ByteStream 中。Reassembler 管理一个内部缓冲区,用于存储那些乱序到达但在输出 ByteStream 可接受容量范围内的段。
reassembler_state 结构体
这个内部结构体用于在 Reassembler 的内部缓冲区中存储各个数据段。
struct reassembler_state
{
uint64_t first_index; // 该数据段在原始流中的起始字节索引。
uint64_t last_index; // 该数据段的结束字节索引(不包含此索引对应的字节)。
std::string data; // 段的实际字节数据。
bool is_last; // 如果此段包含整个流的最后一个字节,则为 true。
// 用于按 first_index 排序段的比较运算符。
bool operator<( const reassembler_state& other ) const { return first_index < other.first_index; }
// 构造函数
reassembler_state( uint64_t first_index_in, uint64_t last_index_in, std::string data_in, bool is_last_in )
: first_index( first_index_in )
, last_index( last_index_in )
, data( std::move( data_in ) )
, is_last( is_last_in )
{}
};
私有成员变量
ByteStream output_: 重组后的数据被写入的ByteStream对象。Reassembler拥有这个流的所有权。std::vector<reassembler_state> buffer_: 用于存储已接收但由于早期字节缺失而无法立即写入output_的reassembler_state段的缓冲区。此向量按first_index保持排序。uint64_t current_index_:Reassembler期望写入到output_流的下一个字节的索引。uint64_t pending_size_: 当前存储在buffer_中所有段的总字节数。此计数反映了待重组的唯一字节。
构造函数
explicit Reassembler( ByteStream&& output )
: output_( std::move( output ) ), buffer_(), current_index_( 0 ), pending_size_( 0 )
{}
- 通过移入提供的
ByteStream(output) 来初始化Reassembler的内部存储。 - 将
current_index_(第一个期望的字节) 设置为 0。 - 将
pending_size_(内部存储的字节数) 初始化为 0。 - 内部
buffer_初始化为空。
主要方法
void insert( uint64_t first_index, std::string data, bool is_last_substring )
这是向 Reassembler 输入数据段的核心方法。
-
功能:
-
修剪与验证:
- 首先根据
output_流的available_capacity()(可用容量) 和current_index_(当前应写入位置) 来验证传入的data段。 - 完全落在
current_index_之前的数据字节将被忽略。 - 超出
current_index_ + output_.writer().available_capacity()范围的数据字节将被丢弃。这意味着数据会被修剪以适应当前可接受字节的窗口。 - 如果在修剪后,数据段为空或无效 (例如,其有效起始点在其有效结束点之后),函数可能会提前返回。
- 首先根据
-
is_last标志处理:is_last_substring标志与处理后的段相关联。关键在于,如果传入的段被标记为is_last_substring,但由于容量限制而被截断 (即,其原始数据直到其原始末尾的部分并非全部都能被接受),那么其存储段的is_last状态实际上会被撤销。- 处理可能带有
is_last_substring标志的空data字符串,将它们作为有效的 (可能是零长度的) 段进行处理。如果它们位于current_index_,则可以关闭流。
-
缓冲与合并:
-
为传入
data的有效部分创建一个reassembler_state对象。 -
更新
pending_size_以反映这个新的、有效数据段的大小。 -
然后,代码尝试将这个新段与
buffer_中现有的相邻或重叠段合并:- 它在
buffer_中搜索新段可以与之合并的段 (包括在新段之后的段,以及可能在新段之前的一个段)。 - 合并期间,数据被组合,
last_index被更新,并且is_last标志被传播 (如果参与合并的任一段是is_last,则结果合并段变为is_last)。 pending_size_会被精确调整,以移除被完全吸收的段的贡献,或解释合并段大小的变化。- 被完全合并到另一个段中的段将从
buffer_中移除。
- 它在
-
如果新段 (在任何向前合并之后) 没有被前一个段完全吸收,它将被插入到
buffer_中,同时保持按first_index的排序顺序。
-
-
写入输出流:
- 在处理并可能插入/合并新段后,
Reassembler检查buffer_开头的段 (即buffer_.front()) 是否具有等于current_index_的first_index。 - 如果是,则这个连续的段被
push到output_.writer()。 pending_size_因写入的数据量而减少。current_index_因写入的数据量而前进。- 如果写入的段被标记为
is_last,则调用output_.writer().close()方法。 - 然后从
buffer_中移除已写入的段。 - (注意:当前实现仅在刚处理的段恰好位于缓冲区前端且其索引与
current_index_匹配时,才会尝试写入该段。它不会在单次insert调用中循环写入多个新形成的连续段。)
- 在处理并可能插入/合并新段后,
-
uint64_t count_bytes_pending() const
- 功能: 返回当前存储在
Reassembler内部buffer_中、等待重组并写入输出流的唯一字节总数。 - 返回值:
pending_size_的值。 - 注意: 此函数用于测试,并依赖于
insert方法准确维护pending_size_。
注意事项总结
- 空的末尾段: 可能会插入""空数据,并且这个空数据可能设置了
is_last_substring,所以需要当作正常数据进行处理. - 末尾段的截断: 如果一个标记为
last的段因为过长而无法完全容纳在output_流的可用容量内 (考虑到current_index_),它将被截断。我可能插入的数据过长(这个数据为last数据),但是数据长度超过了缓冲区的大小,所以此时last数据作废。