Блочное копирование данных между потоковыми буферами

Я хотел бы эффективно копировать данные между экземплярами std::streambuf. То есть, я хотел бы сгребать блоки данных между ними, а не выполнять копирование по одному символу. Например, это не то, что я ищу:

stringbuf in{ios_base::in};
stringbuf out{ios_base::out};
copy(istreambuf_iterator<char>{in},
     istreambuf_iterator<char>{},
     ostreambuf_iterator<char>{out});

Для этого существует синтаксический сахар, с еще большей проверкой ошибок:

ostream os{&out};
os << &in;

Вот фрагмент реализации operator<<(basic_streambuf<..>*) в моей стандартной библиотеке (Mac OS X, XCode 7):

                typedef istreambuf_iterator<_CharT, _Traits> _Ip;
                typedef ostreambuf_iterator<_CharT, _Traits> _Op;
                _Ip __i(__sb);
                _Ip __eof;
                _Op __o(*this);
                size_t __c = 0;
                for (; __i != __eof; ++__i, ++__o, ++__c)
                {
                    *__o = *__i;
                    if (__o.failed())
                        break;
                }

Суть в том, что это копирование по-одному. Я надеялся, что стандартная библиотека использует алгоритм, который опирается на функции-члены уровня блока streambuffers, sputn и sgetn, в отличие от транспорта на один символ. Предоставляет ли эталонная библиотека такой алгоритм или мне нужно сворачивать самостоятельно?

Ответ 1

Я боюсь, что ответ таков: это невозможно с текущим дизайном стандартной библиотеки. Причина в том, что потоковые буфферы полностью скрывают последовательность символов, которыми они управляют. Это делает невозможным прямое копирование байтов из области получения одного буфера потока в область сложения другого.

Если "input" streambuffer будет выставлять свой внутренний буфер, тогда "выходной" streambuffer может просто использовать sputn(in.data(), in.size()). Или, что более очевидно: если выходной буфер также выставил свой внутренний буфер, можно было бы использовать простой memcpy для перекоса байтов между ними. Другие библиотеки ввода-вывода работают следующим образом: поток реализации буферов протокола Google, например. Boost IOStreams имеет оптимизированную реализацию для копирования между потоками. В обоих случаях эффективное блочное копирование возможно, потому что эквивалент streambuffer обеспечивает доступ к его промежуточному буферу.

Фактически, потоковым буферистам, по иронии судьбы, даже не нужно иметь буфер: при работе без буферизации каждая запись/запись переходит непосредственно к базовому устройству. Предположительно, это одна из причин, по которой стандартная библиотека не поддерживает интроспекцию. К сожалению, невозможно обеспечить эффективное копирование между потоковыми буферами ввода и вывода. Для копирования на уровне блоков требуется промежуточный буфер, и алгоритм копирования будет работать следующим образом:

  • Чтение из входного потокового буфера через sgetn в промежуточный буфер.
  • Запишите из промежуточного буфера в выходной поток буфера через sputn.
  • Перейдите к 1. до тех пор, пока вход не будет исчерпан или не будет сгенерирован вывод выходной бункер-буфера