#pragma once #include #include #include #include #include namespace { // Trivial type template , bool> = true> std::vector encode_helper(const T& val) { auto begin = reinterpret_cast(&val); auto end = begin + sizeof(val); return {begin, end}; } // Container type template , bool> = true> std::vector encode_helper(const Container& val) { // Check comment in decode_helper below static_assert(std::is_trivially_copyable_v, "Can encode only a containers of trivial types currently"); constexpr auto member_size = sizeof(typename Container::value_type); auto n_bytes = member_size * val.size(); std::vector out(n_bytes); std::memcpy(out.data(), val.data(), n_bytes); return out; } // Trivial type template , bool> = true> std::optional decode_helper(const std::vector& bytes) { T t; if (sizeof(t) != bytes.size()) { return {}; } std::memcpy(&t, bytes.data(), bytes.size()); return t; } // Container type template , bool> = true> std::optional decode_helper(const std::vector& bytes) { Container t; size_t member_size = sizeof(typename Container::value_type); // NOTE: This can only reconstruct container of trivial types, not a // container of non-trivial types. We can either use a standard serializer // (like protobuf) or roll one of our own simple ones (like prepending size // of the object), but have to be careful about securing such a serializer. // But, do we even need that? I do not see any metadata which is either not // trivial or a container of trivial type. size_t to_copy = bytes.size(); if (to_copy % member_size != 0) { return {}; } size_t members = to_copy / member_size; t.resize(members); std::memcpy(t.data(), bytes.data(), to_copy); return t; } } // namespace namespace pixel::graphics::utils { // TODO: Setup a fuzzer for encode/decode template std::vector encode(const T& val) { return encode_helper(val); } template std::optional decode(const std::vector& bytes) { return decode_helper(bytes); } } // namespace pixel::graphics::utils