Capnp:移动到 BufferedInputStreamWrapper 中的上一个位置

Capnp: Move to previous position in BufferedInputStreamWrapper

我有一个二进制文件,其中包含我想读取的多个 Capnp 消息。按顺序阅读效果很好,但我有一个用例,我想跳转到以前已知的位置。 带有元数据的数据序列图像包括时间戳。我希望有可能来回跳转(就像在视频播放器中一样)。

这是我试过的:

int fd = open(filePath.c_str(), O_RDONLY);
kj::FdInputStream fdStream(fd);
kj::BufferedInputStreamWrapper bufferedStream(fdStream);
for (;;) {
  kj::ArrayPtr<const kj::byte> framePtr = bufferedStream.tryGetReadBuffer();

  if (framePtr != nullptr) {
    capnp::PackedMessageReader message(bufferedStream);
    // This should reset the buffer to the last read message?
    bufferedStream.read((void*)framePtr.begin(), framePtr.size());
    // ...
  }
  else {
    // reset to beginning
  }
}

但是我得到这个错误:

capnp/serialize.c++:186: failed: expected segmentCount < 512; Message has too many segments

我假设 tryGetReadBuffer() returns 下一条打包消息的位置和大小。但话又说回来,BufferedInputStream 应该如何知道 "a message" 是什么。

问题:如何获取消息的位置和大小并稍后从 BufferedInputStreamWrapper 读取这些消息?

替代方案:读取整个文件一次,获取数据的所有权并将其保存到向量中。如此处所述 (https://groups.google.com/forum/#!topic/capnproto/Kg_Su1NnPOY)。一直以来都是更好的解决方案?

BufferedInputStream 不可搜索。为了向后查找,您需要销毁 bufferedStream 然后查找底层文件描述符,例如使用 lseek(),然后创建一个新的缓冲流。

请注意,如果存在缓冲流,读取当前位置(以便稍后传递给 lseek() 以返回)也很棘手,因为缓冲流将读取过去的位置以便填充缓冲区。您可以通过减去缓冲区大小来计算它,例如:

// Determine current file position, so that we can seek to it later.
off_t messageStartPos = lseek(fd, 0, SEEK_CUR) -
    bufferedStream.tryGetReadBuffer().size();

// Read a message
{
  capnp::PackedMessageReader message(bufferedStream);
  // ... do stuff with `message` ...

  // Note that `message` is destroyed at this }. It's important that this
  // happens before querying the buffered stream again, because
  // PackedMesasgeReader updates the buffer position in its destructor.
}

// Determine the end position of the message (if you need it?).
off_t messageEndPos = lseek(fd, 0, SEEK_CUR) -
    bufferedStream.tryGetReadBuffer().size();

bufferedStream.read((void*)framePtr.begin(), framePtr.size());

FWIW,这行的效果是"advance past the current buffer an on to the next one"。在使用 PackedMessageReader 时,您不想这样做,因为它已经提前了流本身。事实上,因为 PackedMessageReader 可能已经超过了当前缓冲区,所以 framePtr 现在可能无效,并且这一行可能会出现段错误。


Alternative: Reading the whole file once, take ownership of the data and save it to a vector. Such as described here (https://groups.google.com/forum/#!topic/capnproto/Kg_Su1NnPOY). Better solution all along?

如果文件适合 RAM,那么预先读取它通常没问题,如果您希望经常来回查找,这可能是个好主意。

另一种选择是mmap()它。这使得它看起来好像文件在 RAM 中,但操作系统实际上会在您访问它们时按需读取内容。

但是,我认为这实际上不会大大简化代码。现在你要处理一个 ArrayInputStreamBufferedInputStream 的子类)。要 "seek",您将根据从您要开始的位置开始的缓冲区切片创建一个新的 ArrayInputStream