#ifndef COMMON_SERIALIZATION_HPP
#define COMMON_SERIALIZATION_HPP
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <type_traits>
#include "common/utils.hpp"
namespace dnnl {
namespace impl {
#define DNNL_ASSERT_TRIVIALLY_SERIALIZABLE(cls) \
static_assert(serialization_stream_t::is_trivially_serialized<cls>::value, \
#cls " must be trivially serializable.")
struct serialization_stream_t {
serialization_stream_t() = default;
template <typename Arg1, typename... Args>
serialization_stream_t(const Arg1 &a1, const Args &...args) {
append(a1, args...);
}
static serialization_stream_t from_data(std::vector<uint8_t> data) {
serialization_stream_t s;
s.data_ = std::move(data);
return s;
}
bool operator==(const serialization_stream_t &other) const {
return data_ == other.data_;
}
#if defined(__cpp_lib_has_unique_object_representations) \
&& __cpp_lib_has_unique_object_representations >= 201606L
template <typename T>
struct is_trivially_serialized {
static const bool value
= (std::has_unique_object_representations<T>::value
|| std::is_floating_point<T>::value)
&& !(std::is_pointer<T>::value);
};
#else
template <typename T>
struct is_trivially_serialized {
static const bool value = std::is_trivially_copyable<T>::value
&& !(std::is_pointer<T>::value);
};
#endif
template <typename T, typename = void>
struct has_serialize_t {
static const bool value = false;
};
template <typename T>
struct has_serialize_t<T,
decltype(std::declval<T>().serialize(
std::declval<serialization_stream_t &>()))> {
static const bool value = true;
};
template <typename T,
utils::enable_if_t<has_serialize_t<T>::value, bool> = true>
void append(const T &t) {
t.serialize(*this);
}
template <typename T,
utils::enable_if_t<is_trivially_serialized<T>::value
&& !has_serialize_t<T>::value,
bool>
= true>
void append(const T &t) {
std::array<uint8_t, sizeof(T)> type_data;
std::memcpy(type_data.data(), &t, sizeof(T));
data_.insert(data_.end(), type_data.begin(), type_data.end());
}
template <typename T,
utils::enable_if_t<utils::is_vector<T>::value, bool> = true>
void append(const T &v) {
append(v.size());
for (const typename T::value_type &d : v)
append<typename T::value_type>(d);
}
template <typename Arg1, typename Arg2, typename... Args>
void append(const Arg1 &a1, const Arg2 &a2, const Args &...args) {
append(a1);
append(a2, args...);
}
template <typename T,
utils::enable_if_t<is_trivially_serialized<T>::value, bool> = true>
void append_array(size_t size, const T *ptr) {
append(size);
const auto *p = reinterpret_cast<const uint8_t *>(ptr);
data_.insert(data_.end(), p, p + sizeof(T) * size);
}
template <typename T,
utils::enable_if_t<is_trivially_serialized<T>::value, bool> = true>
T get(size_t idx) const {
T t {};
if (data_.size() < idx + sizeof(T)) {
assert(!"unexpected");
return t;
}
std::memcpy(&t, &data_[idx], sizeof(T));
return t;
}
void get(size_t idx, size_t size, uint8_t *ptr) const {
if (data_.size() < idx + size) {
assert(!"unexpected");
return;
}
std::memcpy(ptr, &data_[idx], size);
}
size_t get_hash() const { return hash_range(data_.data(), data_.size()); }
template <typename T>
static size_t get_hash(const T &t) {
return serialization_stream_t(t).get_hash();
}
std::string str() {
ostringstream_t oss;
oss << std::hex << std::setfill('0');
for (auto c : data_) {
oss << std::setw(2) << static_cast<uint32_t>(c);
}
return oss.str();
}
bool empty() const { return data_.empty(); }
const std::vector<uint8_t> &get_data() const { return data_; }
private:
static size_t hash_range(const uint8_t *v, size_t size) {
size_t seed = 0;
const uint8_t *end = v + size;
for (; v < end; v += sizeof(seed)) {
size_t value = 0;
std::memcpy(&value, v,
std::min(static_cast<size_t>(end - v), sizeof(seed)));
seed = hash_combine(seed, value);
}
return seed;
}
std::vector<uint8_t> data_;
};
struct deserializer_t {
deserializer_t(const serialization_stream_t &sstream)
: idx_(0), sstream_(sstream) {}
template <typename T>
struct has_deserialize_t {
using yes_t = uint8_t;
using no_t = uint16_t;
template <typename U>
static yes_t test(
utils::enable_if_t<std::is_same<decltype(&U::deserialize),
U (*)(deserializer_t &)>::value,
bool>);
template <typename U>
static no_t test(...);
static const bool value = (sizeof(test<T>(0)) == sizeof(yes_t));
};
template <typename T,
utils::enable_if_t<has_deserialize_t<T>::value, bool> = true>
void pop(T &t) {
t = T::deserialize(*this);
}
template <typename T,
utils::enable_if_t<has_deserialize_t<T>::value, bool> = true>
T pop() {
return T::deserialize(*this);
}
template <typename T,
utils::enable_if_t<
serialization_stream_t::is_trivially_serialized<T>::value
&& !has_deserialize_t<T>::value,
bool>
= true>
void pop(T &t) {
t = sstream_.get<T>(idx_);
idx_ += sizeof(T);
}
template <typename T,
utils::enable_if_t<
serialization_stream_t::is_trivially_serialized<T>::value
&& !has_deserialize_t<T>::value,
bool>
= true>
T pop() {
auto idx_start = idx_;
idx_ += sizeof(T);
return sstream_.get<T>(idx_start);
}
template <typename T,
utils::enable_if_t<utils::is_vector<T>::value, bool> = true>
void pop(T &v) {
size_t size;
pop(size);
v.clear();
v.reserve(size);
for (size_t i = 0; i < size; i++) {
typename T::value_type t = {};
pop(t);
v.emplace_back(t);
}
}
template <typename T,
utils::enable_if_t<
serialization_stream_t::is_trivially_serialized<T>::value,
bool>
= true>
void pop_array(size_t &size, T *ptr) {
pop(size);
sstream_.get(idx_, sizeof(T) * size, reinterpret_cast<uint8_t *>(ptr));
idx_ += sizeof(T) * size;
}
bool empty() const { return idx_ >= sstream_.get_data().size(); }
private:
size_t idx_ = 0;
const serialization_stream_t &sstream_;
};
struct trivially_serializable_base_t {};
template <typename T>
struct trivially_serializable_t : public trivially_serializable_base_t {
serialization_stream_t serialize() const {
DNNL_ASSERT_TRIVIALLY_SERIALIZABLE(T);
return serialization_stream_t(*static_cast<const T *>(this));
}
static T deserialize(const serialization_stream_t &s) {
return deserializer_t(s).pop<T>();
}
};
} }
#endif