CS144 Lab1:StreamReAssemer

更好的阅读体验

实验架构

TCP实施中模块和数据流的排列 :

CS144 Lab1:StreamReAssemer_第1张图片

字节流是Lab0。TCP的工作是通过不可靠的数据报网络传输两个字节流(每个方向一个),以便写入连接一侧套接字的字节显示为可在对等端读取的字节,反之亦然。Lab1是StreamReAssemer,在Lab2、3和4中,您将实施TCPReceiverTCPSender,然后实施 TCPConnection 将它们连接在一起。

  1. 在Lab1中,您将实现一个流重组器-该模块将字节流的一小部分(称为子串或段)按正确的顺序缝合回连续的字节流。

  2. 在Lab2中,您将实现TCP中处理入站字节流的部分:TCPReceiver。这涉及到考虑TCP将如何表示流中每个字节的位置-称为“序列号”。TCPReceiver 负责告诉发送者(A)它已经能够成功组装多少入站字节流(这称为“确认”)和(B)发送者现在被允许发送多少字节(“flow控制”)。(B)TCPReceiver 负责告诉发送者(A)它已经能够成功组装多少入站字节流(这称为“确认”)和(B)允许发送者现在发送多少字节(“flow control”)。

  3. 在Lab3中,您将实现TCP中处理出站字节流的部分:TCPSender。当发送方怀疑其传输的数据段在途中丢失并且从未到达接收方时,它应该如何反应?它应该在什么时候重试并重新传输丢失的数据段?

  4. 在Lab4中,您将结合前面的工作和Lab来创建工作的TCP实现:包含TCPSenderTCPReceiverTCPConnection。您将使用它与世界各地的真实服务器进行对话。

您的Push Substring方法将忽略会导致 StreamReAssembly 超出其“容量”的字符串的任何部分:内存使用限制,即允许它存储的最大字节数。这可以防止重新组装器使用无限量的内存,无论TCP发送器决定执行什么操作。我们已经在下面的图片中对此进行了说明。“容量”是两者的上限:

  1. 重组的ByteStream中的字节数(如下绿色所示),以及

  2. unassembled”的子字符串可以使用的最大字节数(以红色显示)

CS144 Lab1:StreamReAssemer_第2张图片

  • 红色:re-assembler 保存在辅助存储器中的已接收字节
  • 绿色:re-assembler 保存在字节流中的已接收字节数
  • 蓝色:已读取的已接收字节数

说明

  • 整个数据流中第一个字节的索引是什么?
    • 0。
  • 我的实现应该有多大的效率?
    • 我们还不打算指定一个效率的概念,但请不要建立一个严重影响空间或时间的数据结构——这个数据结构将是你的TCP实现的基础。
  • 应该如何处理不一致的子串?
    • 你可以假设它们不存在。也就是说,你可以假设有一个唯一的底层字节流,而所有的子串都是它的(精确)片段。
  • 我可以使用什么?
    • 你可以使用你认为有用的标准库的任何部分。特别是,我们希望你至少要使用一个数据结构。
  • 字节什么时候应该被写入流中?
    • 越快越好。一个字节不应该出现在流中的唯一情况是,在它之前有一个字节还没有被”push”。
  • 子串可能重叠吗?
    • 可能。
  • 我是否需要向StreamReassembler添加私有成员?
    • 是的。由于段可能以任何顺序到达,你的数据结构将不得不记住子串,直到它们准备好被放入流中,也就是说,直到它们之前的所有索引都已填充。

实现思路

1. 要求

在我们所实现的 StreamReassembler 中,有以下几种特性:

  • 接收子字符串。这些子字符串中包含了一串字节,以及该字符串在总的数据流中的第一个字节的索引
  • 流的每个字节都有自己唯一的索引,从零开始向上计数。
  • StreamReassembler 中存在一个 ByteStream 用于输出,当 StreamReassembler 知道了流的下一个字节,它就会将其写入至 ByteStream 中。

需要注意的是,传入的子串中:

  • 子串之间可能相互重复,存在重叠部分

    但假设重叠部分数据完全重复。

    不存在某些 index 下的数据在某个子串中是一种数据,在另一个子串里又是另一种数据。

    重叠部分的处理最为麻烦。

  • 可能会传一些已经被装配了的数据

  • 如果 ByteStream 已满,则必须暂停装配,将未装配数据暂时保存起来

