From 13f7d58569776b82cfce7f772768973325d878ff Mon Sep 17 00:00:00 2001 From: Paul Elder Date: Sat, 5 Dec 2020 19:30:48 +0900 Subject: libcamera: Add IPADataSerializer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an IPADataSerializer which implements (de)serialization of built-in (PODs, vector, map, string) and libcamera data structures. This is intended to be used by the proxy and the proxy worker in the IPC layer. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Acked-by: Niklas Söderlund --- src/libcamera/ipa_data_serializer.cpp | 615 ++++++++++++++++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 src/libcamera/ipa_data_serializer.cpp (limited to 'src/libcamera/ipa_data_serializer.cpp') diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp new file mode 100644 index 00000000..131b3bd6 --- /dev/null +++ b/src/libcamera/ipa_data_serializer.cpp @@ -0,0 +1,615 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer + */ + +#include "libcamera/internal/ipa_data_serializer.h" + +#include "libcamera/internal/log.h" + +/** + * \file ipa_data_serializer.h + * \brief IPA Data Serializer + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPADataSerializer) + +/** + * \class IPADataSerializer + * \brief IPA Data Serializer + * + * Static template class that provides functions for serializing and + * deserializing IPA data. + * + * \todo Switch to Span instead of byte and fd vector + * + * \todo Harden the vector and map deserializer + * + * \todo For FileDescriptors, instead of storing a validity flag, store an + * index into the fd array. This will allow us to use views instead of copying. + */ + +namespace { + +/** + * \fn template void appendPOD(std::vector &vec, T val) + * \brief Append POD to end of byte vector, in little-endian order + * \tparam T Type of POD to append + * \param[in] vec Byte vector to append to + * \param[in] val Value to append + * + * This function is meant to be used by the IPA data serializer, and the + * generated IPA proxies. + */ + +/** + * \fn template T readPOD(std::vector::iterator it, size_t pos, + * std::vector::iterator end) + * \brief Read POD from byte vector, in little-endian order + * \tparam T Type of POD to read + * \param[in] it Iterator of byte vector to read from + * \param[in] pos Index in byte vector to read from + * \param[in] end Iterator marking end of byte vector + * + * This function is meant to be used by the IPA data serializer, and the + * generated IPA proxies. + * + * If the \a pos plus the byte-width of the desired POD is past \a end, it is + * a fata error will occur, as it means there is insufficient data for + * deserialization, which should never happen. + * + * \return The POD read from \a it at index \a pos + */ + +/** + * \fn template T readPOD(std::vector &vec, size_t pos) + * \brief Read POD from byte vector, in little-endian order + * \tparam T Type of POD to read + * \param[in] vec Byte vector to read from + * \param[in] pos Index in vec to start reading from + * + * This function is meant to be used by the IPA data serializer, and the + * generated IPA proxies. + * + * If the \a pos plus the byte-width of the desired POD is past the end of + * \a vec, a fatal error will occur, as it means there is insufficient data + * for deserialization, which should never happen. + * + * \return The POD read from \a vec at index \a pos + */ + +} /* namespace */ + +/** + * \fn template IPADataSerializer::serialize( + * T data, + * ControlSerializer *cs = nullptr) + * \brief Serialize an object into byte vector and fd vector + * \tparam T Type of object to serialize + * \param[in] data Object to serialize + * \param[in] cs ControlSerializer + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return Tuple of byte vector and fd vector, that is the serialized form + * of \a data + */ + +/** + * \fn template IPADataSerializer::deserialize( + * const std::vector &data, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector into an object + * \tparam T Type of object to deserialize to + * \param[in] data Byte vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() can be used if the object type \a T and its + * members don't have any FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector::const_iterator dataBegin, + * std::vector::const_iterator dataEnd, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector into an object + * \tparam T Type of object to deserialize to + * \param[in] dataBegin Begin iterator of byte vector to deserialize from + * \param[in] dataEnd End iterator of byte vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() can be used if the object type \a T and its + * members don't have any FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * const std::vector &data, + * const std::vector &fds, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector and fd vector into an object + * \tparam T Type of object to deserialize to + * \param[in] data Byte vector to deserialize from + * \param[in] fds Fd vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() (or the iterator version) must be used if + * the object type \a T or its members contain FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector::const_iterator dataBegin, + * std::vector::const_iterator dataEnd, + * std::vector::const_iterator fdsBegin, + * std::vector::const_iterator fdsEnd, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector and fd vector into an object + * \tparam T Type of object to deserialize to + * \param[in] dataBegin Begin iterator of byte vector to deserialize from + * \param[in] dataEnd End iterator of byte vector to deserialize from + * \param[in] fdsBegin Begin iterator of fd vector to deserialize from + * \param[in] fdsEnd End iterator of fd vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() (or the vector version) must be used if + * the object type \a T or its members contain FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +#ifndef __DOXYGEN__ + +#define DEFINE_POD_SERIALIZER(type) \ + \ +template<> \ +std::tuple, std::vector> \ +IPADataSerializer::serialize(const type &data, \ + [[maybe_unused]] ControlSerializer *cs) \ +{ \ + std::vector dataVec; \ + dataVec.reserve(sizeof(type)); \ + appendPOD(dataVec, data); \ + \ + return { dataVec, {} }; \ +} \ + \ +template<> \ +type IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, \ + std::vector::const_iterator dataEnd, \ + [[maybe_unused]] ControlSerializer *cs) \ +{ \ + return readPOD(dataBegin, 0, dataEnd); \ +} \ + \ +template<> \ +type IPADataSerializer::deserialize(const std::vector &data, \ + ControlSerializer *cs) \ +{ \ + return deserialize(data.cbegin(), data.end(), cs); \ +} \ + \ +template<> \ +type IPADataSerializer::deserialize(const std::vector &data, \ + [[maybe_unused]] const std::vector &fds, \ + ControlSerializer *cs) \ +{ \ + return deserialize(data.cbegin(), data.end(), cs); \ +} \ + \ +template<> \ +type IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, \ + std::vector::const_iterator dataEnd, \ + [[maybe_unused]] std::vector::const_iterator fdsBegin, \ + [[maybe_unused]] std::vector::const_iterator fdsEnd, \ + ControlSerializer *cs) \ +{ \ + return deserialize(dataBegin, dataEnd, cs); \ +} + +DEFINE_POD_SERIALIZER(bool) +DEFINE_POD_SERIALIZER(uint8_t) +DEFINE_POD_SERIALIZER(uint16_t) +DEFINE_POD_SERIALIZER(uint32_t) +DEFINE_POD_SERIALIZER(uint64_t) +DEFINE_POD_SERIALIZER(int8_t) +DEFINE_POD_SERIALIZER(int16_t) +DEFINE_POD_SERIALIZER(int32_t) +DEFINE_POD_SERIALIZER(int64_t) +DEFINE_POD_SERIALIZER(float) +DEFINE_POD_SERIALIZER(double) + +/* + * Strings are serialized simply by converting by {string.cbegin(), string.end()}. + * The size of the string is recorded by the container (struct, vector, map, or + * function parameter serdes). + */ +template<> +std::tuple, std::vector> +IPADataSerializer::serialize(const std::string &data, + [[maybe_unused]] ControlSerializer *cs) +{ + return { { data.cbegin(), data.end() }, {} }; +} + +template<> +std::string +IPADataSerializer::deserialize(const std::vector &data, + [[maybe_unused]] ControlSerializer *cs) +{ + return { data.cbegin(), data.cend() }; +} + +template<> +std::string +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + [[maybe_unused]] ControlSerializer *cs) +{ + return { dataBegin, dataEnd }; +} + +template<> +std::string +IPADataSerializer::deserialize(const std::vector &data, + [[maybe_unused]] const std::vector &fds, + [[maybe_unused]] ControlSerializer *cs) +{ + return { data.cbegin(), data.cend() }; +} + +template<> +std::string +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + [[maybe_unused]] std::vector::const_iterator fdsBegin, + [[maybe_unused]] std::vector::const_iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs) +{ + return { dataBegin, dataEnd }; +} + +/* + * ControlList is serialized as: + * + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes + * 4 bytes - uint32_t Size of serialized ControlList, in bytes + * X bytes - Serialized ControlInfoMap (using ControlSerializer) + * X bytes - Serialized ControlList (using ControlSerializer) + * + * If data.infoMap() is nullptr, then the default controls::controls will + * be used. The serialized ControlInfoMap will have zero length. + */ +template<> +std::tuple, std::vector> +IPADataSerializer::serialize(const ControlList &data, ControlSerializer *cs) +{ + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for serialization of ControlList"; + + size_t size; + std::vector infoData; + int ret; + + /* + * \todo Revisit this opportunistic serialization of the + * ControlInfoMap, as it could be fragile + */ + if (data.infoMap() && !cs->isCached(*data.infoMap())) { + size = cs->binarySize(*data.infoMap()); + infoData.resize(size); + ByteStreamBuffer buffer(infoData.data(), infoData.size()); + ret = cs->serialize(*data.infoMap(), buffer); + + if (ret < 0 || buffer.overflow()) { + LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap"; + return { {}, {} }; + } + } + + size = cs->binarySize(data); + std::vector listData(size); + ByteStreamBuffer buffer(listData.data(), listData.size()); + ret = cs->serialize(data, buffer); + + if (ret < 0 || buffer.overflow()) { + LOG(IPADataSerializer, Error) << "Failed to serialize ControlList"; + return { {}, {} }; + } + + std::vector dataVec; + dataVec.reserve(8 + infoData.size() + listData.size()); + appendPOD(dataVec, infoData.size()); + appendPOD(dataVec, listData.size()); + dataVec.insert(dataVec.end(), infoData.begin(), infoData.end()); + dataVec.insert(dataVec.end(), listData.begin(), listData.end()); + + return { dataVec, {} }; +} + +template<> +ControlList +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + ControlSerializer *cs) +{ + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlList"; + + if (std::distance(dataBegin, dataEnd) < 8) + return {}; + + uint32_t infoDataSize = readPOD(dataBegin, 0, dataEnd); + uint32_t listDataSize = readPOD(dataBegin, 4, dataEnd); + + std::vector::const_iterator it = dataBegin + 8; + + if (infoDataSize + listDataSize < infoDataSize || + static_cast(std::distance(it, dataEnd)) < infoDataSize + listDataSize) + return {}; + + if (infoDataSize > 0) { + ByteStreamBuffer buffer(&*it, infoDataSize); + ControlInfoMap map = cs->deserialize(buffer); + /* It's fine if map is empty. */ + if (buffer.overflow()) { + LOG(IPADataSerializer, Error) + << "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow"; + return ControlList(); + } + } + + it += infoDataSize; + ByteStreamBuffer buffer(&*it, listDataSize); + ControlList list = cs->deserialize(buffer); + if (buffer.overflow()) + LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow"; + + return list; +} + +template<> +ControlList +IPADataSerializer::deserialize(const std::vector &data, + ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), cs); +} + +template<> +ControlList +IPADataSerializer::deserialize(const std::vector &data, + [[maybe_unused]] const std::vector &fds, + ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), cs); +} + +template<> +ControlList +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + [[maybe_unused]] std::vector::const_iterator fdsBegin, + [[maybe_unused]] std::vector::const_iterator fdsEnd, + ControlSerializer *cs) +{ + return deserialize(dataBegin, dataEnd, cs); +} + +/* + * const ControlInfoMap is serialized as: + * + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes + * X bytes - Serialized ControlInfoMap (using ControlSerializer) + */ +template<> +std::tuple, std::vector> +IPADataSerializer::serialize(const ControlInfoMap &map, + ControlSerializer *cs) +{ + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for serialization of ControlInfoMap"; + + size_t size = cs->binarySize(map); + std::vector infoData(size); + ByteStreamBuffer buffer(infoData.data(), infoData.size()); + int ret = cs->serialize(map, buffer); + + if (ret < 0 || buffer.overflow()) { + LOG(IPADataSerializer, Error) << "Failed to serialize ControlInfoMap"; + return { {}, {} }; + } + + std::vector dataVec; + appendPOD(dataVec, infoData.size()); + dataVec.insert(dataVec.end(), infoData.begin(), infoData.end()); + + return { dataVec, {} }; +} + +template<> +ControlInfoMap +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + ControlSerializer *cs) +{ + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlInfoMap"; + + if (std::distance(dataBegin, dataEnd) < 4) + return {}; + + uint32_t infoDataSize = readPOD(dataBegin, 0, dataEnd); + + std::vector::const_iterator it = dataBegin + 4; + + if (static_cast(std::distance(it, dataEnd)) < infoDataSize) + return {}; + + ByteStreamBuffer buffer(&*it, infoDataSize); + ControlInfoMap map = cs->deserialize(buffer); + + return map; +} + +template<> +ControlInfoMap +IPADataSerializer::deserialize(const std::vector &data, + ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), cs); +} + +template<> +ControlInfoMap +IPADataSerializer::deserialize(const std::vector &data, + [[maybe_unused]] const std::vector &fds, + ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), cs); +} + +template<> +ControlInfoMap +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + [[maybe_unused]] std::vector::const_iterator fdsBegin, + [[maybe_unused]] std::vector::const_iterator fdsEnd, + ControlSerializer *cs) +{ + return deserialize(dataBegin, dataEnd, cs); +} + +/* + * FileDescriptors are serialized into a single byte that tells if the + * FileDescriptor is valid or not. If it is valid, then for serialization + * the fd will be written to the fd vector, or for deserialization the + * fd vector const_iterator will be valid. + * + * This validity is necessary so that we don't send -1 fd over sendmsg(). It + * also allows us to simply send the entire fd vector into the deserializer + * and it will be recursively consumed as necessary. + * + * \todo Consider serializing the FileDescriptor in 4 bytes to ensure + * 32-bit alignment of all serialized data + */ +template<> +std::tuple, std::vector> +IPADataSerializer::serialize(const FileDescriptor &data, + [[maybe_unused]] ControlSerializer *cs) +{ + std::vector dataVec = { data.isValid() }; + std::vector fdVec; + if (data.isValid()) + fdVec.push_back(data.fd()); + + return { dataVec, fdVec }; +} + +template<> +FileDescriptor IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + std::vector::const_iterator fdsBegin, + std::vector::const_iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs) +{ + ASSERT(std::distance(dataBegin, dataEnd) >= 1); + + bool valid = !!(*dataBegin); + + ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1)); + + return valid ? FileDescriptor(*fdsBegin) : FileDescriptor(); +} + +template<> +FileDescriptor IPADataSerializer::deserialize(const std::vector &data, + const std::vector &fds, + [[maybe_unused]] ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end()); +} + +/* + * FrameBuffer::Plane is serialized as: + * + * 1 byte - FileDescriptor + * 4 bytes - uint32_t Length + */ +template<> +std::tuple, std::vector> +IPADataSerializer::serialize(const FrameBuffer::Plane &data, + [[maybe_unused]] ControlSerializer *cs) +{ + std::vector dataVec; + std::vector fdsVec; + + std::vector fdBuf; + std::vector fdFds; + std::tie(fdBuf, fdFds) = + IPADataSerializer::serialize(data.fd); + dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end()); + fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end()); + + appendPOD(dataVec, data.length); + + return { dataVec, fdsVec }; +} + +template<> +FrameBuffer::Plane +IPADataSerializer::deserialize(std::vector::const_iterator dataBegin, + std::vector::const_iterator dataEnd, + std::vector::const_iterator fdsBegin, + [[maybe_unused]] std::vector::const_iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs) +{ + FrameBuffer::Plane ret; + + ret.fd = IPADataSerializer::deserialize(dataBegin, dataBegin + 1, + fdsBegin, fdsBegin + 1); + ret.length = readPOD(dataBegin, 1, dataEnd); + + return ret; +} + +template<> +FrameBuffer::Plane +IPADataSerializer::deserialize(const std::vector &data, + const std::vector &fds, + ControlSerializer *cs) +{ + return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs); +} + +#endif /* __DOXYGEN__ */ + +} /* namespace libcamera */ -- cgit v1.2.1