Cap'n Proto - De-/Serialize struct to/from std::string 用于存储在 LevelDB 中
Cap'n Proto - De-/Serialize struct to/from std::string for storing in LevelDB
我想在 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;
基本上是可行的,但是上面link的人不确定这种做法以后会不会出错,而且讨论的时间很长,我想问问有没有更好的方法。
最后有人说了 "use 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;
}
据我所知,链接的对话仍然准确。 (该线程上的大多数消息都是我,我是 Cap'n Proto 的作者...)
支持任何 std::string
的缓冲区在实践中很可能是 word-aligned -- 但不能保证。从 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 添加对 "gather writes".
的支持
我想在 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;
基本上是可行的,但是上面link的人不确定这种做法以后会不会出错,而且讨论的时间很长,我想问问有没有更好的方法。
最后有人说了 "use 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;
}
据我所知,链接的对话仍然准确。 (该线程上的大多数消息都是我,我是 Cap'n Proto 的作者...)
支持任何 std::string
的缓冲区在实践中很可能是 word-aligned -- 但不能保证。从 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 添加对 "gather writes".
的支持