CS144-LAB0,实现ByteStream

443 阅读4分钟

我正在参加「掘金·启航计划」

Lab0 要求我们实现一个在内存中的 有序可靠字节流(有点类似于管道)

代码风格

CS144 使用 C++11 标准完成实验,它对C++代码的风格有着严格的限制:

  • 使用 Resource acquisition is initialization 风格,即 RAII 风格。

  • 禁止使用 malloc 和 free 函数

  • 禁止使用 new 和 delete 关键字

  • 禁止使用原生指针(*)。若有必要,最好使用智能指针(unique_ptr等等)。

    CS144实验指导书说明,该实验没有必要用到指针。

  • 禁止使用模板、线程相关、各类锁机制以及虚函数

  • 禁止使用C风格字符串(char*) 以及 C 风格字符串处理函数。使用 string 来代替。

  • 禁止使用 C 风格强制类型转换。若有必要请使用 static_cast

  • 传递参数给函数时,请使用常量引用类型(const Ty& t)

  • 尽可能将每个变量和函数方法都声明成 const

  • 禁止使用全局变量,以及尽可能让每个变量的作用域最小

  • 在完成代码后,务必使用 make format 来标准化代码风格。

要求

  • 字节流可以从写入端写入,并以相同的顺序,从读取端读取
  • 字节流是有限的,写者可以终止写入。而读者可以在读取到字节流末尾时,产生EOF标志,不再读取。
  • 所实现的字节流必须支持流量控制,以控制内存的使用。当所使用的缓冲区爆满时,将禁止写入操作。直到读者读取了一部分数据后,空出了一部分缓冲区内存,才让写者写入。
  • 写入的字节流可能会很长,必须考虑到字节流大于缓冲区大小的情况。即便缓冲区只有1字节大小,所实现的程序也必须支持正常的写入读取操作。

在单线程环境下执行,因此不用考虑各类条件竞争问题。

这是在内存中的有序可靠字节流,接下来的实验会让我们在不可靠网络中实现一个这样的可靠字节流,而这便是传输控制协议(Transmission Control Protocol,TCP)

ByteStream

private:
    std::deque<char> _queue;//双端队列
    size_t _capacity_size; //字符流的大小
    size_t _written_size;//已经写入的字符数
    size_t _read_size;//已经读出的字符数
    bool _end_input;//输入流是否关闭
    bool _error{};  //字符流是否错误

write端

size_t write(const std::string &data); //将数据写入字节流中,并返回写入的大小(可能不能全部写入
size_t remaining_capacity() const; //字节流中还可以写入多少字符
void end_input(); //关闭输入流
void set_error() { _error = true; } //字节流错误

read端

std::string peek_output(const size_t len) const; //读取出长度为len的字符串(可能读不了这么多
void pop_output(const size_t len);  //将读出的字符串删除
std::string read(const size_t len) {  //从字节流中读取字符串
        const auto ret = peek_output(len);
        pop_output(len);
        return ret;
    }
bool input_ended() const; //输入流是否关闭
bool error() const { return _error; } //字节流是否错误
size_t buffer_size() const; //字节流中的大小
bool buffer_empty() const; //字节流是否为空
bool eof() const; //输入流关闭且字节流为空
size_t bytes_written() const; //总共写入了多少字符
size_t bytes_read() const;  //总共读取了多少字符

byte_stream.cc

#include "byte_stream.hh"

// Dummy implementation of a flow-controlled in-memory byte stream.

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

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

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

using namespace std;

ByteStream::ByteStream(const size_t capacity)
    : _queue(), _capacity_size(capacity), _written_size(0), _read_size(0), _end_input(false), _error(false) {}

size_t ByteStream::write(const string &data) {
    size_t res = 0;
    if(!input_ended()){
        res = min(data.length(),remaining_capacity());
        _written_size += res;
        for (size_t i = 0; i < res; i++)
        {
            _queue.push_back(data[i]);
        }        
    }
    return res;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
    size_t pop_size = min(len,buffer_size());
    return string(_queue.begin(), _queue.begin() + pop_size);
    
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
    size_t pop_size = min(len,buffer_size());
    _read_size += pop_size;
    for (size_t i = 0; i < pop_size; i++)
    {
        _queue.pop_front();       
    }
}

//! 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 res = peek_output(len);
    pop_output(len);
    return res;
}

void ByteStream::end_input() { _end_input = true;}

bool ByteStream::input_ended() const { return _end_input; }

size_t ByteStream::buffer_size() const { return _queue.size(); }

bool ByteStream::buffer_empty() const { return _queue.empty(); }

bool ByteStream::eof() const { return input_ended() && buffer_empty();}

size_t ByteStream::bytes_written() const { return _written_size; }

size_t ByteStream::bytes_read() const { return _read_size;}

size_t ByteStream::remaining_capacity() const { return _capacity_size - _queue.size();}

byte_stream.hh

#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH

#include <deque>
#include <string>

//! \brief An in-order byte stream.

//! Bytes are written on the "input" side and read from the "output"
//! side.  The byte stream is finite: the writer can end the input,
//! and then no more bytes can be written.
class ByteStream {
  private:
    // Your code here -- add private members as necessary.

    // Hint: This doesn't need to be a sophisticated data structure at
    // all, but if any of your tests are taking longer than a second,
    // that's a sign that you probably want to keep exploring
    // different approaches.
    std::deque<char> _queue;
    size_t _capacity_size;
    size_t _written_size;
    size_t _read_size;
    bool _end_input;
    bool _error{};  //!< Flag indicating that the stream suffered an error.

  public:
    //! Construct a stream with room for `capacity` bytes.
    ByteStream(const size_t capacity);

    //! \name "Input" interface for the writer
    //!@{

    //! Write a string of bytes into the stream. Write as many
    //! as will fit, and return how many were written.
    //! \returns the number of bytes accepted into the stream
    size_t write(const std::string &data);

    //! \returns the number of additional bytes that the stream has space for
    size_t remaining_capacity() const;

    //! Signal that the byte stream has reached its ending
    void end_input();

    //! Indicate that the stream suffered an error.
    void set_error() { _error = true; }
    //!@}

    //! \name "Output" interface for the reader
    //!@{

    //! Peek at next "len" bytes of the stream
    //! \returns a string
    std::string peek_output(const size_t len) const;

    //! Remove bytes from the buffer
    void pop_output(const size_t len);

    //! Read (i.e., copy and then pop) the next "len" bytes of the stream
    //! \returns a string
    std::string read(const size_t len);

    //! \returns `true` if the stream input has ended
    bool input_ended() const;

    //! \returns `true` if the stream has suffered an error
    bool error() const { return _error; }

    //! \returns the maximum amount that can currently be read from the stream
    size_t buffer_size() const;

    //! \returns `true` if the buffer is empty
    bool buffer_empty() const;

    //! \returns `true` if the output has reached the ending
    bool eof() const;
    //!@}

    //! \name General accounting
    //!@{

    //! Total number of bytes written
    size_t bytes_written() const;

    //! Total number of bytes popped
    size_t bytes_read() const;
    //!@}
};

#endif  // SPONGE_LIBSPONGE_BYTE_STREAM_HH