除了上面的要求以外,容量 Capacity 需要严格限制:

CS144 Lab1:StreamReAssemer_第3张图片

为了便于说明,将图中的绿色区域称为 ByteStream,将图中**存放红色区域的内存范围(即 first unassembled - first unacceptable)**称为 Unassembled_strs。

CS144 要求将 ByteStream + Unassembled_strs 的内存占用总和限制在 Reassember 中构造函数传入的 capacity 大小。因此我们在构造 Reassembler 时,需要既将传入的 capacity 参数设置为 ByteStream的缓冲区大小上限,也将其设置为first unassembled - first unacceptable的范围大小,以避免极端情况下的内存使用。

注意:first unassembled - first unacceptable的范围大小,并不等同于存放尚未装配子串的结构体内存大小上限,别混淆了。

Capacity 这个概念很重要,因为它不仅用于限制高内存占用,而且它还会起到流量控制的作用(见 lab2)。

2.实现

变量说明:

  • _next_assembled_idx:下一个待装配的字节索引

  • _unassemble_strs: 一个字节索引到数据子串的 map 映射

  • _eof_idx: 指示哪个字节索引代表 EOF

  • _unassemle_strs 一个字节索引到数据子串的 map 映射

  • _next_assembled_idx 下一个待装配的字节索引

  • _unassembled_bytes_num 已存储但尚未重组的子字符串中的字节数

  • _eof_idx 指示哪个字节索引代表 EOF

  • _output 重组后的有序字节流

  • _capacity 最大字节数

  • data 子字符串

  • index 表示data中第一个字节的索引(按顺序放置)

  • eof 标记data的最后一个字节是不是整个流中的最后一个字节

