流缓冲区之间的块级数据复制
Block-level copying of data between streambuffers
我想在 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 << ∈
这是我的标准库中 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;
}
底线是:这仍然是按字符复制。我希望标准库使用一种算法,该算法依赖于流缓冲区 sputn
和 sgetn
的块级成员函数,而不是按字符传输。标准库是否提供这样的算法,还是我必须自己动手?
恐怕答案是:以目前的标准库设计是不可能的。原因是流缓冲区完全隐藏了它们管理的字符序列。这使得无法直接将字节从一个流缓冲区的获取区域复制到另一个流缓冲区的放置区域。
如果 "input" 流缓冲区将公开其内部缓冲区,那么 "output" 流缓冲区可以只使用 sputn(in.data(), in.size())
。或者更明显:如果输出缓冲区也暴露了它的内部缓冲区,那么可以使用普通的 memcpy
来铲除两者之间的字节。其他 I/O 图书馆以这种方式运作:stream implementation of Google's Protocol Buffers, for example. Boost IOStreams has an optimized implementation to copy between streams。在这两种情况下,高效的块级复制是可能的,因为流缓冲区等价物提供对其中间缓冲区的访问。
事实上,具有讽刺意味的是,流缓冲区甚至不需要缓冲区:在无缓冲操作时,每个 read/write 直接进入底层设备。大概这是标准库不支持内省的原因之一。不幸的结果是输入和输出流缓冲区之间不可能进行有效的复制。块级复制需要一个中间缓冲区,复制算法的操作如下:
- 通过
sgetn
从输入流缓冲区读取到中间缓冲区。
- 通过
sputn
从中间缓冲区写入输出流缓冲区。
- 转到 1。直到输入耗尽或写入输出流缓冲区失败
我想在 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 << ∈
这是我的标准库中 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;
}
底线是:这仍然是按字符复制。我希望标准库使用一种算法,该算法依赖于流缓冲区 sputn
和 sgetn
的块级成员函数,而不是按字符传输。标准库是否提供这样的算法,还是我必须自己动手?
恐怕答案是:以目前的标准库设计是不可能的。原因是流缓冲区完全隐藏了它们管理的字符序列。这使得无法直接将字节从一个流缓冲区的获取区域复制到另一个流缓冲区的放置区域。
如果 "input" 流缓冲区将公开其内部缓冲区,那么 "output" 流缓冲区可以只使用 sputn(in.data(), in.size())
。或者更明显:如果输出缓冲区也暴露了它的内部缓冲区,那么可以使用普通的 memcpy
来铲除两者之间的字节。其他 I/O 图书馆以这种方式运作:stream implementation of Google's Protocol Buffers, for example. Boost IOStreams has an optimized implementation to copy between streams。在这两种情况下,高效的块级复制是可能的,因为流缓冲区等价物提供对其中间缓冲区的访问。
事实上,具有讽刺意味的是,流缓冲区甚至不需要缓冲区:在无缓冲操作时,每个 read/write 直接进入底层设备。大概这是标准库不支持内省的原因之一。不幸的结果是输入和输出流缓冲区之间不可能进行有效的复制。块级复制需要一个中间缓冲区,复制算法的操作如下:
- 通过
sgetn
从输入流缓冲区读取到中间缓冲区。 - 通过
sputn
从中间缓冲区写入输出流缓冲区。 - 转到 1。直到输入耗尽或写入输出流缓冲区失败