/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*! * \file tvm/support/serializer.h * \brief Serializer specializations for tvm::support::Stream. * * Built-in support for: * - Arithmetic types (endian-aware) * - Enum types (via underlying arithmetic type) * - std::string, std::vector, std::pair, std::unordered_map * - DLDataType, DLDevice * * Custom types with Save(Stream*)/Load(Stream*) methods should define * Serializer specializations directly. */ #ifndef TVM_SUPPORT_SERIALIZER_H_ #define TVM_SUPPORT_SERIALIZER_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace tvm { namespace support { // ---- Arithmetic types ---- template struct Serializer>> { static constexpr bool enabled = true; static void Write(Stream* strm, const T& data) { if constexpr (TVM_FFI_IO_NO_ENDIAN_SWAP) { strm->Write(&data, sizeof(T)); } else { T copy = data; ffi::ByteSwap(©, sizeof(T), 1); strm->Write(©, sizeof(T)); } } static bool Read(Stream* strm, T* data) { bool ok = strm->Read(data, sizeof(T)) == sizeof(T); if constexpr (!TVM_FFI_IO_NO_ENDIAN_SWAP) { ffi::ByteSwap(data, sizeof(T), 1); } return ok; } }; // ---- Enum types (delegate to underlying arithmetic type) ---- template struct Serializer>> { static constexpr bool enabled = true; using U = std::underlying_type_t; static void Write(Stream* strm, const T& data) { Serializer::Write(strm, static_cast(data)); } static bool Read(Stream* strm, T* data) { U val; if (!Serializer::Read(strm, &val)) return false; *data = static_cast(val); return true; } }; // ---- std::string ---- template <> struct Serializer { static constexpr bool enabled = true; static void Write(Stream* strm, const std::string& data) { uint64_t sz = static_cast(data.size()); Serializer::Write(strm, sz); if (sz != 0) { strm->Write(data.data(), data.size()); } } static bool Read(Stream* strm, std::string* data) { uint64_t sz; if (!Serializer::Read(strm, &sz)) return false; data->resize(static_cast(sz)); if (sz != 0) { size_t nbytes = static_cast(sz); return strm->Read(&(*data)[0], nbytes) == nbytes; } return true; } }; // ---- std::vector ---- template struct Serializer> { static constexpr bool enabled = true; static void Write(Stream* strm, const std::vector& vec) { uint64_t sz = static_cast(vec.size()); Serializer::Write(strm, sz); if constexpr (std::is_arithmetic_v && TVM_FFI_IO_NO_ENDIAN_SWAP) { if (sz != 0) { strm->Write(vec.data(), sizeof(T) * vec.size()); } } else { for (const auto& v : vec) { Serializer::Write(strm, v); } } } static bool Read(Stream* strm, std::vector* vec) { uint64_t sz; if (!Serializer::Read(strm, &sz)) return false; vec->resize(static_cast(sz)); if constexpr (std::is_arithmetic_v && TVM_FFI_IO_NO_ENDIAN_SWAP) { if (sz != 0) { size_t nbytes = sizeof(T) * static_cast(sz); return strm->Read(vec->data(), nbytes) == nbytes; } return true; } else { for (size_t i = 0; i < static_cast(sz); ++i) { if (!Serializer::Read(strm, &(*vec)[i])) return false; } return true; } } }; // ---- std::pair ---- template struct Serializer> { static constexpr bool enabled = true; static void Write(Stream* strm, const std::pair& data) { Serializer::Write(strm, data.first); Serializer::Write(strm, data.second); } static bool Read(Stream* strm, std::pair* data) { return Serializer::Read(strm, &data->first) && Serializer::Read(strm, &data->second); } }; // ---- std::unordered_map ---- template struct Serializer> { static constexpr bool enabled = true; static void Write(Stream* strm, const std::unordered_map& data) { std::vector> vec(data.begin(), data.end()); Serializer>>::Write(strm, vec); } static bool Read(Stream* strm, std::unordered_map* data) { std::vector> vec; if (!Serializer>>::Read(strm, &vec)) return false; data->clear(); data->insert(vec.begin(), vec.end()); return true; } }; // ---- ffi::String ---- template <> struct Serializer { static constexpr bool enabled = true; static void Write(Stream* strm, const ffi::String& data) { uint64_t sz = static_cast(data.size()); Serializer::Write(strm, sz); if (sz != 0) { strm->Write(data.data(), data.size()); } } static bool Read(Stream* strm, ffi::String* data) { std::string s; if (!Serializer::Read(strm, &s)) return false; *data = ffi::String(std::move(s)); return true; } }; // ---- ffi::Bytes (binary payload, same wire format as std::string) ---- template <> struct Serializer { static constexpr bool enabled = true; static void Write(Stream* strm, const ffi::Bytes& data) { uint64_t sz = static_cast(data.size()); Serializer::Write(strm, sz); if (sz != 0) { strm->Write(data.data(), data.size()); } } static bool Read(Stream* strm, ffi::Bytes* data) { std::string s; if (!Serializer::Read(strm, &s)) return false; *data = ffi::Bytes(std::move(s)); return true; } }; // ---- ffi::Array (binary-compatible with std::vector) ---- template struct Serializer> { static constexpr bool enabled = true; static void Write(Stream* strm, const ffi::Array& arr) { uint64_t sz = static_cast(arr.size()); Serializer::Write(strm, sz); for (size_t i = 0; i < arr.size(); ++i) { Serializer::Write(strm, arr[i]); } } static bool Read(Stream* strm, ffi::Array* arr) { uint64_t sz; if (!Serializer::Read(strm, &sz)) return false; *arr = ffi::Array(); for (uint64_t i = 0; i < sz; ++i) { T val; if (!Serializer::Read(strm, &val)) return false; arr->push_back(std::move(val)); } return true; } }; // ---- ffi::Map (binary format: uint64_t count, then key/value pairs) ---- template struct Serializer> { static constexpr bool enabled = true; static void Write(Stream* strm, const ffi::Map& data) { uint64_t sz = static_cast(data.size()); Serializer::Write(strm, sz); for (const auto& kv : data) { Serializer::Write(strm, kv.first); Serializer::Write(strm, kv.second); } } static bool Read(Stream* strm, ffi::Map* data) { uint64_t sz; if (!Serializer::Read(strm, &sz)) return false; *data = ffi::Map(); for (uint64_t i = 0; i < sz; ++i) { K key; V val; if (!Serializer::Read(strm, &key)) return false; if (!Serializer::Read(strm, &val)) return false; data->Set(std::move(key), std::move(val)); } return true; } }; // ---- DLDataType ---- template <> struct Serializer { static constexpr bool enabled = true; static void Write(Stream* strm, const DLDataType& dtype) { Serializer::Write(strm, dtype.code); Serializer::Write(strm, dtype.bits); Serializer::Write(strm, dtype.lanes); } static bool Read(Stream* strm, DLDataType* dtype) { if (!Serializer::Read(strm, &dtype->code)) return false; if (!Serializer::Read(strm, &dtype->bits)) return false; if (!Serializer::Read(strm, &dtype->lanes)) return false; return true; } }; // ---- DLDevice ---- template <> struct Serializer { static constexpr bool enabled = true; static void Write(Stream* strm, const DLDevice& dev) { int32_t device_type = static_cast(dev.device_type); Serializer::Write(strm, device_type); Serializer::Write(strm, dev.device_id); } static bool Read(Stream* strm, DLDevice* dev) { int32_t device_type = 0; if (!Serializer::Read(strm, &device_type)) return false; dev->device_type = static_cast(device_type); if (!Serializer::Read(strm, &dev->device_id)) return false; return true; } }; } // namespace support } // namespace tvm #endif // TVM_SUPPORT_SERIALIZER_H_