2016-05-17 1 views
0

В основном я реализую традиционный файловый буффер для файлового дескриптора для операций istream. Реализация переопределяет функции sync и underflow, тогда как в sync буфер будет автоматически увеличен, если необходимо (точно так же, как vector).Настраиваемый streambuffer для C++ istream

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. Мультиплексор ввода-вывода будет автономно вызывать sync и вызывать зарегистрированные обратные вызовы для выполнения операций istream. Проще говоря, вот некоторые из моих вопросов:

  1. Входной потоковый буффер имеет определенные операции, называемые «putback». Каково потенциальное преимущество использования putback?

  2. ::read вернется 0 при достижении EOF, который является очень полезным индикатором для сигнализации состояния соединения сокета на основе IO. Какова наилучшая практика для этого в sync? Моя текущая реализация просто пропускает это.

  3. Любые комментарии, которые могут улучшить реализацию, будут очень благодарны!

+0

Я думаю, что это относится к [обзор кода] (http://codereview.stackexchange.com/) – Jerfov2

ответ

0

Согласно Mr. Edd «с очень полезным введение в пользовательской реализации буфера потока,

Входной поток буферов, написанный для использования с IStreams, имеют тенденцию быть немного более сложным, чем выходных буферов, написанных для ostreams. Это связано с тем, что мы должны попытаться позволить пользователю вернуть символы в поток в разумной степени, что делается через функцию-член putback(). Это означает, что нам нужно поддерживать раздел в начале массива для пространства обратной связи. Как правило, ожидается один символ пространства возврата, хотя нет причин, по которым мы не сможем предоставить больше, в общем.

См. Также https://stackoverflow.com/a/39078167/1399272.

Смежные вопросы