Cap’n Proto — де- / сериализация структуры в / из std :: string для хранения в LevelDB

Я хочу сохранить некоторую структуру Capnproto в LevelDB, поэтому я должен сериализовать ее в строку и затем десериализовать обратно из 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

Решение

Связанный разговор все еще точен, насколько мне известно. (Большинство сообщений в этой теме — это я, и я автор 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 поддержку «сбора записей».

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector