0

我有一个包含多个 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()返回下一个打包消息的位置和大小。但话又说回来,BufferedInputStream 应该如何知道“消息”是什么。

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

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

4

1 回答 1

1

BufferedInputStream是不可搜索的。为了向后搜索,您将需要销毁bufferedStream然后搜索底层文件描述符,例如 with 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,这条线的效果是“前进到当前缓冲区并进入下一个缓冲区”。使用时您不想这样做PackedMessageReader,因为它已经推进了流本身。事实上,因为PackedMessageReader可能已经超过了当前缓冲区,framePtr现在可能是无效的,并且这条线可能会出现段错误。


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

如果文件在 RAM 中很合适,那么预先阅读它通常是好的,如果您希望来回寻找很多东西,这可能是一个好主意。

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

但是,我认为这实际上不会大大简化代码。现在您将处理一个ArrayInputStream(的子类BufferedInputStream)。要“寻找”,您将ArrayInputStream基于从您想要开始的点开始的缓冲区切片创建一个新的。

于 2020-04-20T13:54:17.270 回答