[{"id":15117,"web_url":"https://patchwork.libcamera.org/comment/15117/","msgid":"<YCXe6WAX9ncOB4GI@pendragon.ideasonboard.com>","date":"2021-02-12T01:50:33","subject":"Re: [libcamera-devel] [PATCH v7 06/10] libcamera: Add\n\tIPADataSerializer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Thu, Feb 11, 2021 at 04:18:01PM +0900, Paul Elder wrote:\n> Add an IPADataSerializer which implements (de)serialization of built-in\n> (PODs, vector, map, string) and libcamera data structures. This is\n> intended to be used by the proxy and the proxy worker in the IPC layer.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Acked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> \n> ---\n> Changes in v7:\n> - move DEFINE_POD_SERIALIZER from header to cpp\n> - make readPOD and appendPOD more readable\n> - add some overflow checks\n> - drop unnecessary const casts\n> - add todo for supporting vectors/maps of references\n> - remove error message when ControlList is deserialized but it's empty\n>   - it's fine to send empty ControlLists\n> \n> Changes in v6:\n> - remove (de)serializers for IPASettings, CameraSensorInfo, IPAStream,\n>   and IPABuffer as they are now defined in core.mojom and generated\n> - ControlList (de)serializer no longer needs a ControlInfoMap\n> - ControlList (de)serializer now checks the ControlSerializer if the\n>   ControlList's ControlInfoMap is cached\n> - add some todos for optimization and hardening\n> \n> Changes in v5:\n> - fix style of: {{}, {}} -> { {}, {} }\n> - add documentation on serialization formats (not in doxygen, though)\n> - compress readPOD\n>   - use memcpy\n>   - use ASSERT\n> - remove ifdef DOXYGEN guard from base IPADataSerializer\n> - remove integer overflow risk when calculating vector offsets\n> - use const iterators and const references\n> - remove const ControlList and const ControlInfoMap specializations\n> \n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n> \n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n> \n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 660 ++++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         | 244 +++++++\n>  src/libcamera/meson.build                     |   1 +\n>  3 files changed, 905 insertions(+)\n>  create mode 100644 include/libcamera/internal/ipa_data_serializer.h\n>  create mode 100644 src/libcamera/ipa_data_serializer.cpp\n> \n> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\n> new file mode 100644\n> index 00000000..ebc32a9e\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,660 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <type_traits>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T,\n> +\t typename std::enable_if<std::is_arithmetic_v<T>> * = nullptr>\n\nShouldn't this be std::enable_if_t ? Same below.\n\n> +void appendPOD(std::vector<uint8_t> &vec, T val)\n> +{\n> +\tconstexpr size_t byteWidth = sizeof(val);\n> +\tvec.resize(vec.size() + byteWidth);\n> +\tmemcpy(&*(vec.end() - byteWidth), &val, byteWidth);\n> +}\n> +\n> +template<typename T,\n> +\t std::enable_if<std::is_arithmetic_v<T>> * = nullptr>\n> +T readPOD(std::vector<uint8_t>::const_iterator it, size_t pos,\n> +\t  std::vector<uint8_t>::const_iterator end)\n> +{\n> +\tASSERT(pos + it < end);\n> +\n> +\tT ret = 0;\n> +\tmemcpy(&ret, &(*(it + pos)), sizeof(ret));\n> +\n> +\treturn ret;\n> +}\n> +\n> +template<typename T,\n> +\t std::enable_if<std::is_arithmetic_v<T>> * = nullptr>\n> +T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> +{\n> +\treturn readPOD<T>(vec.cbegin(), pos, vec.end());\n> +}\n> +\n> +} /* namespace */\n> +\n> +template<typename T>\n> +class IPADataSerializer\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const T &data, ControlSerializer *cs = nullptr);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     ControlSerializer *cs = nullptr);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     ControlSerializer *cs = nullptr);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     const std::vector<int32_t> &fds,\n> +\t\t\t     ControlSerializer *cs = nullptr);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs = nullptr);\n> +};\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +/*\n> + * Serialization format for vector of type V:\n> + *\n> + * 4 bytes - uint32_t Length of vector, in number of elements\n> + *\n> + * For every element in the vector:\n> + *\n> + * 4 bytes - uint32_t Size of element, in bytes\n> + * 4 bytes - uint32_t Number of fds for the element\n> + * X bytes - Serialized element\n> + *\n> + * \\todo Support elements that are references\n> + */\n> +template<typename V>\n> +class IPADataSerializer<std::vector<V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t   dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +/*\n> + * Serialization format for map of key type K and value type V:\n> + *\n> + * 4 bytes - uint32_t Length of map, in number of pairs\n> + *\n> + * For every pair in the map:\n> + *\n> + * 4 bytes - uint32_t Size of key, in bytes\n> + * 4 bytes - uint32_t Number of fds for the key\n> + * X bytes - Serialized key\n> + * 4 bytes - uint32_t Size of value, in bytes\n> + * 4 bytes - uint32_t Number of fds for the value\n> + * X bytes - Serialized value\n> + *\n> + * \\todo Support keys or values that are references\n> + */\n> +template<typename K, typename V>\n> +class IPADataSerializer<std::map<K, V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({ key, value });\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n\nAll the specializations below should move to the .cpp file. Here one\nexample for the std::string specialization, the other ones should follow\nthe same pattern.\n\ndiff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\nindex ebc32a9e298c..cadd237cf7b0 100644\n--- a/include/libcamera/internal/ipa_data_serializer.h\n+++ b/include/libcamera/internal/ipa_data_serializer.h\n@@ -300,51 +300,6 @@ public:\n \t}\n };\n\n-/*\n- * Strings are serialized simply by converting by {string.cbegin(), string.end()}.\n- * The size of the string is recorded by the container (struct, vector, map, or\n- * function parameter serdes).\n- */\n-template<>\n-class IPADataSerializer<std::string>\n-{\n-public:\n-\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n-\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n-\t{\n-\t\treturn { { data.cbegin(), data.end() }, {} };\n-\t}\n-\n-\tstatic std::string deserialize(std::vector<uint8_t> &data,\n-\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n-\t{\n-\t\treturn { data.cbegin(), data.cend() };\n-\t}\n-\n-\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n-\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n-\t{\n-\t\treturn { dataBegin, dataEnd };\n-\t}\n-\n-\tstatic std::string deserialize(std::vector<uint8_t> &data,\n-\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n-\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n-\t{\n-\t\treturn { data.cbegin(), data.cend() };\n-\t}\n-\n-\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n-\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n-\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n-\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n-\t{\n-\t\treturn { dataBegin, dataEnd };\n-\t}\n-};\n-\n /*\n  * FileDescriptors are serialized into a single byte that tells if the\n  * FileDescriptor is valid or not. If it is valid, then for serialization\ndiff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\nindex e20682b94e15..61887a7c4a06 100644\n--- a/src/libcamera/ipa_data_serializer.cpp\n+++ b/src/libcamera/ipa_data_serializer.cpp\n@@ -182,6 +182,8 @@ namespace {\n  * \\return The deserialized object\n  */\n\n+#ifndef __DOXYGEN__\n+\n #define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n template<>\t\t\t\t\t\t\t\t\\\n@@ -241,4 +243,56 @@ DEFINE_POD_SERIALIZER(int64_t)\n DEFINE_POD_SERIALIZER(float)\n DEFINE_POD_SERIALIZER(double)\n\n+/*\n+ * Strings are serialized simply by converting by {string.cbegin(), string.end()}.\n+ * The size of the string is recorded by the container (struct, vector, map, or\n+ * function parameter serdes).\n+ */\n+template<>\n+std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+IPADataSerializer<std::string>::serialize(const std::string &data,\n+\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n+{\n+\treturn { { data.cbegin(), data.end() }, {} };\n+}\n+\n+template<>\n+std::string\n+IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n+{\n+\treturn { data.cbegin(), data.cend() };\n+}\n+\n+template<>\n+std::string\n+IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n+\t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n+{\n+\treturn { dataBegin, dataEnd };\n+}\n+\n+template<>\n+std::string\n+IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n+\t\t\t\t\t    [[maybe_unused]] const std::vector<int32_t> &fds,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n+{\n+\treturn { data.cbegin(), data.cend() };\n+}\n+\n+template<>\n+std::string\n+IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n+\t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n+{\n+\treturn { dataBegin, dataEnd };\n+}\n+\n+#endif /* __DOXYGEN__ */\n+\n } /* namespace libcamera */\n\n\n> +/*\n> + * Strings are serialized simply by converting by {string.cbegin(), string.end()}.\n> + * The size of the string is recorded by the container (struct, vector, map, or\n> + * function parameter serdes).\n> + */\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { { data.cbegin(), data.end() }, {} };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +};\n> +\n> +/*\n> + * FileDescriptors are serialized into a single byte that tells if the\n> + * FileDescriptor is valid or not. If it is valid, then for serialization\n> + * the fd will be written to the fd vector, or for deserialization the\n> + * fd vector const_iterator will be valid.\n> + *\n> + * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n> + * also allows us to simply send the entire fd vector into the deserializer\n> + * and it will be recursively consumed as necessary.\n> + */\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn { dataVec, fdVec };\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tASSERT(std::distance(dataBegin, dataEnd) >= 1);\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : FileDescriptor();\n> +\t}\n> +};\n> +\n> +/*\n> + * ControlList is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * 4 bytes - uint32_t Size of serialized ControlList, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + * X bytes - Serialized ControlList (using ControlSerializer)\n> + *\n> + * If data.infoMap() is nullptr, then the default controls::controls will\n> + * be used. The serialized ControlInfoMap will have zero length.\n> + */\n> +template<>\n> +class IPADataSerializer<ControlList>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlList &data, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlList\";\n> +\n> +\t\tsize_t size;\n> +\t\tstd::vector<uint8_t> infoData;\n> +\t\tint ret;\n> +\n> +\t\t/*\n> +\t\t * \\todo Revisit this opportunistic serialization of the\n> +\t\t * ControlInfoMap, as it could be fragile\n> +\t\t */\n> +\t\tif (data.infoMap() && !cs->isCached(*data.infoMap())) {\n> +\t\t\tsize = cs->binarySize(*data.infoMap());\n> +\t\t\tinfoData.resize(size);\n> +\t\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\t\tret = cs->serialize(*data.infoMap(), buffer);\n> +\n> +\t\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList's ControlInfoMap\";\n> +\t\t\t\treturn { {}, {} };\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize = cs->binarySize(data);\n> +\t\tstd::vector<uint8_t> listData(size);\n> +\t\tByteStreamBuffer buffer(listData.data(), listData.size());\n> +\t\tret = cs->serialize(data, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tif (std::distance(dataBegin, dataEnd) < 8)\n> +\t\t\treturn {};\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator it = dataBegin + 8;\n> +\n> +\t\tif (infoDataSize + listDataSize < infoDataSize ||\n> +\t\t    static_cast<uint32_t>(std::abs(std::distance(it, dataEnd))) < infoDataSize + listDataSize)\n\nit can't be larger than dataEnd given the check above, so there's no\nneed for std::abs(). And if it could be larger, then this would be\nwrong, as it would result in the error being silently ignored. Were you\ntrying to silence a compiler warning ? We want libcamera to build\nwithout warning, but not at the cost of introducing errors :-)\n\nInterestingly, the signed comparison warning is only produced by builds\non 32-bit platforms. The static_cast seems the right solution, but\nstd::abs isn't needed.\n\nThis comment applies to other locations below too.\n\n> +\t\t\treturn {};\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\t\tstd::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);\n> +\n> +\t\tif (infoDataSize > 0) {\n> +\t\t\tByteStreamBuffer buffer(&*it, infoData.size());\n\ninfoData is only used for its .size(), which is equal to infoDataSize.\nThis comment applies to other locations below too.\n\n> +\t\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\t\t\t/* It's fine if map is empty. */\n> +\t\t\tif (buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error)\n> +\t\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> +\t\t\t\treturn ControlList();\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tit += infoDataSize;\n> +\t\tByteStreamBuffer buffer(&*it, listData.size());\n\nSame here.\n\n> +\t\tControlList list = cs->deserialize<ControlList>(buffer);\n> +\t\tif (buffer.overflow())\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: buffer overflow\";\n> +\n> +\t\treturn list;\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * const ControlInfoMap is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + */\n> +template<>\n> +class IPADataSerializer<ControlInfoMap>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlInfoMap &map, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlInfoMap\";\n> +\n> +\t\tsize_t size = cs->binarySize(map);\n> +\t\tstd::vector<uint8_t> infoData(size);\n> +\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\tint ret = cs->serialize(map, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlInfoMap\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlInfoMap\";\n> +\n> +\t\tif (std::distance(dataBegin, dataEnd) < 4)\n> +\t\t\treturn {};\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator it = dataBegin + 4;\n> +\n> +\t\tif (static_cast<uint32_t>(std::abs(std::distance(it, dataEnd))) < infoDataSize)\n> +\t\t\treturn {};\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\n> +\t\tByteStreamBuffer buffer(&*it, infoData.size());\n> +\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\n> +\t\treturn map;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * FrameBuffer::Plane is serialized as:\n> + *\n> + * 1 byte  - FileDescriptor\n> + * 4 bytes - uint32_t Length\n> + */\n> +template<>\n> +class IPADataSerializer<FrameBuffer::Plane>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t      std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t      std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#endif /* __DOXYGEN__ */\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */\n> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> new file mode 100644\n> index 00000000..e20682b9\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,244 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer\n> + */\n> +\n> +#include \"libcamera/internal/ipa_data_serializer.h\"\n> +\n> +#include \"libcamera/internal/log.h\"\n> +\n> +/**\n> + * \\file ipa_data_serializer.h\n> + * \\brief IPA Data Serializer\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(IPADataSerializer)\n> +\n> +/**\n> + * \\class IPADataSerializer\n> + * \\brief IPA Data Serializer\n> + *\n> + * Static template class that provides functions for serializing and\n> + * deserializing IPA data.\n> + *\n> + * \\todo Switch to Span instead of byte and fd vector\n> + *\n> + * \\todo Harden the vector and map deserializer\n> + *\n> + * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> + * index into the fd array. This will allow us to use views instead of copying.\n> + */\n> +\n> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> + * \\brief Append POD to end of byte vector, in little-endian order\n> + * \\tparam T Type of POD to append\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value to append\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tconst std::vector<int32_t> &fds,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] fds Fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the iterator version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tstd::vector<int32_t>::const_iterator fdsBegin,\n> + * \tstd::vector<int32_t>::const_iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +#define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\t\t\t\\\n> +IPADataSerializer<type>::serialize(const type &data,\t\t\t\\\n> +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\tstd::vector<uint8_t> dataVec;\t\t\t\t\t\\\n> +\tdataVec.reserve(sizeof(type));\t\t\t\t\t\\\n> +\tappendPOD<type>(dataVec, data);\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\treturn { dataVec, {} };\t\t\t\t\t\t\\\n> +}\t\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\treturn readPOD<type>(dataBegin, 0, dataEnd);\t\t\t\\\n> +}\t\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> +\t\t\t\t\t  ControlSerializer *cs)\t\\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n> +}\t\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> +\t\t\t\t\t  [[maybe_unused]] const std::vector<int32_t> &fds, \\\n> +\t\t\t\t\t  ControlSerializer *cs)\t\\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n> +}\t\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin, \\\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd, \\\n> +\t\t\t\t\t  ControlSerializer *cs)\t\\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +\treturn deserialize(dataBegin, dataEnd, cs);\t\t\t\\\n> +}\n> +\n> +DEFINE_POD_SERIALIZER(bool)\n> +DEFINE_POD_SERIALIZER(uint8_t)\n> +DEFINE_POD_SERIALIZER(uint16_t)\n> +DEFINE_POD_SERIALIZER(uint32_t)\n> +DEFINE_POD_SERIALIZER(uint64_t)\n> +DEFINE_POD_SERIALIZER(int8_t)\n> +DEFINE_POD_SERIALIZER(int16_t)\n> +DEFINE_POD_SERIALIZER(int32_t)\n> +DEFINE_POD_SERIALIZER(int64_t)\n> +DEFINE_POD_SERIALIZER(float)\n> +DEFINE_POD_SERIALIZER(double)\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 5a4bf0d7..7d8ea00a 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -26,6 +26,7 @@ libcamera_sources = files([\n>      'geometry.cpp',\n>      'ipa_context_wrapper.cpp',\n>      'ipa_controls.cpp',\n> +    'ipa_data_serializer.cpp',\n>      'ipa_interface.cpp',\n>      'ipa_manager.cpp',\n>      'ipa_module.cpp',","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id F2FD1BD162\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 12 Feb 2021 01:51:01 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5E77A63758;\n\tFri, 12 Feb 2021 02:51:01 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 093A2602FC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 12 Feb 2021 02:51:00 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3CF348B5;\n\tFri, 12 Feb 2021 02:50:59 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ji64+jG5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1613094659;\n\tbh=ZsgeJjmjCiz+yoIg4B+0cJGoeG7FosjvnUAqJdgjKb0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ji64+jG5mi+5rsKcZBreHRRrAxn5/1wL9sbsbQGzM/XUl3B94GCYHX58TPMTobjhp\n\tzk1MLCM36FUWUEZmE7hW6gliSJl9pOMzHvs0DpLe2H+7MDytTwJLrISYZpA3N9Hdgj\n\tiFYCEoHcqTWkDd4wFOKqfs2xSnrGCdBZjS48qlW0=","Date":"Fri, 12 Feb 2021 03:50:33 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<YCXe6WAX9ncOB4GI@pendragon.ideasonboard.com>","References":"<20210211071805.34963-1-paul.elder@ideasonboard.com>\n\t<20210211071805.34963-7-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20210211071805.34963-7-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v7 06/10] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]