为C++istream定制流缓冲区

Customized streambuffer for C++ istream

本文关键字:缓冲区 C++istream      更新时间:2023-10-16

基本上,我为istream操作实现了一个传统的基于文件描述符的流缓冲区。该实现覆盖syncunderflow函数,而在sync中,缓冲区将在必要时自动放大(就像vector一样)。

class InputStreamBuffer : public std::basic_streambuf<char> {
  private:
    // small stack buffer optimization
    constexpr static size_t _STACK_BUFSZ = 128;
    static_assert(_STACK_BUFSZ >= 1, "Stack buffer size should be at least 1");
    char_type  _stk [_STACK_BUFSZ];
    char_type* _buf {_stk};
    size_t _size {_STACK_BUFSZ};
    const int _fd {-1};
  public:
    InputStreamBuffer(const int);
    ~InputStreamBuffer();
  protected:
    int sync() override;
    int_type underflow() override;
};
InputStreamBuffer::InputStreamBuffer(const int fd) :
  _fd {fd} {
  setg(_buf, _buf, _buf);
}
InputStreamBuffer::~InputStreamBuffer() {
  if(_buf != _stk) {
    std::free(_buf);
  }
}
int InputStreamBuffer::sync() {
  auto success = int {0};
  while(1) {
    size_t empty = gptr() - eback();
    size_t avail = egptr() - gptr();
    // Before we issue the read, make sure we have enough space.
    if(egptr() == eback() + _size) {
      // Reuse the empty region.
      if(empty > _size / 2) {
        std::memmove(eback(), gptr(), avail);
      }
      // Double the array size.
      else {
        _size = _size * 2;
        auto chunk = static_cast<char_type*>(std::malloc(_size*sizeof(char_type)));
        std::memcpy(chunk, gptr(), avail);
        if(_buf != _stk) std::free(_buf);
        _buf = chunk;
      }
      setg(_buf, _buf, _buf + avail);
    }
    // Read the data.
    issue_read:
    auto ret = ::read(_fd, egptr(), _size - avail);
    if(ret == -1) {
      if(errno == EINTR) {
        goto issue_read;
      }
      if(errno != EAGAIN && errno != EWOULDBLOCK) {
        success = -1;
      }
      break;
    }
    else if(ret == 0) {
      break;
    }
    setg(eback(), gptr(), egptr() + ret);
  }
  return success;
}
InputStreamBuffer::int_type InputStreamBuffer::underflow() {
  int success = sync();
  if(success == -1 || gptr() == egptr()) return traits_type::eof();
  return *(gptr());
}

这里的想法是将这个流缓冲区与非阻塞io一起使用。io多路复用器将自主调用sync,并调用用户注册的回调来执行istream风格的操作。简单地说,以下是我的一些问题:

  1. 输入流缓冲区具有称为"putback"的特定类型的操作。使用putback的潜在好处是什么?

  2. 当到达EOF时,::read将返回0,这是一个非常有用的指示基于套接字的IO连接状态的信号。在sync中处理此问题的最佳实践是什么?我当前的实现只是跳过了这个。

  3. 任何可以改进实施的意见都将不胜感激!

根据Edd先生对自定义流缓冲区实现的非常有用的介绍,

为与istream一起使用而编写的输入流缓冲区往往比为ostream编写的输出缓冲区复杂一点。这是因为我们应该努力允许用户在合理的程度上将字符放回流中,这是通过std::istreamputback()成员函数完成的。这意味着我们需要在数组的开头保留一个部分,用于放置回空间。通常,预计会有一个字符的放回空间,尽管一般来说,我们没有理由不能提供更多。

另请参阅https://stackoverflow.com/a/39078167/1399272.