Basically I implement a traditional file-descriptor-based streambuffer for istream operations. The implementation overrides the sync and underflow functions, while in sync the buffer will be automatically enlarged if necessary (just like 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());}The idea here is to use this streambuffer with non-blocking io. The IO multiplexer will autonomously call sync and invoke the user-registered callbacks to perform istream-style operations. Simply put, here are some of my questions:The input streambuffer has certain type of operations called putback. What is the potentially benefit of using the putback?The ::read will return 0 when reaching EOF, which is a very useful indicator for signaling the connection status of socket-based IO. What is the best practice to handle this in sync? My current implementation simply skips this.Any comments that can improve my implementation will be greatly appreciated!
Customized streambuffer for C++ istream
c++;io;stream