#5467·poco

enh(Foundation): add a counted StreamCopier copy that does not seek

Author: matejkCreated Aug 28, 2026Updated Sep 15, 2026
Labelsenhancement

StreamCopier can already copy a bounded number of bytes, but only through copyStreamRange() / copyStreamRange64(), which begin with

cpp
istr.seekg(rangeStart, std::ios_base::beg);

That makes the bounded copy unavailable to code that reads sequentially from the current position on a stream that may not be seekable.

The rest of copyStreamRangeImpl() is exactly the loop such code needs: the buffer is clamped to the requested length

cpp
if (bufferSize > static_cast<std::size_t>(rangeLength))
    bufferSize = static_cast<std::size_t>(rangeLength);

so the allocation stays independent of the requested count, and the return value reports how much was actually copied, which is what distinguishes a complete read from a truncated one.

Proposal: factor the seek out and expose the loop.

cpp
template <typename T>
static T copyStreamCountedImpl(std::istream& istr, std::ostream& ostr,
                               std::streamsize count, std::size_t bufferSize);

static std::streamsize copyStreamN(std::istream& istr, std::ostream& ostr,
                                   std::streamsize count, std::size_t bufferSize = 8192);
static Poco::UInt64 copyStreamN64(std::istream& istr, std::ostream& ostr,
                                  std::streamsize count, std::size_t bufferSize = 8192);

copyStreamRangeImpl() then becomes the seek plus a call to the counted variant, so existing callers are unaffected and the change is additive.

Call sites that currently hand-roll the same loop:

  • Zip/src/ZipArchiveInfo.cpp, ZipArchiveInfo64::parse() -- reads the ZIP64 extensible data sector in chunks so a declared record size cannot drive the allocation.
  • Net/src/HTTPFixedLengthStream.cpp and Net/src/MultipartReader.cpp read a bounded amount in a similar way.

Deliberately not proposed for a patch release: this adds public Foundation API.