是否有可能使用Varint32大小前缀的协议缓冲区消息实现&FileConput_ream :: BackUp()'类功能?

时间:2015-09-23 18:35:51

标签: c++ protocol-buffers

我正在尝试使用readDelimitedFrom()的{​​{3}}在C ++中解析分隔的protobuf消息(来自文件) - 也复制如下:

bool readDelimitedFrom(
    google::protobuf::io::ZeroCopyInputStream* rawInput,
    google::protobuf::MessageLite* message) {
  // We create a new coded stream for each message.  Don't worry, this is fast,
  // and it makes sure the 64MB total size limit is imposed per-message rather
  // than on the whole stream.  (See the CodedInputStream interface for more
  // info on this limit.)
  google::protobuf::io::CodedInputStream input(rawInput);

  // Read the size.
  uint32_t size;
  if (!input.ReadVarint32(&size)) return false;

  // Tell the stream not to read beyond that size.
  google::protobuf::io::CodedInputStream::Limit limit =
      input.PushLimit(size);

  // Parse the message.
  if (!message->MergeFromCodedStream(&input)) return false;
  if (!input.ConsumedEntireMessage()) return false;

  // Release the limit.
  input.PopLimit(limit);

  return true;
}

我的问题是,我需要根据邮件中包含的uint32_t字段对邮件进行分组处理并批量处理 - 让我们将其称为id

目前,我的主循环中有以下代码:

...
int infd = -1;
_sopen_s(&infd, argv[1], _O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD);

google::protobuf::io::ZeroCopyInputStream *input = 
    new google::protobuf::io::FileInputStream(infd);

std::vector<ProtoMessage> msgList;
bool readMore = true;

do {
    ProtoMessage msg;
    readMore = readNextMessage(input, msg, msgList);

    if (!msgList.empty()) {
        std::cout << "Processing Message Batch - ID: " << msgList[0].id();
        /* some processing done here */
    }
} while (readMore);

readNextMessage()的实施如下:

bool readNextMessage(
    google::protobuf::io::ZeroCopyInputStream* rawInput,
    ProtoMessage& nextMsg,
    std::vector<ProtoMessage>& batchList) {

    bool sameBatch = false;
    uint32_t msgID = 0;
    do {
        if (readDelimitedFrom(rawInput, &scan) == -1)
            return false;
        if (nextMsg.id() == 0)
            msgID = nextMsg.id();    // guaranteed to be non-zero
        if (sameBatch = (msgID == nextMsg.id()))
            batchList.push_back(nextMsg); 
    } while (sameBatch); 

    // need a way to roll-back here as nextMsg is now the first new
    // ProtoMessage belonging to a new batch.

    return true;
}

此功能的逻辑非常简单:使用ZeroCopyInputStream并使用readDelimitedFrom()将其解析为基于ProtoMessage字段将id条消息组合到一个向量中。如果遇到带有新ID的消息,请停止并将控制权返回main以便对消息批处理进行处理。

这导致不必要地消费/读取属于前一批次的第一条消息(包括其Varint32编码的大小),而没有办法使用&#39;备份&#39;流。我希望能够将ZeroCopyInputStream指向上一个readDelimitedFrom()之前的位置。

我有没有办法修改readDelimitedFrom()以返回其调用期间消耗的字节数,然后在ZeroCopyInputStream上使用指针算法来实现所需的功能?

提供的函数ZeroCopyInputStream::Backup()具有ZeroCopyInputStream::Next()是最后一个方法调用的前提条件。显然,使用CodedInputStream包装器来解析分隔的消息时并非如此。

1 个答案:

答案 0 :(得分:1)

ZeroCopyInputStream::Backup()只能备份收到的最后一个缓冲区。单个消息可能跨越多个缓冲区,因此在给定ZeroCopyInputStream接口的情况下,没有通用的方法可以执行您想要的操作。

一些选项:

  • 在解析每条消息之前调用rawInput->ByteCount(),以便确切地确定消息开始的字节位置。如果需要回滚,请向后搜索基础文件并在其上重新创建ZeroCopyInputStream。这只适用于您正在阅读文件的情况。
  • 当您在新批次中遇到消息时,将其存储在侧面,然后在呼叫者要求开始阅读下一批时将其退回。