以下是具体需要考虑的情况

  • index <= _next_assembled_idx && _next_assembled_idx < index + data.size()

    $ next-assembled-idx \in [index,\ index + data.size()] $

    下一个待装配的字节索引在 data 子字符串之中

    data 头                             data 尾
    index    _next_assembled_idx   index+data.size()   
      V               V                   V 
    --+---------------+-------------------+-----------------------------
      |///+///|
    --+---------------+-------------------+-----------------------------
    

    这种情况可以先截断掉

    • 前面已经装配过的那部分数据,即 [ i n d e x   n e x t − a s s e m b l e d − i d x ] [index ~ next-assembled-idx] [index nextassembledidx] 这一部分

    • 后面与已经存入 _unassembled_strs 的数据重合的那部分数据,即 data 中 位于$ [next-assembled-idx,\ index + data.size()] $ 这一区间中与 _unassembled_strs 的数据重合的部分

    这样截断是为了让每次装配进的数据与存入 _unassembled_strs 的数据不产生重合,简化处理逻辑。

    之后就可以直接装配,无需任何额外处理。

    如果装配不下,即 _output 已满,那么就必须先放入待装配队列 _unassembled_strs 中,等待装配。

  • index > _next_assembled_idx

    这种情况是需要认真考虑的,因为这种情况可能会与一些已经保存起来的未装配子串重合,导致大量的内存占用以及无用的轮询处理。对于传入数据的始末位置,分别有好几种情况。

    为了便于说明,我们将_unassembled_strs中比 index 小且距离最近 的那部分数据,称作 front_data, 其起始位置称为 front_idx

    back_databack_idx 同上,指的是在 _unassembled_strs中比当前传入 index 大且距离最近的那部分数据与起始位置。

    front 指的是数据前面的那个方向,back 是数据后面的那个方向。

    首先是传入数据头部位置的情况:

    1. front_idx + front_data.size() <= index, 则说明当前传入 data 没有与已经保存的上一个子串重叠。这种无需处理

      _next_assembled_idx   front_idx             index  
                V            V                      V 
      ----------+------------+------------------+---+-----------------...
                |            |++++++++++++++++++|   |\\\\\\\\\\\\\\\\\...
      ----------+------------+------------------+---+-----------------...
                                      A
                                front_data.size
      
    2. front_idx + front_data.size() > index, 则说明传入数据前半部分重合,需要进行截断,同时在截断后更新当前 index

      截断前:

      _next_assembled_idx  front_idx   front_idx+front_data.size     
                V            V                  V 
      ----------+------------+-----------+------+-----------------...
                |            |+++++++++++|+/+/+/|\\\\\\\\\\\\\\\\\...
      ----------+------------+-----------+------+-----------------...
                                         A
                                       index
      

      截断后:

      _next_assembled_idx  front_idx   front_idx+front_data.size     
                V            V                  V 
      ----------+------------+------------------+-----------------...
                |            |++++++++++++++++++|\\\\\\\\\\\\\\\\\...
      ----------+------------+------------------+-----------------...
                                                A
                                             new_idx
      

    而对于传入数据尾部位置的情况,情况又有所不同:

    1. index + data.size() <= back_idx,则说明当前数据的后半部分没有重合,此时无需进行任何处理。

       index      index+data.size  back_idx
         V               V            V
      ---+---------------+------------+------------------...
         |///|            |++++++++++++++++++...
      ---+---------------+------------+------------------...
      
    2. index + data.size() > back_idx,则说明后半部分重合。但后半部分重合又有两种情况

      1. index + data.size() < back_idx + back_data.size(),这种就是常规情况的部分重合,截断掉重合部分即可

        截断前:

         index              index+data.size   
           V                       V  
        ---+---------------+-------+------------+-----...
           |///|+/+/+/+|++++++++++++|     ...
        ---+---------------+-------+------------+-----...
                           A                    A
                       back_idx       back_idx+back_data.size
        

        截断后

         index       index+data.size   
           V               V  
        ---+------------------------------------+-----...
           |///|++++++++++++++++++++|     ...
        ---+------------------------------------+-----...
                           A                    A
                        back_idx       back_idx+back_data.size
        
      2. index + data.size() < back_idx + back_data.size(),这种是完全重合:当前传入的 data 完全覆盖下一个保存的 data,此时将下一个 data 丢弃。

        注意,若存在完全覆盖的情况,则需要重复检测 index + data.size() 的位置与丢弃下一个data后,新的下一个data末尾位置。因为可能当前传入的 data同时覆盖好几个保存的 data

        处理前:

         index                          index+data.size   
           V                                    V  
        ---+-----+--------------------+---------+-----+------...
           |/|/+/+/+/+/+/+/+/+/+/+|/|     |++++++...
        ---+-----+--------------------+---------+-----+------...
                 A                    A                 
              back_idx       back_idx+back_data.size
        

        处理后:

         index                          index+data.size   
           V                                    V  
        ---+------------------------------------+-----+------...
           ||     |++++++...
        ---+------------------------------------+-----+------...
                                                      A
                                                  新的 back_idx
        

上面所描述的处理方式可以很好的保证, _unassembled_strs 中的各个子串之间互不重叠,提高了内存利用效率。

EOF 的实现需要严格按照实验指导书来。当传入的 eof 参数为真时,表示当前传入的数据子串的最后一个字节将是整个流中的最后一个字节,这并不意味着这是最后一次调用 reassembler 来传入子串,因此需要额外将这个 eof_idx 保存,并在 reassemble 后判断一下是否到达 EOF 位置。

环境配置

git fetch
git merge origin/lab1-startercode
cd build
make -j4

出现如下错误:

/usr/bin/ld: CMakeFiles/fsm_stream_reassembler_holes.dir/fsm_stream_reassembler_holes.cc.o: in function `BytesAvailable::execute(StreamReassembler&) const':
/home/misaka/Desktop/sponge/tests/fsm_stream_reassembler_harness.hh:57: undefined reference to `ByteStream::read[abi:cxx11](unsigned long)'
collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/fsm_stream_reassembler_holes.dir/build.make:86:tests/fsm_stream_reassembler_holes] 错误 1
make[1]: *** [CMakeFiles/Makefile2:5093:tests/CMakeFiles/fsm_stream_reassembler_holes.dir/all] 错误 2
make: *** [Makefile:95:all] 错误 2

由于 Lab0 中没有实现 ByteStream::read 函数,所以在 byte_stream.cc 中添加如下实现:

