CS144 Lab1

看本文之前请先仔细浏览实验手册,要大致知道实验让我们做啥。

实验内容

        详细请看实验手册。简略来说就是做一个流重组器,保证能够有序接收字符串。

要点

capacity以及_output

        个人感觉重点是要理解实验指导书的这幅图以及capacity的含义到底是什么。

图一 

本人一开始做的时候思路非常的乱,看实验指导书看了许多遍也未能准确把握capacity的含义,后面本人想既然实验是实现TCP中的某个部分,那么这些东西肯定会与TCP中的原理相对应,于是本人回顾了TCP原理,最后发现capacity就是滑动窗口的大小。于是本人将上图稍微进行了下修改一一下使它更便于理解。如图二所示。

图二 

再来看实验代码中已经有的变量:

    ByteStream _output;  //!< The reassembled in-order byte stream

这个是在lab0中实现的变量,在上图中对应的就是有序但等待上层程序读取的数据。

所以我们还要添加一个私有变量,用来存放无序,但需要接收字符串以变成有序的数据。

    std::map<size_t,std::string> unassembled_strings;//未排序好的字符串

本人为什么用map待会儿再解释。

push_substring函数 

        push_substring函数绝对是整个实验中核心,传入的字符串data,本人一开始考虑了大概6种情况,但花费了许久仔细想想,得到图二之后,发现其实就两种情况。因为需要传入字符串的区域,仅是图二中用来存放无序数据需要接收字符串,以变成有序的数据的这片区域。即第一,传入的字符串data并未落入这片区域,那么我们不需要对其进行处理;第二,如果传入的字符串全部或者部分落在了这片区域内,我们仅需要对字符串进行裁剪仅保留落入到这片区域的部分。核心代码如下:

unassembled_start为这片区域开始的位置,unassembled_capacity函数返回该区域的大小。

    if(index+data.size()<unassembled_start||index>unassembled_start+unassembled_capacity())return;
    //取二者最大的
    size_t sub_str_index=max(index,unassembled_start);
    //取二者最小的
    size_t sub_str_endindex=min(index+data.size(),unassembled_start+unassembled_capacity());
    string sub_str=data.substr(sub_str_index-index,sub_str_endindex-sub_str_index);

        在得到sub_str字符串之后,我们插入到这片区域即可,注意该字符串可能与该区域会有重叠,所以我们要对其进行处理。

    if(index+data.size()<=unassembled_start+unassembled_capacity()&&eof) _eof=true;
    //将字符串插入到该区域中
    insert_unassembled_string(sub_str_index,sub_str);
    //将有序的数据写入到_output区域中
    write_assembled_string();
  insert_unassembled_string函数

以下是insert_unassembled_string函数的具体代码,相关的算法可以参考Leetcode56 Leetcode57。

https://leetcode.cn/problems/merge-intervals/description/

https://leetcode.cn/problems/insert-interval/

相信做完以上两道题,实现该函数会容易很多。

这里解释使用map的原因,map底层是用红黑树实现的,并且map自动维护了升序的序列,更便于对字符串相关重叠进行处理。

void StreamReassembler::insert_unassembled_string(size_t index,string str1){
    if(str1.empty()) return;
    map<size_t,string>::iterator it=unassembled_strings.find(index);
    //如果找到了
    if(it!=unassembled_strings.end()){
        //如果大于
        if(str1.size()>it->second.size()) {
            unassembled_count+=str1.size()-it->second.size();
            it->second=str1;
        }
    }
    //没找到那么直接插入即可
    else{
        unassembled_strings.insert(make_pair(index,str1));
        unassembled_count+=str1.size();
    }
    //做的是字符串的合并
    for(it=unassembled_strings.begin();it!=unassembled_strings.end();){
        map<size_t,string>::iterator it1=next(it);
        if(it1==unassembled_strings.end()) break;
        size_t str1_end=it->first+it->second.size();
        size_t str2_end=it1->first+it1->second.size();
        大于等于说明有重叠
        if(str1_end>=it1->first){
            //二号字符有一部分超过一号字符串
            if(str1_end<str2_end){
                //减去重叠部分的数量
                unassembled_count-=it1->second.size()-(str2_end-str1_end);
                it->second=it->second+it1->second.substr(it1->second.size()-(str2_end-str1_end),str2_end-str1_end);
            }
            //否则减去二号字符串
            else unassembled_count-=it1->second.size();
            unassembled_strings.erase(it1);
        }
        else{
            it++;
        }
    }
}
 write_assembled_string()函数

我们将字符串插入到无序区域,无序区域可能会变成有序,所以要将有序的字符串写入_output中。

void StreamReassembler::write_assembled_string() {
    //如果非空
    if(!empty()){
        auto it=unassembled_strings.begin();
        if(it->first==unassembled_start){
            _output.write(it->second);
            unassembled_count-=it->second.size();
            unassembled_start+=it->second.size();
            unassembled_strings.erase(it);
        }
    }
    //如果已经空了,并且字符已经结束那么结束input
    if(empty()&&_eof) _output.end_input();
}

完整代码

