Capnp: перейти к предыдущей позиции в BufferedInputStreamWrapper - PullRequest
0 голосов
/ 16 апреля 2020

У меня есть двоичный файл с несколькими сообщениями 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 ). Лучшее решение с самого начала?

1 Ответ

0 голосов
/ 20 апреля 2020

BufferedInputStream не ищется. Чтобы искать в обратном направлении, вам нужно будет уничтожить bufferedStream, а затем искать базовый дескриптор файла, например, с помощью lseek(), а затем создать новый буферный поток.

Обратите внимание, что читается текущая позиция (по порядку переходить к lseek() позже к go back) также сложно, если буферизованный поток присутствует, так как буферизованный поток будет считывать данные после позиции, чтобы заполнить буфер. Вы можете вычислить его, вычтя из размера буфера, например:

// 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 ). Лучшее решение с самого начала?

Если файл удобно помещается в ОЗУ, то чтение его заранее, как правило, хорошо, и, вероятно, будет хорошей идеей, если вы будете много и много искать.

Другой вариант - mmap(). При этом создается впечатление, что файл находится в ОЗУ, но операционная система фактически читает содержимое по требованию при обращении к ним.

Однако я не думаю, что это действительно сильно упростит код , Теперь вы будете иметь дело с ArrayInputStream (подкласс BufferedInputStream). Чтобы «искать», вы должны создать новый ArrayInputStream, основанный на срезе буфера, начиная с той точки, с которой вы хотите начать.

...