//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len) {
    string data = this->peek_output(len);
    this->pop_output(len);
    return data;
}

重新编译:

cd build
make clean && make -j4

编译成功

[  8%] Building CXX object libsponge/CMakeFiles/sponge.dir/stream_reassembler.cc.o
[  8%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/address.cc.o
[  8%] Building CXX object tests/CMakeFiles/spongechecks.dir/byte_stream_test_harness.cc.o
[  8%] Building CXX object libsponge/CMakeFiles/sponge.dir/byte_stream.cc.o
[ 10%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/buffer.cc.o
[ 12%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/eventloop.cc.o
[ 14%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/file_descriptor.cc.o
[ 17%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/parser.cc.o
[ 19%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/socket.cc.o
[ 21%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/tun.cc.o
[ 23%] Linking CXX static library libspongechecks.a
[ 23%] Built target spongechecks
[ 25%] Building CXX object libsponge/CMakeFiles/sponge.dir/util/util.cc.o
[ 27%] Linking CXX static library libsponge.a
[ 27%] Built target sponge
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_seq.dir/fsm_stream_reassembler_seq.cc.o
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_single.dir/fsm_stream_reassembler_single.cc.o
[ 36%] Building CXX object apps/CMakeFiles/webget.dir/webget.cc.o
[ 36%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_dup.dir/fsm_stream_reassembler_dup.cc.o
[ 38%] Linking CXX executable webget
[ 38%] Built target webget
[ 40%] Building CXX object tests/CMakeFiles/byte_stream_capacity.dir/byte_stream_capacity.cc.o
[ 42%] Linking CXX executable fsm_stream_reassembler_seq
[ 44%] Linking CXX executable fsm_stream_reassembler_single
[ 44%] Built target fsm_stream_reassembler_seq
[ 46%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_holes.dir/fsm_stream_reassembler_holes.cc.o
[ 46%] Built target fsm_stream_reassembler_single
[ 48%] Building CXX object tests/CMakeFiles/byte_stream_construction.dir/byte_stream_construction.cc.o
[ 51%] Linking CXX executable fsm_stream_reassembler_dup
[ 51%] Built target fsm_stream_reassembler_dup
[ 53%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_many.dir/fsm_stream_reassembler_many.cc.o
[ 55%] Linking CXX executable byte_stream_capacity
[ 55%] Built target byte_stream_capacity
[ 57%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_overlapping.dir/fsm_stream_reassembler_overlapping.cc.o
[ 59%] Linking CXX executable byte_stream_construction
[ 59%] Built target byte_stream_construction
[ 61%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_win.dir/fsm_stream_reassembler_win.cc.o
[ 63%] Linking CXX executable fsm_stream_reassembler_many
[ 63%] Built target fsm_stream_reassembler_many
[ 65%] Building CXX object tests/CMakeFiles/byte_stream_many_writes.dir/byte_stream_many_writes.cc.o
[ 68%] Linking CXX executable fsm_stream_reassembler_win
[ 68%] Built target fsm_stream_reassembler_win
[ 70%] Building CXX object tests/CMakeFiles/fsm_stream_reassembler_cap.dir/fsm_stream_reassembler_cap.cc.o
[ 72%] Linking CXX executable fsm_stream_reassembler_holes
[ 72%] Built target fsm_stream_reassembler_holes
[ 74%] Building CXX object tests/CMakeFiles/byte_stream_one_write.dir/byte_stream_one_write.cc.o
[ 76%] Linking CXX executable byte_stream_many_writes
[ 78%] Linking CXX executable fsm_stream_reassembler_overlapping
[ 78%] Built target byte_stream_many_writes
[ 80%] Building CXX object tests/CMakeFiles/byte_stream_two_writes.dir/byte_stream_two_writes.cc.o
[ 80%] Built target fsm_stream_reassembler_overlapping
[ 82%] Building CXX object doctests/CMakeFiles/parser_dt.dir/parser_dt.cc.o
[ 85%] Linking CXX executable parser_dt
[ 87%] Linking CXX executable byte_stream_one_write
[ 87%] Built target parser_dt
[ 89%] Building CXX object doctests/CMakeFiles/socket_dt.dir/socket_dt.cc.o
[ 89%] Built target byte_stream_one_write
[ 91%] Building CXX object doctests/CMakeFiles/address_dt.dir/address_dt.cc.o
[ 93%] Linking CXX executable byte_stream_two_writes
[ 93%] Built target byte_stream_two_writes
[ 95%] Linking CXX executable address_dt
[ 95%] Built target address_dt
[ 97%] Linking CXX executable socket_dt
[100%] Linking CXX executable fsm_stream_reassembler_cap
[100%] Built target socket_dt
[100%] Built target fsm_stream_reassembler_cap

编程实现

stream_reassembler.hh:

#ifndef SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
#define SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

#include "byte_stream.hh"

#include 
#include 
#include 

//! \brief A class that assembles a series of excerpts from a byte stream (possibly out of order,
//! possibly overlapping) into an in-order byte stream.
class StreamReassembler {
  private:
    // Your code here -- add private members as necessary.
    std::map<size_t, std::string> _unass; //!< The map the reflect the index of bytes to the substring
    size_t _next_ass_idx; //!< The index of the next bytes should be assembled
    size_t _unass_bytes;  //!< The number of bytes that is stored but still unassembled
    size_t _eof_idx;      //!< The index of EOF

    ByteStream _output;  //!< The reassembled in-order byte stream
    size_t _capacity;    //!< The maximum number of bytes

    size_t get_idx(const size_t index);
    void truncation(const size_t new_idx, ssize_t& data_size);
    void check_data(
      const std::string &data, 
      const size_t new_idx, 
      const ssize_t data_size, 
      const size_t data_start_pos
    );
    void check_contiguous();

  public:
    //! \brief Construct a `StreamReassembler` that will store up to `capacity` bytes.
    //! \note This capacity limits both the bytes that have been reassembled,
    //! and those that have not yet been reassembled.
    StreamReassembler(const size_t capacity);

    //! \brief Receive a substring and write any newly contiguous bytes into the stream.
    //!
    //! The StreamReassembler will stay within the memory limits of the `capacity`.
    //! Bytes that would exceed the capacity are silently discarded.
    //!
    //! \param data the substring
    //! \param index indicates the index (place in sequence) of the first byte in `data`
    //! \param eof the last byte of `data` will be the last byte in the entire stream
    void push_substring(const std::string &data, const uint64_t index, const bool eof);

    //! \name Access the reassembled byte stream
    //!@{
    const ByteStream &stream_out() const { return _output; }
    ByteStream &stream_out() { return _output; }
    //!@}

    //! The number of bytes in the substrings stored but not yet reassembled
    //!
    //! \note If the byte at a particular index has been pushed more than once, it
    //! should only be counted once for the purpose of this function.
    size_t unassembled_bytes() const;

    //! \brief Is the internal state empty (other than the output stream)?
    //! \returns `true` if no substrings are waiting to be assembled
    bool empty() const;
};

#endif  // SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

stream_reassembler.cc:

#include "stream_reassembler.hh"

#include 
// Dummy implementation of a stream reassembler.

// For Lab 1, please replace with a real implementation that passes the
// automated checks run by `make check_lab1`.

// You will need to add private members to the class declaration in `stream_reassembler.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}

using namespace std;

StreamReassembler::StreamReassembler(const size_t capacity)
    : _unass()
    , _next_ass_idx(0)
    , _unass_bytes(0)
    , _eof_idx(-1)
    , _output(capacity)
    , _capacity(capacity) {}

//! \details This function calls at first in the push_substring 
//! It aims to get the first index that doesn't overlap with 
//! the substring in the unassembled string in the map
size_t StreamReassembler::get_idx(const size_t index) {
    //!< Gets the first iterator pointer in _ unass that is greater than index
    auto pos_iter = _unass.upper_bound(index);
    //!< Try to get an iterator pointer that is less than or equal to index
    if (pos_iter != _unass.begin()) {
        pos_iter -- ;
    }

    size_t new_idx = index; //!< Gets the new starting position of the current substring
    //!< If there is a substring in front of it
    if (pos_iter != _unass.end() && pos_iter->first <= index) {
        const size_t front_idx = pos_iter->first;

        //!< If there is an overlap in front of the current substring
        if (index < front_idx + pos_iter->second.size()) {
            new_idx = front_idx + pos_iter->second.size();
        }
    } else if (index < _next_ass_idx) {
        //!< If there is no substring in front of it, compare it with the currently read pos
        new_idx = _next_ass_idx;
    }

    return new_idx;
}

//! \details When getting the next substring of the current substring, 
//! we need to consider that new_idx may coincide with back_idx
//! This function is aims to truncate the overlapping area
void StreamReassembler::truncate(const size_t new_idx, ssize_t& data_size) {
    auto pos_iter = _unass.lower_bound(new_idx);
    //!< If it conflicts with the following substring, truncate the length
    while (pos_iter != _unass.end() && new_idx <= pos_iter->first) {
        const size_t data_end_pos = new_idx + data_size;
        if (pos_iter->first < data_end_pos) {   //!< If there is an overlapping area
            //!< If it's a partial overlap
            if (data_end_pos < pos_iter->first + pos_iter->second.size()) {
                data_size = pos_iter->first - new_idx;
                break;
            } else {    //!< If it all overlaps
                _unass_bytes -= pos_iter->second.size();
                pos_iter = _unass.erase(pos_iter);
                continue;
            }
        } else { //!< If there is no overlap, break directly.
            break;
        }
    }
}

//! \details This function is aim at determine whether any data is independent 
//! and check whether the current substring is completely 
//! contained by the previous substring.
void StreamReassembler::check_data (
    const std::string &data,
    const size_t new_idx,
    const ssize_t data_size,
    const size_t data_start_pos 
) {
    if (data_size > 0) {                       
        const string new_data = data.substr(data_start_pos, data_size);
        
        if (new_idx == _next_ass_idx) {  //!< If the new string can be written directly
            const size_t write_byte = _output.write(new_data);
            _next_ass_idx += write_byte;
            
            if (write_byte < new_data.size()) { //!< If it haven't been wirte all, save it
                //!< _output is full that can't be written
                //!< insert into the _unass
                size_t len = new_data.size() - write_byte;
                const string data_to_store = new_data.substr(write_byte, len);
                _unass_bytes += data_to_store.size();
                _unass.insert(make_pair(_next_ass_idx, std::move(data_to_store)));
            }
        } else {
            const string data_to_store = new_data.substr(0, new_data.size());
            _unass_bytes += data_to_store.size();
            _unass.insert(make_pair(new_idx, std::move(data_to_store)));
        }
    }
}

//! \details This functions calls just after pushing a substring into the
//! _output stream. It aims to check if there exists any contiguous substrings
//! recorded earlier can be push into the stream.
void StreamReassembler::check_contiguous() {
    for (auto iter = _unass.begin(); iter != _unass.end(); /* nop */) { 
        assert(_next_ass_idx <= iter->first);

        //!< If it happens to be a message that can be received
        if (iter->first == _next_ass_idx) {
            const size_t write_num = _output.write(iter->second);
            _next_ass_idx += write_num;
            
            //!< If it is not fully written, it means that it is full
            //!< keep the rest and exit.
            if (write_num < iter->second.size()) {  
                _unass_bytes += iter->second.size() - write_num;
                _unass.insert(
                    make_pair(
                        _next_ass_idx, 
                        std::move(iter->second.substr(write_num))
                    )
                );
                _unass_bytes -= iter->second.size();
                _unass.erase(iter);
                break;
            } else {
                //!< If it is all written, delete the original iterator and update it
                _unass_bytes -= iter->second.size();
                iter = _unass.erase(iter);
            }
        } else { //!< Otherwise, just leave.
            break;
        }
    }
}

//! \details This function accepts a substring (aka a segment) of bytes,
//! possibly out-of-order, from the logical stream, and assembles any newly
//! contiguous substrings and writes them into the output stream in order.
void StreamReassembler::push_substring(const string &data, const size_t index, const bool eof) {
    size_t new_idx = get_idx(index);
    
    //!< The data index to which the new start position of the substring corresponds
    const size_t data_start_pos = new_idx - index;
    //!< The length of the data to be saved by the current substring
    ssize_t data_size = data.size() - data_start_pos;
    
    truncate(new_idx, data_size);
    
    size_t _1st_unac_idx = _next_ass_idx + _capacity - _output.buffer_size();

    //!< Detect whether there is data out of capacity.
    //!< Note that the capacity here does not refer to the number of bytes that can be saved
    //!< but to the size of the window that can be saved.
    if (_1st_unac_idx <= new_idx) {        
        return;
    }
    
    check_data(data, new_idx, data_size, data_start_pos);

    check_contiguous();
    
    if (eof) {
        _eof_idx = index + data.size();
    }

    if (_eof_idx <= _next_ass_idx) {
        _output.end_input();
    }
}

size_t StreamReassembler::unassembled_bytes() const { return _unass_bytes; }

bool StreamReassembler::empty() const { return _unass_bytes == 0; }

编译测试:

$ make -j4 && make check_lab1
[ 10%] Built target spongechecks
[ 27%] Built target sponge
[ 40%] Built target fsm_stream_reassembler_dup
[ 40%] Built target fsm_stream_reassembler_seq
[ 40%] Built target fsm_stream_reassembler_single
[ 44%] Built target webget
[ 61%] Built target byte_stream_capacity
[ 61%] Built target fsm_stream_reassembler_holes
[ 61%] Built target fsm_stream_reassembler_many
[ 61%] Built target byte_stream_construction
[ 78%] Built target fsm_stream_reassembler_cap
[ 78%] Built target byte_stream_many_writes
[ 78%] Built target fsm_stream_reassembler_overlapping
[ 78%] Built target fsm_stream_reassembler_win
[ 87%] Built target byte_stream_two_writes
[ 87%] Built target byte_stream_one_write
[ 91%] Built target socket_dt
[ 95%] Built target parser_dt
[100%] Built target address_dt
[100%] Testing the stream reassembler...
Test project /home/misaka/Desktop/sponge/build
      Start 18: t_strm_reassem_single
 1/16 Test #18: t_strm_reassem_single ............   Passed    0.00 sec
      Start 19: t_strm_reassem_seq
 2/16 Test #19: t_strm_reassem_seq ...............   Passed    0.00 sec
      Start 20: t_strm_reassem_dup
 3/16 Test #20: t_strm_reassem_dup ...............   Passed    0.00 sec
      Start 21: t_strm_reassem_holes
 4/16 Test #21: t_strm_reassem_holes .............   Passed    0.00 sec
      Start 22: t_strm_reassem_many
 5/16 Test #22: t_strm_reassem_many ..............   Passed    0.05 sec
      Start 23: t_strm_reassem_overlapping
 6/16 Test #23: t_strm_reassem_overlapping .......   Passed    0.00 sec
      Start 24: t_strm_reassem_win
 7/16 Test #24: t_strm_reassem_win ...............   Passed    0.05 sec
      Start 25: t_strm_reassem_cap
 8/16 Test #25: t_strm_reassem_cap ...............   Passed    0.06 sec
      Start 26: t_byte_stream_construction
 9/16 Test #26: t_byte_stream_construction .......   Passed    0.00 sec
      Start 27: t_byte_stream_one_write
10/16 Test #27: t_byte_stream_one_write ..........   Passed    0.00 sec
      Start 28: t_byte_stream_two_writes
11/16 Test #28: t_byte_stream_two_writes .........   Passed    0.00 sec
      Start 29: t_byte_stream_capacity
12/16 Test #29: t_byte_stream_capacity ...........   Passed    0.30 sec
      Start 30: t_byte_stream_many_writes
13/16 Test #30: t_byte_stream_many_writes ........   Passed    0.00 sec
      Start 53: t_address_dt
14/16 Test #53: t_address_dt .....................   Passed    0.00 sec
      Start 54: t_parser_dt
15/16 Test #54: t_parser_dt ......................   Passed    0.00 sec
      Start 55: t_socket_dt
16/16 Test #55: t_socket_dt ......................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 16

Total Test time (real) =   0.49 sec
[100%] Built target check_lab1

你可能感兴趣的:(CS144,wireshark,网络,windows)