我想在LevelDB中存储一些Capnproto结构,所以我必须将它序列化为字符串,然后将其从std :: string反序列化。目前,我使用以下内容(改编自此处:https://groups.google.com/forum/#!msg/capnproto/viZXnQ5iN50/B-hSgZ1yLWUJ):
capnp::MallocMessageBuilder message;
WortData::Builder twort = message.initRoot<WortData>();
twort.setWid(1234);
twort.setW("Blabliblub");
kj::Array<capnp::word> dataArr = capnp::messageToFlatArray(message);
kj::ArrayPtr<kj::byte> bytes = dataArr.asBytes();
std::string data(bytes.begin(), bytes.end());
std::cout << data << std::endl;
const kj::ArrayPtr<const capnp::word> view(
reinterpret_cast<const capnp::word*>(&(*std::begin(data))),
reinterpret_cast<const capnp::word*>(&(*std::end(data))));
capnp::FlatArrayMessageReader message2(view);
WortData::Reader wortRestore = message2.getRoot<WortData>();
std::cout << wortRestore.getWid() << " " << std::string(wortRestore.getW()) << std::endl;
它基本上有效,但上面链接中的人不确定这种方法是否会在以后引起错误,而且由于讨论相当陈旧,我想问一下是否有更好的方法。
最后有人说“使用memcpy!”,但我不确定这是否有用,以及如何使用FlatArrayMessageReader
所需的数组类型。
提前致谢!
dvs23
更新:
我尝试实现与字对齐相关的建议:
capnp::MallocMessageBuilder message;
WortData::Builder twort = message.initRoot<WortData>();
twort.setWid(1234);
twort.setW("Blabliblub");
kj::Array<capnp::word> dataArr = capnp::messageToFlatArray(message);
kj::ArrayPtr<kj::byte> bytes = dataArr.asBytes();
std::string data(bytes.begin(), bytes.end());
std::cout << data << std::endl;
if(reinterpret_cast<uintptr_t>(data.data()) % sizeof(void*) == 0) {
const kj::ArrayPtr<const capnp::word> view(
reinterpret_cast<const capnp::word*>(&(*std::begin(data))),
reinterpret_cast<const capnp::word*>(&(*std::end(data))));
capnp::FlatArrayMessageReader message2(view);
WortData::Reader wortRestore = message2.getRoot<WortData>();
std::cout << wortRestore.getWid() << " " << std::string(wortRestore.getW()) << std::endl;
}
else {
size_t numWords = data.size() / sizeof(capnp::word);
if(data.size() % sizeof(capnp::word) != 0) {
numWords++;
std::cout << "Something wrong here..." << std::endl;
}
std::cout << sizeof(capnp::word) << " " << numWords << " " << data.size() << std::endl;
capnp::word dataWords[numWords];
std::memcpy(dataWords, data.data(), data.size());
kj::ArrayPtr<capnp::word> dataWordsPtr(dataWords, dataWords + numWords);
capnp::FlatArrayMessageReader message2(dataWordsPtr);
WortData::Reader wortRestore = message2.getRoot<WortData>();
std::cout << wortRestore.getWid() << " " << std::string(wortRestore.getW()) << std::endl;
}
答案 0 :(得分:1)
据我所知,链接的对话仍然准确无误。 (该主题上的大多数消息都是我,我是Cap'n Proto的作者......)
支持任何std::string
的缓冲区很可能在实践中是字对齐的 - 但不能保证。从std::string
读取时,您应该检查指针是否对齐(例如,通过reinterpret_cast<uintptr_t>(str.data()) % sizeof(void*) == 0
)。如果对齐,您可以reinterpret_cast
指向capnp::word*
的指针。如果没有对齐,您需要复制。实际上,代码可能永远不会复制,因为std::string
的后备缓冲区可能始终是对齐的。
在写作结束时,避免复制是比较棘手的。你编写的代码实际上是两份副本。
一个人在这里:
kj::Array<capnp::word> dataArr = capnp::messageToFlatArray(message);
一个人在这里:
std::string data(bytes.begin(), bytes.end());
看起来LevelDB支持一种名为Slice
的类型,在编写时可以使用它而不是std::string
,以避免第二个副本:
leveldb::Slice data(bytes.begin(), bytes.size());
这将引用底层字节而不是复制,并且应该可以在所有LevelDB写入函数中使用。
不幸的是,这里一个副本是不可避免的,因为LevelDB希望该值是一个连续的字节数组,而Cap'n Proto消息可以分成多个段。避免这种情况的唯一方法是让LevelDB添加对“收集写入”的支持。