Improve SLK

Summary:
SLK now correctly handles different CPU architectures (BIG/little endian).
Also, more string encoding functions have been added.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2806
This commit is contained in:
Matej Ferencevic 2020-05-26 12:45:07 +02:00
parent 1513a455de
commit aaf0c1ca08
3 changed files with 66 additions and 11 deletions

View File

@ -10,12 +10,15 @@
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include "slk/streams.hpp"
#include "utils/cast.hpp"
#include "utils/endian.hpp"
#include "utils/exceptions.hpp"
// The namespace name stands for SaveLoadKit. It should be not mistaken for the
@ -90,10 +93,11 @@ void Load(
// Implementation of serialization for primitive types.
#define MAKE_PRIMITIVE_SAVE(primitive_type) \
inline void Save(primitive_type obj, Builder *builder) { \
builder->Save(reinterpret_cast<const uint8_t *>(&obj), \
sizeof(primitive_type)); \
#define MAKE_PRIMITIVE_SAVE(primitive_type) \
inline void Save(primitive_type obj, Builder *builder) { \
primitive_type obj_encoded = utils::HostToLittleEndian(obj); \
builder->Save(reinterpret_cast<const uint8_t *>(&obj_encoded), \
sizeof(primitive_type)); \
}
MAKE_PRIMITIVE_SAVE(bool)
@ -106,14 +110,15 @@ MAKE_PRIMITIVE_SAVE(int32_t)
MAKE_PRIMITIVE_SAVE(uint32_t)
MAKE_PRIMITIVE_SAVE(int64_t)
MAKE_PRIMITIVE_SAVE(uint64_t)
MAKE_PRIMITIVE_SAVE(float)
MAKE_PRIMITIVE_SAVE(double)
#undef MAKE_PRIMITIVE_SAVE
#define MAKE_PRIMITIVE_LOAD(primitive_type) \
inline void Load(primitive_type *obj, Reader *reader) { \
reader->Load(reinterpret_cast<uint8_t *>(obj), sizeof(primitive_type)); \
#define MAKE_PRIMITIVE_LOAD(primitive_type) \
inline void Load(primitive_type *obj, Reader *reader) { \
primitive_type obj_encoded; \
reader->Load(reinterpret_cast<uint8_t *>(&obj_encoded), \
sizeof(primitive_type)); \
*obj = utils::LittleEndianToHost(obj_encoded); \
}
MAKE_PRIMITIVE_LOAD(bool)
@ -126,11 +131,29 @@ MAKE_PRIMITIVE_LOAD(int32_t)
MAKE_PRIMITIVE_LOAD(uint32_t)
MAKE_PRIMITIVE_LOAD(int64_t)
MAKE_PRIMITIVE_LOAD(uint64_t)
MAKE_PRIMITIVE_LOAD(float)
MAKE_PRIMITIVE_LOAD(double)
#undef MAKE_PRIMITIVE_LOAD
inline void Save(float obj, Builder *builder) {
slk::Save(utils::MemcpyCast<uint32_t>(obj), builder);
}
inline void Save(double obj, Builder *builder) {
slk::Save(utils::MemcpyCast<uint64_t>(obj), builder);
}
inline void Load(float *obj, Reader *reader) {
uint32_t obj_encoded;
slk::Load(&obj_encoded, reader);
*obj = utils::MemcpyCast<float>(obj_encoded);
}
inline void Load(double *obj, Reader *reader) {
uint64_t obj_encoded;
slk::Load(&obj_encoded, reader);
*obj = utils::MemcpyCast<double>(obj_encoded);
}
// Implementation of serialization of complex types.
inline void Save(const std::string &obj, Builder *builder) {
@ -139,6 +162,18 @@ inline void Save(const std::string &obj, Builder *builder) {
builder->Save(reinterpret_cast<const uint8_t *>(obj.data()), size);
}
inline void Save(const char *obj, Builder *builder) {
uint64_t size = strlen(obj);
Save(size, builder);
builder->Save(reinterpret_cast<const uint8_t *>(obj), size);
}
inline void Save(const std::string_view &obj, Builder *builder) {
uint64_t size = obj.size();
Save(size, builder);
builder->Save(reinterpret_cast<const uint8_t *>(obj.data()), size);
}
inline void Load(std::string *obj, Reader *reader) {
uint64_t size = 0;
Load(&size, reader);

View File

@ -6,9 +6,11 @@
namespace utils {
inline uint8_t HostToLittleEndian(uint8_t value) { return value; }
inline uint16_t HostToLittleEndian(uint16_t value) { return htole16(value); }
inline uint32_t HostToLittleEndian(uint32_t value) { return htole32(value); }
inline uint64_t HostToLittleEndian(uint64_t value) { return htole64(value); }
inline int8_t HostToLittleEndian(int8_t value) { return value; }
inline int16_t HostToLittleEndian(int16_t value) {
return MemcpyCast<int16_t>(htole16(MemcpyCast<uint16_t>(value)));
}
@ -19,9 +21,11 @@ inline int64_t HostToLittleEndian(int64_t value) {
return MemcpyCast<int64_t>(htole64(MemcpyCast<uint64_t>(value)));
}
inline uint8_t LittleEndianToHost(uint8_t value) { return value; }
inline uint16_t LittleEndianToHost(uint16_t value) { return le16toh(value); }
inline uint32_t LittleEndianToHost(uint32_t value) { return le32toh(value); }
inline uint64_t LittleEndianToHost(uint64_t value) { return le64toh(value); }
inline int8_t LittleEndianToHost(int8_t value) { return value; }
inline int16_t LittleEndianToHost(int16_t value) {
return MemcpyCast<int16_t>(le16toh(MemcpyCast<uint16_t>(value)));
}
@ -32,9 +36,11 @@ inline int64_t LittleEndianToHost(int64_t value) {
return MemcpyCast<int64_t>(le64toh(MemcpyCast<uint64_t>(value)));
}
inline uint8_t HostToBigEndian(uint8_t value) { return value; }
inline uint16_t HostToBigEndian(uint16_t value) { return htobe16(value); }
inline uint32_t HostToBigEndian(uint32_t value) { return htobe32(value); }
inline uint64_t HostToBigEndian(uint64_t value) { return htobe64(value); }
inline int8_t HostToBigEndian(int8_t value) { return value; }
inline int16_t HostToBigEndian(int16_t value) {
return MemcpyCast<int16_t>(htobe16(MemcpyCast<uint16_t>(value)));
}
@ -45,9 +51,11 @@ inline int64_t HostToBigEndian(int64_t value) {
return MemcpyCast<int64_t>(htobe64(MemcpyCast<uint64_t>(value)));
}
inline uint8_t BigEndianToHost(uint8_t value) { return value; }
inline uint16_t BigEndianToHost(uint16_t value) { return be16toh(value); }
inline uint32_t BigEndianToHost(uint32_t value) { return be32toh(value); }
inline uint64_t BigEndianToHost(uint64_t value) { return be64toh(value); }
inline int8_t BigEndianToHost(int8_t value) { return value; }
inline int16_t BigEndianToHost(int16_t value) {
return MemcpyCast<int16_t>(be16toh(MemcpyCast<uint16_t>(value)));
}

View File

@ -44,6 +44,18 @@ TEST(SlkCore, String) {
ASSERT_EQ(loopback.size(), sizeof(uint64_t) + original.size());
}
TEST(SlkCore, ConstStringLiteral) {
const char *original = "hello world";
slk::Loopback loopback;
auto builder = loopback.GetBuilder();
slk::Save(original, builder);
std::string decoded;
auto reader = loopback.GetReader();
slk::Load(&decoded, reader);
ASSERT_EQ(original, decoded);
ASSERT_EQ(loopback.size(), sizeof(uint64_t) + strlen(original));
}
TEST(SlkCore, VectorPrimitive) {
std::vector<int> original{1, 2, 3, 4, 5};
slk::Loopback loopback;