stream_reassembler.hh

#ifndef SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH
#define SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

#include "byte_stream.hh"

#include <cstdint>
#include <string>
#include <map>

//! \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.
    ByteStream _output;  //!< The reassembled in-order byte stream
    size_t _capacity;    //!< The maximum number of byte
    std::map<size_t,std::string> unassembled_strings;//未排序好的字符串
    size_t unassembled_start;//未排好序到字符串开始
    size_t unassembled_count;//未排序的字符串个数
    bool _eof;
  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;

    size_t assembled_capacity()const;

    size_t unassembled_capacity() const;
    //插入未排序好的字符串
    void insert_unassembled_string(size_t index,std::string str1);
    //插入排序好的字符串
    void write_assembled_string();
};

#endif  // SPONGE_LIBSPONGE_STREAM_REASSEMBLER_HH

stream_reassembler.cc

#include "stream_reassembler.hh"

// 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) : _output(capacity), _capacity(capacity),unassembled_strings(),unassembled_start(0),unassembled_count(0),_eof(false) {}

//! \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) {
    DUMMY_CODE(data, index, eof);
    if(index+data.size()<unassembled_start||index>unassembled_start+unassembled_capacity())return;
    //取二者最大的
    size_t sub_str_index=max(index,unassembled_start);
    //取二者最小的
    size_t sub_str_endindex=min(index+data.size(),unassembled_start+unassembled_capacity());
    string sub_str=data.substr(sub_str_index-index,sub_str_endindex-sub_str_index);
    if(index+data.size()<=unassembled_start+unassembled_capacity()&&eof) _eof=true;
    insert_unassembled_string(sub_str_index,sub_str);
    write_assembled_string();
}

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

bool StreamReassembler::empty() const { return unassembled_strings.empty(); }

size_t StreamReassembler::assembled_capacity() const{
    return _output.buffer_size();
}

size_t StreamReassembler::unassembled_capacity() const{
    return _capacity-_output.buffer_size();
}

void StreamReassembler::insert_unassembled_string(size_t index,string str1){
    if(str1.empty()) return;
    map<size_t,string>::iterator it=unassembled_strings.find(index);
    //如果找到了
    if(it!=unassembled_strings.end()){
        //如果大于
        if(str1.size()>it->second.size()) {
            unassembled_count+=str1.size()-it->second.size();
            it->second=str1;
        }
    }
    //没找到那么直接插入即可
    else{
        unassembled_strings.insert(make_pair(index,str1));
        unassembled_count+=str1.size();
    }
    //做的是字符串的合并
    for(it=unassembled_strings.begin();it!=unassembled_strings.end();){
        map<size_t,string>::iterator it1=next(it);
        if(it1==unassembled_strings.end()) break;
        size_t str1_end=it->first+it->second.size();
        size_t str2_end=it1->first+it1->second.size();
        大于等于说明有重叠
        if(str1_end>=it1->first){
            //二号字符有一部分超过一号字符串
            if(str1_end<str2_end){
                //减去重叠部分的数量
                unassembled_count-=it1->second.size()-(str2_end-str1_end);
                it->second=it->second+it1->second.substr(it1->second.size()-(str2_end-str1_end),str2_end-str1_end);
            }
            //否则减去二号字符串
            else unassembled_count-=it1->second.size();
            unassembled_strings.erase(it1);
        }
        else{
            it++;
        }
    }
}

void StreamReassembler::write_assembled_string() {
    //如果非空
    if(!empty()){
        auto it=unassembled_strings.begin();
        if(it->first==unassembled_start){
            _output.write(it->second);
            unassembled_count-=it->second.size();
            unassembled_start+=it->second.size();
            unassembled_strings.erase(it);
        }
    }
    //如果已经空了,并且字符已经结束那么结束input
    if(empty()&&_eof) _output.end_input();
}





实验结果

遇到的坑

1.关于如何debug的问题,本人一开始用的是GDB,但是GDB奈何本人不是很熟练,于是将GDB改成了VScode下的debug,如何设置可参考以下文章:

https://yao-yin.github.io/cs144/2021/06/20/cs144-lab-1/

2.本人一开始在写完代码之后,发现怎么样结果都是错的,后来发现lab1要用到lab0实现的东西,所以得加上lab0写的代码。并且要确保lab0的代码是正确的,这个可能得debug啥的才知道,本人个人的体验是,由于自己之前写的某些lab0的代码不够规范,即使通过了lab0的test(lab0的测试用列过少了)也会因为某些问题,导致lab1的测试结果受到影响

碎碎念

        本人算是比较菜的吧,相比于很多大佬几个小时就写完了,本人花费了接近两天的时间,中途一度想过要放弃吧,思想斗争也做了很久 ~~没关系的吧,我认识的很多学长也没做cs144呀他们也不是一样也很厉害嘛~~ 反正是自己给自己加的任务,做不出来就做不出来吧,没有人会怪我的。虽然debug是很折磨,但最终还是坚持了下来,看到全部pass的那一刻成就感满满,哈哈哈,算是彩笔的自我满足吧。

  • 28
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值