[{"id":12678,"web_url":"https://patchwork.libcamera.org/comment/12678/","msgid":"<20200923153218.gkfk5rfbzrqxakas@uno.localdomain>","date":"2020-09-23T15:32:18","subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Paul,\n\nOn Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:\n> Add an IPADataSerializer which implments de/serialization of built-in\n\nWith built-in do you mean STL defined data types ?\n\n> and libcamera data structures. This is intended to be used by the proxy\n> and the proxy worker in the IPC layer.\n>\n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n>\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  | 1220 +++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         |  154 +++\n>  src/libcamera/meson.build                     |    1 +\n>  3 files changed, 1375 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..accd4dce\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,1220 @@\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 <tuple>\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> +template<typename T>\n> +void appendUInt(std::vector<uint8_t> &vec, T val)\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n> +\tfor (size_t i = 0; i < byteWidth; i++)\n> +\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n\nI see in several places missing spaces around the '*' operator\nI think you can also drop the & 0xff as it's implied in the cast to\nuint8_t?\n\n> +}\n> +\n> +template<typename T>\n> +T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> +{\n> +\tT ret = 0;\n> +\tsize_t byteWidth = sizeof(ret);\n> +\tif (pos + byteWidth > vec.size())\n> +\t\treturn ret;\n> +\n> +\tfor (size_t i = 0; i < byteWidth; i++)\n> +\t\tret |= vec[pos + i] << 8*i;\n\nYou could iterate the vector and call the below defined overload. Not\na big deal, it would be probably more confusing.\n\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +T readUInt(std::vector<uint8_t>::iterator it)\n> +{\n> +\tT ret = 0;\n> +\tsize_t byteWidth = sizeof(ret);\n> +\tfor (size_t i = 0; i < byteWidth; i++)\n> +\t\tret |= *(it + i) << 8*i;\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +class IPADataSerializer\n> +{\n> +#ifdef __DOXYGEN__\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(T data, ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(std::vector<uint8_t> &data,\n> +\t\t\t     ControlSerializer *cs);\n\nThat *cs parameter is unused in all specializations but ControlList\none. We already had to face a similar issue when we implemented\nControlValue. You could define two versions of these functions one for\nthe ControlList case which has *cs in its signature, the other for all\nthe other types, without cs. The construct to do so is not nice at a\nfirst look but effective:\nhttps://en.cppreference.com/w/cpp/types/enable_if\n\nI think it could be applied here, maybe on top ?\n\n> +\tstatic T deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t     std::vector<uint8_t>::iterator it2,\n> +\t\t\t     ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(std::vector<uint8_t> &data,\n> +\t\t\t     std::vector<int32_t> &fds,\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t     ControlSerializer *cs);\n\nWhere can I see this used ? As I wonder why this always take one\n> +#endif /* __DOXYGEN__ */\n> +};\n> +\n> +#ifndef __DOXYGEN__\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> data_vec;\n> +\t\tstd::vector<int32_t> fds_vec;\n> +\n> +\t\t// serialize the length\n\n// ?\n\n> +\t\tuint32_t vec_len = data.size();\n> +\t\tappendUInt<uint32_t>(data_vec, vec_len);\n> +\n> +\t\t// serialize the members\n> +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> +\n> +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {data_vec, fds_vec};\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(it1, it2,\n> +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),\n> +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> +\t\t\t\t\t\t\t\t      cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vec_len = readUInt<uint32_t>(data_it1);\n> +\t\tstd::vector<V> ret(vec_len);\n> +\n> +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> +\t\tfor (uint32_t i = 0; i < vec_len; i++) {\n> +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n> +\t\t\t\t\t\t\t\t   data_it + 8 + sizeof_data,\n> +\t\t\t\t\t\t\t\t   fd_it,\n> +\t\t\t\t\t\t\t\t   fd_it + sizeof_fds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdata_it += 8 + sizeof_data;\n> +\t\t\tfd_it += sizeof_fds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\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> data_vec;\n> +\t\tstd::vector<int32_t> fds_vec;\n> +\n> +\t\t// serialize the length\n> +\t\tuint32_t map_len = data.size();\n> +\t\tappendUInt<uint32_t>(data_vec, map_len);\n> +\n> +\t\t// serialize the members\n> +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> +\n> +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfds_vec.insert(fds_vec.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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> +\n> +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {data_vec, fds_vec};\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,\n> +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),\n> +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> +\t\t\t\t\t\t\t\t      cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t map_len = readUInt<uint32_t>(data_it1);\n> +\n> +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> +\t\tfor (uint32_t i = 0; i < map_len; i++) {\n> +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(data_it + 8,\n> +\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> +\t\t\t\t\t\t\t\t  fd_it,\n> +\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdata_it += 8 + sizeof_data;\n> +\t\t\tfd_it += sizeof_fds;\n> +\t\t\tsizeof_data = readUInt<uint32_t>(data_it);\n> +\t\t\tsizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(data_it + 8,\n> +\t\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> +\t\t\t\t\t\t\t\t\t  fd_it,\n> +\t\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdata_it += 8 + sizeof_data;\n> +\t\t\tfd_it += sizeof_fds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#define DECLARE_INTEGRAL_SERIALIZER(type)\t\t\t\t\\\n> +template<>\t\t\t\t\t\t\t\t\\\n> +class IPADataSerializer<type>\t\t\t\t\t\t\\\n> +{\t\t\t\t\t\t\t\t\t\\\n> +public:\t\t\t\t\t\t\t\t\t\\\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\t\\\n> +\tserialize(const type data,\t\t\t\t\t\\\n> +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\t\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\tstd::vector<uint8_t> data_vec;\t\t\t\t\\\n> +\t\tappendUInt<type>(data_vec, data);\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\t\treturn {data_vec, {}};\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> +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> +\t\t\t\t\t\t\t    data.end());\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\tstatic type deserialize(std::vector<uint8_t>::iterator it1,\t\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn readUInt<type>(it1);\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> +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> +\t\t\t\t\t\t\t    data.end());\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\tstatic type deserialize(std::vector<uint8_t>::iterator data_it1,\\\n> +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn IPADataSerializer<type>::deserialize(data_it1,\t\\\n> +\t\t\t\t\t\t\t    data_it2);\t\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +};\n> +\n> +DECLARE_INTEGRAL_SERIALIZER(uint8_t)\n> +DECLARE_INTEGRAL_SERIALIZER(uint16_t)\n> +DECLARE_INTEGRAL_SERIALIZER(uint32_t)\n> +DECLARE_INTEGRAL_SERIALIZER(uint64_t)\n> +DECLARE_INTEGRAL_SERIALIZER(int8_t)\n> +DECLARE_INTEGRAL_SERIALIZER(int16_t)\n> +DECLARE_INTEGRAL_SERIALIZER(int32_t)\n> +\n> +template<>\n> +class IPADataSerializer<int64_t>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const int64_t data,\n> +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\t\tappendUInt<uint64_t>(data_vec, data);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic int64_t deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<int64_t>::deserialize(data.begin(),\n> +\t\t\t\t\t\t\t       data.end());\n> +\t}\n> +\n> +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t   [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(it1, it2) < 8) {\n> +\t\t\tLOG(IPADataSerializer, Info)\n> +\t\t\t\t<< \"Not enough bytes to deserialize int64_t, returning 0\";\n> +\t\t\treturn 0;\n> +\t\t}\n> +\n> +\t\tuint32_t lower = readUInt<uint32_t>(it1);\n> +\t\tuint32_t upper = readUInt<uint32_t>(it1 + 4);\n> +\n> +\t\treturn lower + (uint64_t(upper) << 32);\n> +\t}\n> +\n> +\tstatic int64_t 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 IPADataSerializer<int64_t>::deserialize(data.begin(),\n> +\t\t\t\t\t\t\t       data.end());\n> +\t}\n> +\n> +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t   std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n\nIn what is the int64_t specialization different that the other ones\ndefined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you\ndon't have a  readUInt<int64_t> ?\n\n> +\n> +template<>\n> +class IPADataSerializer<bool>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const bool data,\n> +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\t\tappendUInt<uint8_t>(data_vec, data);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic bool deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<bool>::deserialize(data.begin(),\n> +\t\t\t\t\t\t\t    data.end());\n> +\t}\n> +\n> +\tstatic bool deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn readUInt<uint8_t>(it1);\n\nThe sizeof(bool) seems to be a bit an issue actually... Can you solve\nit by declarting a\n\ntemplate<> bool readUInt(std::vector<uint8_t>::iterator it)\n?\n\nActually it seems to me the current implementation might work with\nbool too...\n\n\n> +\t}\n> +\n> +\tstatic bool 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 IPADataSerializer<bool>::deserialize(data.begin(),\n> +\t\t\t\t\t\t\t    data.end());\n> +\t}\n> +\n> +\tstatic bool deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<bool>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<float>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint8_t arr[4];\n> +\t\tmemcpy(arr, &data, sizeof(arr));\n\nI wonder if we should no be using memcpy in the readUInt() function\ntoo. Would this help you with floats ?\n\n> +\n> +\t\tstd::vector<uint8_t> data_vec(arr, arr + 4);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic float deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic float deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(it1, it2) < 4) {\n> +\t\t\tLOG(IPADataSerializer, Info)\n> +\t\t\t\t<< \"Not enough bytes to deserialize float, returning 0\";\n> +\t\t\treturn 0;\n> +\t\t}\n> +\n> +\t\tstd::vector data(it1, it2);\n> +\t\tfloat ret;\n> +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic float 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 IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic float deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<float>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<double>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint8_t arr[8];\n> +\t\tmemcpy(arr, &data, sizeof(arr));\n> +\n> +\t\tstd::vector<uint8_t> data_vec(arr, arr + 8);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic double deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic double deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(it1, it2) < 8) {\n> +\t\t\tLOG(IPADataSerializer, Info)\n> +\t\t\t\t<< \"Not enough bytes to deserialize double, returning 0\";\n> +\t\t\treturn 0;\n> +\t\t}\n> +\n> +\t\tstd::vector data(it1, it2);\n> +\t\tdouble ret;\n> +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic double 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 IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic double deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<double>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\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\tstd::vector<uint8_t> data_vec(data.begin(), data.end());\n> +\n> +\t\treturn {data_vec, {}};\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::string str(it1, it2);\n> +\n> +\t\treturn str;\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<std::string>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n\nAh, that's why you have the fds parameter in deserializer and the\nstd::vector<int32_t> in the serializer return parameter.\n\nI think this would be cleaner if we could use enable_if and define an\nad-hoc function signature for FileDescriptor and ControlList. It would\nremove the fds and cs parameters from all other types (the ones I've\nsee so far at least).\n\nI think I'll stop here, there's enough to discuss on\nThanks\n   j\n\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> data_vec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fd_vec;\n> +\t\tif (data.isValid())\n> +\t\t\tfd_vec.push_back(data.fd());\n> +\n> +\t\treturn {data_vec, fd_vec};\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 IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),\n> +\t\t\t\t\t\t\t\t      fds.begin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(data_it1, data_it2) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> +\n> +\t\tbool valid = *data_it1;\n> +\n> +\t\tif (valid && std::distance(fds_it1, fds_it2) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> +\n> +\t\treturn valid ? FileDescriptor(*fds_it1) : FileDescriptor();\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<IPASettings>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<std::string>::serialize(data.configurationFile);\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPASettings ret;\n> +\t\tret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic IPASettings 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 IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<CameraSensorInfo>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\n> +\t\tuint32_t str_len = data.model.size();\n> +\t\tappendUInt<uint32_t>(data_vec, str_len);\n> +\n> +\t\tdata_vec.insert(data_vec.end(), data.model.begin(), data.model.end());\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.bitsPerPixel);\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.width);\n> +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.height);\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));\n> +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));\n> +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.width);\n> +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.height);\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.width);\n> +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.height);\n> +\n> +\t\tappendUInt<uint64_t>(data_vec, data.pixelRate);\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.lineLength);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tCameraSensorInfo ret;\n> +\n> +\t\tuint32_t str_len = readUInt<uint32_t>(it1);\n> +\t\tstd::string str(it1 + 4, it1 + 4 + str_len);\n> +\t\tret.model = str;\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = it1 + 4 + str_len;\n> +\n> +\t\tret.bitsPerPixel = readUInt<uint32_t>(it);\n> +\n> +\t\tret.activeAreaSize.width = readUInt<uint32_t>(it + 4);\n> +\t\tret.activeAreaSize.height = readUInt<uint32_t>(it + 8);\n> +\n> +\t\tret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));\n> +\t\tret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));\n> +\t\tret.analogCrop.width = readUInt<uint32_t>(it + 20);\n> +\t\tret.analogCrop.height = readUInt<uint32_t>(it + 24);\n> +\n> +\t\tret.outputSize.width = readUInt<uint32_t>(it + 28);\n> +\t\tret.outputSize.height = readUInt<uint32_t>(it + 32);\n> +\n> +\t\tret.pixelRate = readUInt<uint64_t>(it + 36);\n> +\n> +\t\tret.lineLength = readUInt<uint64_t>(it + 44);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic CameraSensorInfo 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    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t    std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<IPAStream>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.pixelFormat);\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.size.width);\n> +\t\tappendUInt<uint32_t>(data_vec, data.size.height);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPAStream ret;\n> +\n> +\t\tret.pixelFormat = readUInt<uint32_t>(it1);\n> +\n> +\t\tret.size.width = readUInt<uint32_t>(it1 + 4);\n> +\t\tret.size.height = readUInt<uint32_t>(it1 + 8);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic IPAStream 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 IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<const ControlList>\n> +{\n> +public:\n> +\t// map arg will be generated, since it's per-pipeline anyway\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlList &data, const ControlInfoMap &map,\n> +\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 serialization of ControlList\";\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 ControlList's ControlInfoMap\";\n> +\t\t\treturn {{}, {}};\n> +\t\t}\n> +\n> +\t\tsize = cs->binarySize(data);\n> +\t\tstd::vector<uint8_t> listData(size);\n> +\t\tbuffer = ByteStreamBuffer(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> data_vec;\n> +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> +\t\tappendUInt<uint32_t>(data_vec, listData.size());\n> +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> +\t\tdata_vec.insert(data_vec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> +\t\tuint32_t listDataSize = readUInt<uint32_t>(it1 + 4);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = it1 + 8;\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\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> +\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\t\t/* It's fine if map is empty. */\n> +\t\tif (buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error)\n> +\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> +\t\t\treturn ControlList();\n> +\t\t}\n> +\n> +\t\tbuffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());\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> +\t\tif (list.empty())\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: empty list\";\n> +\n> +\t\treturn list;\n> +\t}\n> +\n> +\tstatic const 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 IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);\n> +\t}\n> +};\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, const ControlInfoMap &map,\n> +\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tconst ControlList &list_const = const_cast<const ControlList &>(data);\n> +\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\t\tstd::tie(data_vec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tControlList ret;\n> +\t\tconst ControlList &list = ret;\n> +\t\tconst_cast<ControlList &>(list) =\n> +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tControlList ret;\n> +\t\tconst ControlList &list = ret;\n> +\t\tconst_cast<ControlList &>(list) =\n> +\t\t\tIPADataSerializer<const ControlList>::deserialize(it1, it2, cs);\n> +\n> +\t\treturn ret;\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\tControlList ret;\n> +\t\tconst ControlList &list = ret;\n> +\t\tconst_cast<ControlList &>(list) =\n> +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, fds, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tControlList ret;\n> +\t\tconst ControlList &list = ret;\n> +\t\tconst_cast<ControlList &>(list) =\n> +\t\t\tIPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,\n> +\t\t\t\t\t\t\t\t\t  fds_it1, fds_it2, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<const 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\treturn {{}, {}};\n> +\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t\t\tControlSerializer *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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = it1 + 4;\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\n> +\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> +\t\tconst ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\n> +\t\treturn map;\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);\n> +\t}\n> +};\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, [[maybe_unused]] ControlSerializer *cs)\n> +\t{\n> +\t\tconst ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);\n> +\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\t\tstd::tie(data_vec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> +\n> +\t\treturn {data_vec, {}};\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tControlInfoMap ret;\n> +\t\tconst ControlInfoMap &map = ret;\n> +\t\tconst_cast<ControlInfoMap &>(map) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\tControlInfoMap ret;\n> +\t\tconst ControlInfoMap &map = ret;\n> +\t\tconst_cast<ControlInfoMap &>(map) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\tControlInfoMap ret;\n> +\t\tconst ControlInfoMap &map = ret;\n> +\t\tconst_cast<ControlInfoMap &>(map) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tControlInfoMap ret;\n> +\t\tconst ControlInfoMap &map = ret;\n> +\t\tconst_cast<ControlInfoMap &>(map) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,\n> +\t\t\t\t\t\t\t\t\t     fds_it1, fds_it2, cs);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\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> data_vec;\n> +\t\tstd::vector<int32_t> fds_vec;\n> +\n> +\t\t// fd\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\tdata_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\t// length\n> +\t\tappendUInt<uint32_t>(data_vec, data.length);\n> +\n> +\t\treturn {data_vec, fds_vec};\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 IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),\n> +\t\t\t\t\t\t\t\t\t  fds.begin(), fds.end(),\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t\t      std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\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(data_it1, data_it1 + 1,\n> +\t\t\t\t\t\t\t\t\tfds_it1, fds_it1 + 1);\n> +\t\tret.length = readUInt<uint32_t>(data_it1 + 1);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +\n> +template<>\n> +class IPADataSerializer<IPABuffer>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const IPABuffer &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> data_vec;\n> +\n> +\t\tappendUInt<uint32_t>(data_vec, data.id);\n> +\n> +\t\tstd::vector<uint8_t> planes_data_vec;\n> +\t\tstd::vector<int32_t> planes_fds_vec;\n> +\t\tstd::tie(planes_data_vec, planes_fds_vec) =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> +\n> +\t\tdata_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());\n> +\n> +\t\treturn {data_vec, planes_fds_vec};\n> +\t}\n> +\n> +\tstatic IPABuffer deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t     std::vector<int32_t> &fds,\n> +\t\t\t\t     ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),\n> +\t\t\t\t\t\t\t\t fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,\n> +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> +\t\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> +\t\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> +\t\t\t\t     ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPABuffer ret;\n> +\n> +\t\tret.id = readUInt<uint32_t>(data_it1);\n> +\n> +\t\tret.planes =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> +\t\t\t\tdata_it1 + 4, data_it2, fds_it1, fds_it2, cs);\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..5029cdf6\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,154 @@\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> +\n> +/**\n> + * \\fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)\n> + * \\brief Append uint to end of byte vector, in little-endian order\n> + * \\tparam T Type of uint to append\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value of uint to append\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read uint from byte vector, in little-endian order\n> + * \\tparam T Type of uint to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * \\return The uint read from \\a vec, or 0 if reading goes past end of \\a vec\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)\n> + * \\brief Read uint from byte vector, in little-endian order\n> + * \\tparam T Type of uint to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + *\n> + * \\return The uint read from \\a vec\n> + */\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> + * \tstd::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>::iterator it1,\n> + * \tstd::vector<uint8_t>::iterator it2,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] it1 Begin iterator of byte vector to deserialize from\n> + * \\param[in] it2 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> + * \tstd::vector<uint8_t> &data,\n> + * \tstd::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>::iterator data_it1,\n> + * \tstd::vector<uint8_t>::iterator data_it2,\n> + * \tstd::vector<int32_t>::iterator fds_it1,\n> + * \tstd::vector<int32_t>::iterator fds_it2,\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_it1 Begin iterator of byte vector to deserialize from\n> + * \\param[in] data_it2 End iterator of byte vector to deserialize from\n> + * \\param[in] fds_it1 Begin iterator of fd vector to deserialize from\n> + * \\param[in] fds_it2 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> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 9d7442fa..61aad08e 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -23,6 +23,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',\n> --\n> 2.27.0\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","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 1F6FAC3B5B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 23 Sep 2020 15:28:28 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A789162FD8;\n\tWed, 23 Sep 2020 17:28:27 +0200 (CEST)","from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net\n\t[217.70.183.194])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 12CDD60576\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Sep 2020 17:28:27 +0200 (CEST)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 753A040008;\n\tWed, 23 Sep 2020 15:28:26 +0000 (UTC)"],"X-Originating-IP":"2.224.242.101","Date":"Wed, 23 Sep 2020 17:32:18 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20200923153218.gkfk5rfbzrqxakas@uno.localdomain>","References":"<20200922133537.258098-1-paul.elder@ideasonboard.com>\n\t<20200922133537.258098-14-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200922133537.258098-14-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","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=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":12710,"web_url":"https://patchwork.libcamera.org/comment/12710/","msgid":"<20200924071700.GL45948@pyrite.rasen.tech>","date":"2020-09-24T07:17:00","subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Wed, Sep 23, 2020 at 05:32:18PM +0200, Jacopo Mondi wrote:\n> Hi Paul,\n> \n> On Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:\n> > Add an IPADataSerializer which implments de/serialization of built-in\n> \n> With built-in do you mean STL defined data types ?\n\nNot quite. Just PODs, vector, map, and string. I'll specify them.\n\n> > and libcamera data structures. This is intended to be used by the proxy\n> > and the proxy worker in the IPC layer.\n> >\n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> >\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  | 1220 +++++++++++++++++\n> >  src/libcamera/ipa_data_serializer.cpp         |  154 +++\n> >  src/libcamera/meson.build                     |    1 +\n> >  3 files changed, 1375 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..accd4dce\n> > --- /dev/null\n> > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > @@ -0,0 +1,1220 @@\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 <tuple>\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> > +template<typename T>\n> > +void appendUInt(std::vector<uint8_t> &vec, T val)\n> > +{\n> > +\tsize_t byteWidth = sizeof(val);\n> > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > +\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n> \n> I see in several places missing spaces around the '*' operator\n>\n> I think you can also drop the & 0xff as it's implied in the cast to\n> uint8_t?\n\nI ended up replacing this (and readUInt) with memcpys, as suggested with\nthe floats. That allows us to have DECLARE_POD_SERIALIZER for bool,\nfloat, double, as well as all of the ints and uints :)\n\n> > +}\n> > +\n> > +template<typename T>\n> > +T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> > +{\n> > +\tT ret = 0;\n> > +\tsize_t byteWidth = sizeof(ret);\n> > +\tif (pos + byteWidth > vec.size())\n> > +\t\treturn ret;\n> > +\n> > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > +\t\tret |= vec[pos + i] << 8*i;\n> \n> You could iterate the vector and call the below defined overload. Not\n> a big deal, it would be probably more confusing.\n> \n> > +\treturn ret;\n> > +}\n> > +\n> > +template<typename T>\n> > +T readUInt(std::vector<uint8_t>::iterator it)\n> > +{\n> > +\tT ret = 0;\n> > +\tsize_t byteWidth = sizeof(ret);\n> > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > +\t\tret |= *(it + i) << 8*i;\n> > +\treturn ret;\n> > +}\n> > +\n> > +template<typename T>\n> > +class IPADataSerializer\n> > +{\n> > +#ifdef __DOXYGEN__\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(T data, ControlSerializer *cs);\n> > +\n> > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t     ControlSerializer *cs);\n> \n> That *cs parameter is unused in all specializations but ControlList\n> one. We already had to face a similar issue when we implemented\n> ControlValue. You could define two versions of these functions one for\n> the ControlList case which has *cs in its signature, the other for all\n> the other types, without cs. The construct to do so is not nice at a\n> first look but effective:\n> https://en.cppreference.com/w/cpp/types/enable_if\n> \n> I think it could be applied here, maybe on top ?\n\nI don't think we can quite do so. ControlSerializer and the fd functions\nare necessary if any descendent of the struct needs it. This means a\nsimple enable_if won't we sufficient, and it would have to check the\nencapsulation tree.\n\nOn the other hand, at code-generation time (for generating the\nserializer), it is known which structs need ControlSerializer/fd, so in\ntheory we could prune the parameters and functions at that point. The\nusers of the serializers are also known at code-generation time, so the\ncalls can be matched as well.\n\nThe problem is in the de/serializer for vector and map. For vector\nserialization:\n\n\tIPADataSerializer<V>::serialize(*it, cs);\n\nAnd for deserialization:\n\n\tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n\t\t\t\t\t\t   data_it + 8 + sizeof_data,\n\t\t\t\t\t\t   fd_it,\n\t\t\t\t\t\t   fd_it + sizeof_fds,\n\t\t\t\t\t\t   cs);\n\nThe vector and map de/serializers don't know if the struct that they\ncontain needs the ControlSerializer/fd, that's why we have to pass it in\nregardless, and that's why all IPADataSerializers must accept them as\nparameters. enable_if might be useful here, but again that only works if\nthe struct that the vector/map contains is the one that needs the\nControlSerializer/fd; if the struct doesn't need it but its children do,\nthen we'd have to do nested enable_if.\n\t\n> > +\tstatic T deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t     std::vector<uint8_t>::iterator it2,\n> > +\t\t\t     ControlSerializer *cs);\n> > +\n> > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t     std::vector<int32_t> &fds,\n> > +\t\t\t     ControlSerializer *cs);\n> > +\tstatic T deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t     ControlSerializer *cs);\n> \n> Where can I see this used ? As I wonder why this always take one\n\nAll of these functions are meant to be used by the proxy and the proxy\nworker, so they can be seen in the template code :)\n\nThis last one can be seen in the generated code for the raspberrypi\nproxy worker:\n\n       \tRPiConfigInput ipaConfig = IPADataSerializer<RPiConfigInput>::deserialize(\n               \t_message.data.begin() + ipaConfigStart,\n               \t_message.data.end(),\n               \t_message.fds.begin() + ipaConfigFdStart,\n               \t_message.fds.end());\n\nWhere _message is a IPCUnixSocket::Payload, which carries a data vector\nand an fd vector.\n\n> > +#endif /* __DOXYGEN__ */\n> > +};\n> > +\n> > +#ifndef __DOXYGEN__\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> data_vec;\n> > +\t\tstd::vector<int32_t> fds_vec;\n> > +\n> > +\t\t// serialize the length\n> \n> // ?\n\nack\n\n> > +\t\tuint32_t vec_len = data.size();\n> > +\t\tappendUInt<uint32_t>(data_vec, vec_len);\n> > +\n> > +\t\t// serialize the members\n> > +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > +\n> > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {data_vec, fds_vec};\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(it1, it2,\n> > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),\n> > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > +\t\t\t\t\t\t\t\t      cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint32_t vec_len = readUInt<uint32_t>(data_it1);\n> > +\t\tstd::vector<V> ret(vec_len);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> > +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> > +\t\tfor (uint32_t i = 0; i < vec_len; i++) {\n> > +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> > +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > +\n> > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n> > +\t\t\t\t\t\t\t\t   data_it + 8 + sizeof_data,\n> > +\t\t\t\t\t\t\t\t   fd_it,\n> > +\t\t\t\t\t\t\t\t   fd_it + sizeof_fds,\n> > +\t\t\t\t\t\t\t\t   cs);\n> > +\n> > +\t\t\tdata_it += 8 + sizeof_data;\n> > +\t\t\tfd_it += sizeof_fds;\n> > +\t\t}\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\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> data_vec;\n> > +\t\tstd::vector<int32_t> fds_vec;\n> > +\n> > +\t\t// serialize the length\n> > +\t\tuint32_t map_len = data.size();\n> > +\t\tappendUInt<uint32_t>(data_vec, map_len);\n> > +\n> > +\t\t// serialize the members\n> > +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > +\n> > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfds_vec.insert(fds_vec.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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > +\n> > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {data_vec, fds_vec};\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,\n> > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),\n> > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > +\t\t\t\t\t\t\t\t      cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::map<K, V> ret;\n> > +\n> > +\t\tuint32_t map_len = readUInt<uint32_t>(data_it1);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> > +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> > +\t\tfor (uint32_t i = 0; i < map_len; i++) {\n> > +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> > +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > +\n> > +\t\t\tK key = IPADataSerializer<K>::deserialize(data_it + 8,\n> > +\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> > +\t\t\t\t\t\t\t\t  fd_it,\n> > +\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> > +\t\t\t\t\t\t\t\t  cs);\n> > +\n> > +\t\t\tdata_it += 8 + sizeof_data;\n> > +\t\t\tfd_it += sizeof_fds;\n> > +\t\t\tsizeof_data = readUInt<uint32_t>(data_it);\n> > +\t\t\tsizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > +\n> > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(data_it + 8,\n> > +\t\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> > +\t\t\t\t\t\t\t\t\t  fd_it,\n> > +\t\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> > +\t\t\t\t\t\t\t\t\t  cs);\n> > +\t\t\tret.insert({key, value});\n> > +\n> > +\t\t\tdata_it += 8 + sizeof_data;\n> > +\t\t\tfd_it += sizeof_fds;\n> > +\t\t}\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +#define DECLARE_INTEGRAL_SERIALIZER(type)\t\t\t\t\\\n> > +template<>\t\t\t\t\t\t\t\t\\\n> > +class IPADataSerializer<type>\t\t\t\t\t\t\\\n> > +{\t\t\t\t\t\t\t\t\t\\\n> > +public:\t\t\t\t\t\t\t\t\t\\\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\t\\\n> > +\tserialize(const type data,\t\t\t\t\t\\\n> > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\t\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\tstd::vector<uint8_t> data_vec;\t\t\t\t\\\n> > +\t\tappendUInt<type>(data_vec, data);\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\t\treturn {data_vec, {}};\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> > +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> > +\t\t\t\t\t\t\t    data.end());\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\tstatic type deserialize(std::vector<uint8_t>::iterator it1,\t\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn readUInt<type>(it1);\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> > +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> > +\t\t\t\t\t\t\t    data.end());\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\tstatic type deserialize(std::vector<uint8_t>::iterator data_it1,\\\n> > +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn IPADataSerializer<type>::deserialize(data_it1,\t\\\n> > +\t\t\t\t\t\t\t    data_it2);\t\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +};\n> > +\n> > +DECLARE_INTEGRAL_SERIALIZER(uint8_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(uint16_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(uint32_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(uint64_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(int8_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(int16_t)\n> > +DECLARE_INTEGRAL_SERIALIZER(int32_t)\n> > +\n> > +template<>\n> > +class IPADataSerializer<int64_t>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const int64_t data,\n> > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\t\tappendUInt<uint64_t>(data_vec, data);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic int64_t deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<int64_t>::deserialize(data.begin(),\n> > +\t\t\t\t\t\t\t       data.end());\n> > +\t}\n> > +\n> > +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t   [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(it1, it2) < 8) {\n> > +\t\t\tLOG(IPADataSerializer, Info)\n> > +\t\t\t\t<< \"Not enough bytes to deserialize int64_t, returning 0\";\n> > +\t\t\treturn 0;\n> > +\t\t}\n> > +\n> > +\t\tuint32_t lower = readUInt<uint32_t>(it1);\n> > +\t\tuint32_t upper = readUInt<uint32_t>(it1 + 4);\n> > +\n> > +\t\treturn lower + (uint64_t(upper) << 32);\n> > +\t}\n> > +\n> > +\tstatic int64_t 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 IPADataSerializer<int64_t>::deserialize(data.begin(),\n> > +\t\t\t\t\t\t\t       data.end());\n> > +\t}\n> > +\n> > +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t   std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> \n> In what is the int64_t specialization different that the other ones\n> defined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you\n> don't have a  readUInt<int64_t> ?\n\nYeah... I was having bit shifting problems with readUInt<int64_t>.\nI reimplemented it with memcpy though, and now the macro can generate\ncode for serializing int64_t, bool, float, and double as well :)\n\n> > +\n> > +template<>\n> > +class IPADataSerializer<bool>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const bool data,\n> > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\t\tappendUInt<uint8_t>(data_vec, data);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic bool deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<bool>::deserialize(data.begin(),\n> > +\t\t\t\t\t\t\t    data.end());\n> > +\t}\n> > +\n> > +\tstatic bool deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn readUInt<uint8_t>(it1);\n> \n> The sizeof(bool) seems to be a bit an issue actually... Can you solve\n> it by declarting a\n>\n> template<> bool readUInt(std::vector<uint8_t>::iterator it)\n> ?\n>\n> Actually it seems to me the current implementation might work with\n> bool too...\n>\n> > +\t}\n> > +\n> > +\tstatic bool 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 IPADataSerializer<bool>::deserialize(data.begin(),\n> > +\t\t\t\t\t\t\t    data.end());\n> > +\t}\n> > +\n> > +\tstatic bool deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<bool>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<float>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint8_t arr[4];\n> > +\t\tmemcpy(arr, &data, sizeof(arr));\n> \n> I wonder if we should no be using memcpy in the readUInt() function\n> too. Would this help you with floats ?\n\nApparently yes.\n\n\nThanks,\n\nPaul\n> > +\n> > +\t\tstd::vector<uint8_t> data_vec(arr, arr + 4);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic float deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic float deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(it1, it2) < 4) {\n> > +\t\t\tLOG(IPADataSerializer, Info)\n> > +\t\t\t\t<< \"Not enough bytes to deserialize float, returning 0\";\n> > +\t\t\treturn 0;\n> > +\t\t}\n> > +\n> > +\t\tstd::vector data(it1, it2);\n> > +\t\tfloat ret;\n> > +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic float 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 IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic float deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<float>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<double>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint8_t arr[8];\n> > +\t\tmemcpy(arr, &data, sizeof(arr));\n> > +\n> > +\t\tstd::vector<uint8_t> data_vec(arr, arr + 8);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic double deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic double deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(it1, it2) < 8) {\n> > +\t\t\tLOG(IPADataSerializer, Info)\n> > +\t\t\t\t<< \"Not enough bytes to deserialize double, returning 0\";\n> > +\t\t\treturn 0;\n> > +\t\t}\n> > +\n> > +\t\tstd::vector data(it1, it2);\n> > +\t\tdouble ret;\n> > +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic double 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 IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic double deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<double>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\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\tstd::vector<uint8_t> data_vec(data.begin(), data.end());\n> > +\n> > +\t\treturn {data_vec, {}};\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::string str(it1, it2);\n> > +\n> > +\t\treturn str;\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<std::string>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<FileDescriptor>\n> \n> Ah, that's why you have the fds parameter in deserializer and the\n> std::vector<int32_t> in the serializer return parameter.\n> \n> I think this would be cleaner if we could use enable_if and define an\n> ad-hoc function signature for FileDescriptor and ControlList. It would\n> remove the fds and cs parameters from all other types (the ones I've\n> see so far at least).\n> \n> I think I'll stop here, there's enough to discuss on\n> Thanks\n>    j\n> \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> data_vec = { data.isValid() };\n> > +\t\tstd::vector<int32_t> fd_vec;\n> > +\t\tif (data.isValid())\n> > +\t\t\tfd_vec.push_back(data.fd());\n> > +\n> > +\t\treturn {data_vec, fd_vec};\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 IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),\n> > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end());\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(data_it1, data_it2) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > +\n> > +\t\tbool valid = *data_it1;\n> > +\n> > +\t\tif (valid && std::distance(fds_it1, fds_it2) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > +\n> > +\t\treturn valid ? FileDescriptor(*fds_it1) : FileDescriptor();\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<IPASettings>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<std::string>::serialize(data.configurationFile);\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPASettings ret;\n> > +\t\tret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic IPASettings 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 IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<CameraSensorInfo>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\n> > +\t\tuint32_t str_len = data.model.size();\n> > +\t\tappendUInt<uint32_t>(data_vec, str_len);\n> > +\n> > +\t\tdata_vec.insert(data_vec.end(), data.model.begin(), data.model.end());\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.bitsPerPixel);\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.width);\n> > +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.height);\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));\n> > +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));\n> > +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.width);\n> > +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.height);\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.width);\n> > +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.height);\n> > +\n> > +\t\tappendUInt<uint64_t>(data_vec, data.pixelRate);\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.lineLength);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tCameraSensorInfo ret;\n> > +\n> > +\t\tuint32_t str_len = readUInt<uint32_t>(it1);\n> > +\t\tstd::string str(it1 + 4, it1 + 4 + str_len);\n> > +\t\tret.model = str;\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = it1 + 4 + str_len;\n> > +\n> > +\t\tret.bitsPerPixel = readUInt<uint32_t>(it);\n> > +\n> > +\t\tret.activeAreaSize.width = readUInt<uint32_t>(it + 4);\n> > +\t\tret.activeAreaSize.height = readUInt<uint32_t>(it + 8);\n> > +\n> > +\t\tret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));\n> > +\t\tret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));\n> > +\t\tret.analogCrop.width = readUInt<uint32_t>(it + 20);\n> > +\t\tret.analogCrop.height = readUInt<uint32_t>(it + 24);\n> > +\n> > +\t\tret.outputSize.width = readUInt<uint32_t>(it + 28);\n> > +\t\tret.outputSize.height = readUInt<uint32_t>(it + 32);\n> > +\n> > +\t\tret.pixelRate = readUInt<uint64_t>(it + 36);\n> > +\n> > +\t\tret.lineLength = readUInt<uint64_t>(it + 44);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo 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    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t    std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<IPAStream>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.pixelFormat);\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.size.width);\n> > +\t\tappendUInt<uint32_t>(data_vec, data.size.height);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPAStream ret;\n> > +\n> > +\t\tret.pixelFormat = readUInt<uint32_t>(it1);\n> > +\n> > +\t\tret.size.width = readUInt<uint32_t>(it1 + 4);\n> > +\t\tret.size.height = readUInt<uint32_t>(it1 + 8);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic IPAStream 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 IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<const ControlList>\n> > +{\n> > +public:\n> > +\t// map arg will be generated, since it's per-pipeline anyway\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const ControlList &data, const ControlInfoMap &map,\n> > +\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 serialization of ControlList\";\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 ControlList's ControlInfoMap\";\n> > +\t\t\treturn {{}, {}};\n> > +\t\t}\n> > +\n> > +\t\tsize = cs->binarySize(data);\n> > +\t\tstd::vector<uint8_t> listData(size);\n> > +\t\tbuffer = ByteStreamBuffer(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> data_vec;\n> > +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> > +\t\tappendUInt<uint32_t>(data_vec, listData.size());\n> > +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> > +\t\tdata_vec.insert(data_vec.end(), listData.begin(), listData.end());\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> > +\t\tuint32_t listDataSize = readUInt<uint32_t>(it1 + 4);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = it1 + 8;\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\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> > +\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> > +\t\t/* It's fine if map is empty. */\n> > +\t\tif (buffer.overflow()) {\n> > +\t\t\tLOG(IPADataSerializer, Error)\n> > +\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> > +\t\t\treturn ControlList();\n> > +\t\t}\n> > +\n> > +\t\tbuffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());\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> > +\t\tif (list.empty())\n> > +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: empty list\";\n> > +\n> > +\t\treturn list;\n> > +\t}\n> > +\n> > +\tstatic const 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 IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);\n> > +\t}\n> > +};\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, const ControlInfoMap &map,\n> > +\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\tconst ControlList &list_const = const_cast<const ControlList &>(data);\n> > +\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\t\tstd::tie(data_vec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic ControlList deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tControlList ret;\n> > +\t\tconst ControlList &list = ret;\n> > +\t\tconst_cast<ControlList &>(list) =\n> > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tControlList ret;\n> > +\t\tconst ControlList &list = ret;\n> > +\t\tconst_cast<ControlList &>(list) =\n> > +\t\t\tIPADataSerializer<const ControlList>::deserialize(it1, it2, cs);\n> > +\n> > +\t\treturn ret;\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\tControlList ret;\n> > +\t\tconst ControlList &list = ret;\n> > +\t\tconst_cast<ControlList &>(list) =\n> > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, fds, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tControlList ret;\n> > +\t\tconst ControlList &list = ret;\n> > +\t\tconst_cast<ControlList &>(list) =\n> > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,\n> > +\t\t\t\t\t\t\t\t\t  fds_it1, fds_it2, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<const 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\treturn {{}, {}};\n> > +\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> > +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t\t\tControlSerializer *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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = it1 + 4;\n> > +\n> > +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> > +\n> > +\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> > +\t\tconst ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> > +\n> > +\t\treturn map;\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);\n> > +\t}\n> > +};\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, [[maybe_unused]] ControlSerializer *cs)\n> > +\t{\n> > +\t\tconst ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);\n> > +\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\t\tstd::tie(data_vec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > +\n> > +\t\treturn {data_vec, {}};\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\tControlInfoMap ret;\n> > +\t\tconst ControlInfoMap &map = ret;\n> > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\tControlInfoMap ret;\n> > +\t\tconst ControlInfoMap &map = ret;\n> > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\tControlInfoMap ret;\n> > +\t\tconst ControlInfoMap &map = ret;\n> > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\tControlInfoMap ret;\n> > +\t\tconst ControlInfoMap &map = ret;\n> > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,\n> > +\t\t\t\t\t\t\t\t\t     fds_it1, fds_it2, cs);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\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> data_vec;\n> > +\t\tstd::vector<int32_t> fds_vec;\n> > +\n> > +\t\t// fd\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\tdata_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());\n> > +\t\tfds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());\n> > +\n> > +\t\t// length\n> > +\t\tappendUInt<uint32_t>(data_vec, data.length);\n> > +\n> > +\t\treturn {data_vec, fds_vec};\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 IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),\n> > +\t\t\t\t\t\t\t\t\t  fds.begin(), fds.end(),\n> > +\t\t\t\t\t\t\t\t\t  cs);\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t\t      std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\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(data_it1, data_it1 + 1,\n> > +\t\t\t\t\t\t\t\t\tfds_it1, fds_it1 + 1);\n> > +\t\tret.length = readUInt<uint32_t>(data_it1 + 1);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +\n> > +template<>\n> > +class IPADataSerializer<IPABuffer>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const IPABuffer &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> data_vec;\n> > +\n> > +\t\tappendUInt<uint32_t>(data_vec, data.id);\n> > +\n> > +\t\tstd::vector<uint8_t> planes_data_vec;\n> > +\t\tstd::vector<int32_t> planes_fds_vec;\n> > +\t\tstd::tie(planes_data_vec, planes_fds_vec) =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > +\n> > +\t\tdata_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());\n> > +\n> > +\t\treturn {data_vec, planes_fds_vec};\n> > +\t}\n> > +\n> > +\tstatic IPABuffer deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t     std::vector<int32_t> &fds,\n> > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),\n> > +\t\t\t\t\t\t\t\t fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPABuffer ret;\n> > +\n> > +\t\tret.id = readUInt<uint32_t>(data_it1);\n> > +\n> > +\t\tret.planes =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > +\t\t\t\tdata_it1 + 4, data_it2, fds_it1, fds_it2, cs);\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..5029cdf6\n> > --- /dev/null\n> > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > @@ -0,0 +1,154 @@\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> > +\n> > +/**\n> > + * \\fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)\n> > + * \\brief Append uint to end of byte vector, in little-endian order\n> > + * \\tparam T Type of uint to append\n> > + * \\param[in] vec Byte vector to append to\n> > + * \\param[in] val Value of uint to append\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> > + * \\brief Read uint from byte vector, in little-endian order\n> > + * \\tparam T Type of uint to read\n> > + * \\param[in] vec Byte vector to read from\n> > + * \\param[in] pos Index in vec to start reading from\n> > + *\n> > + * \\return The uint read from \\a vec, or 0 if reading goes past end of \\a vec\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)\n> > + * \\brief Read uint from byte vector, in little-endian order\n> > + * \\tparam T Type of uint to read\n> > + * \\param[in] it Iterator of byte vector to read from\n> > + *\n> > + * \\return The uint read from \\a vec\n> > + */\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> > + * \tstd::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>::iterator it1,\n> > + * \tstd::vector<uint8_t>::iterator it2,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] it1 Begin iterator of byte vector to deserialize from\n> > + * \\param[in] it2 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> > + * \tstd::vector<uint8_t> &data,\n> > + * \tstd::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>::iterator data_it1,\n> > + * \tstd::vector<uint8_t>::iterator data_it2,\n> > + * \tstd::vector<int32_t>::iterator fds_it1,\n> > + * \tstd::vector<int32_t>::iterator fds_it2,\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_it1 Begin iterator of byte vector to deserialize from\n> > + * \\param[in] data_it2 End iterator of byte vector to deserialize from\n> > + * \\param[in] fds_it1 Begin iterator of fd vector to deserialize from\n> > + * \\param[in] fds_it2 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> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 9d7442fa..61aad08e 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -23,6 +23,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',\n> > --\n> > 2.27.0\n> >\n> > _______________________________________________\n> > libcamera-devel mailing list\n> > libcamera-devel@lists.libcamera.org\n> > https://lists.libcamera.org/listinfo/libcamera-devel","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 9C6C3C3B5B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 24 Sep 2020 07:17:10 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0EE4362FE3;\n\tThu, 24 Sep 2020 09:17:10 +0200 (CEST)","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 F0E2160362\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 24 Sep 2020 09:17:08 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C070C2FD;\n\tThu, 24 Sep 2020 09:17:06 +0200 (CEST)"],"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=\"DlAB8d6M\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1600931828;\n\tbh=x2nieaJeEnjHHA4SP2HQj7hz+c/mNwscwWtR/rVDQ2o=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=DlAB8d6M9eikId8u0YP1/5EICFT/Zn0Ol3aBMfOXuoCKQwrQSFMpGz0AbVXs8Fplj\n\tjj1MymudkZVQKgcLIKJ8sgs0J7GJtfwhcAXlfYn2fppFF5azh9hawSTdkypNVMkDnK\n\tmGnk0Pn3kpgC8SEARJcl77kQkbqlCld6uejdHLfY=","Date":"Thu, 24 Sep 2020 16:17:00 +0900","From":"paul.elder@ideasonboard.com","To":"Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<20200924071700.GL45948@pyrite.rasen.tech>","References":"<20200922133537.258098-1-paul.elder@ideasonboard.com>\n\t<20200922133537.258098-14-paul.elder@ideasonboard.com>\n\t<20200923153218.gkfk5rfbzrqxakas@uno.localdomain>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200923153218.gkfk5rfbzrqxakas@uno.localdomain>","Subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","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=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":12717,"web_url":"https://patchwork.libcamera.org/comment/12717/","msgid":"<20200924095256.lbkxwdpxgrm67yo5@uno.localdomain>","date":"2020-09-24T09:52:56","subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Paul,\n\nOn Thu, Sep 24, 2020 at 04:17:00PM +0900, paul.elder@ideasonboard.com wrote:\n> Hi Jacopo,\n>\n> On Wed, Sep 23, 2020 at 05:32:18PM +0200, Jacopo Mondi wrote:\n> > Hi Paul,\n> >\n> > On Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:\n> > > Add an IPADataSerializer which implments de/serialization of built-in\n> >\n> > With built-in do you mean STL defined data types ?\n>\n> Not quite. Just PODs, vector, map, and string. I'll specify them.\n>\n\nThanks, that makes it clear which types we support\n\n> > > and libcamera data structures. This is intended to be used by the proxy\n> > > and the proxy worker in the IPC layer.\n> > >\n> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > >\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  | 1220 +++++++++++++++++\n> > >  src/libcamera/ipa_data_serializer.cpp         |  154 +++\n> > >  src/libcamera/meson.build                     |    1 +\n> > >  3 files changed, 1375 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..accd4dce\n> > > --- /dev/null\n> > > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > > @@ -0,0 +1,1220 @@\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 <tuple>\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> > > +template<typename T>\n> > > +void appendUInt(std::vector<uint8_t> &vec, T val)\n> > > +{\n> > > +\tsize_t byteWidth = sizeof(val);\n> > > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > > +\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n> >\n> > I see in several places missing spaces around the '*' operator\n> >\n> > I think you can also drop the & 0xff as it's implied in the cast to\n> > uint8_t?\n>\n> I ended up replacing this (and readUInt) with memcpys, as suggested with\n> the floats. That allows us to have DECLARE_POD_SERIALIZER for bool,\n> float, double, as well as all of the ints and uints :)\n>\n\nGreat!\n\n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> > > +{\n> > > +\tT ret = 0;\n> > > +\tsize_t byteWidth = sizeof(ret);\n> > > +\tif (pos + byteWidth > vec.size())\n> > > +\t\treturn ret;\n> > > +\n> > > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > > +\t\tret |= vec[pos + i] << 8*i;\n> >\n> > You could iterate the vector and call the below defined overload. Not\n> > a big deal, it would be probably more confusing.\n> >\n> > > +\treturn ret;\n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +T readUInt(std::vector<uint8_t>::iterator it)\n> > > +{\n> > > +\tT ret = 0;\n> > > +\tsize_t byteWidth = sizeof(ret);\n> > > +\tfor (size_t i = 0; i < byteWidth; i++)\n> > > +\t\tret |= *(it + i) << 8*i;\n> > > +\treturn ret;\n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +class IPADataSerializer\n> > > +{\n> > > +#ifdef __DOXYGEN__\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(T data, ControlSerializer *cs);\n> > > +\n> > > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t     ControlSerializer *cs);\n> >\n> > That *cs parameter is unused in all specializations but ControlList\n> > one. We already had to face a similar issue when we implemented\n> > ControlValue. You could define two versions of these functions one for\n> > the ControlList case which has *cs in its signature, the other for all\n> > the other types, without cs. The construct to do so is not nice at a\n> > first look but effective:\n> > https://en.cppreference.com/w/cpp/types/enable_if\n> >\n> > I think it could be applied here, maybe on top ?\n>\n> I don't think we can quite do so. ControlSerializer and the fd functions\n> are necessary if any descendent of the struct needs it. This means a\n> simple enable_if won't we sufficient, and it would have to check the\n> encapsulation tree.\n>\n> On the other hand, at code-generation time (for generating the\n> serializer), it is known which structs need ControlSerializer/fd, so in\n> theory we could prune the parameters and functions at that point. The\n> users of the serializers are also known at code-generation time, so the\n> calls can be matched as well.\n>\n> The problem is in the de/serializer for vector and map. For vector\n> serialization:\n>\n> \tIPADataSerializer<V>::serialize(*it, cs);\n>\n> And for deserialization:\n>\n> \tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n> \t\t\t\t\t\t   data_it + 8 + sizeof_data,\n> \t\t\t\t\t\t   fd_it,\n> \t\t\t\t\t\t   fd_it + sizeof_fds,\n> \t\t\t\t\t\t   cs);\n>\n> The vector and map de/serializers don't know if the struct that they\n> contain needs the ControlSerializer/fd, that's why we have to pass it in\n> regardless, and that's why all IPADataSerializers must accept them as\n> parameters. enable_if might be useful here, but again that only works if\n> the struct that the vector/map contains is the one that needs the\n> ControlSerializer/fd; if the struct doesn't need it but its children do,\n> then we'd have to do nested enable_if.\n>\n\nAs we have clarified, enable_if<> would only help you in overloading\non the return type of the 'serialize()' function.\nFor 'deserialize()' you could probably get away with overloads.\n\nAs you point out, the real issue is in the map/vector deserializer,\nwhere you would need to define a specialization for FileDescriptor\n(whose serialize() return type has to be a tuple, while for others\ntypes it might just be data) and for ControlList/ControlInfoMap (whose\ndeserialze() has an additional cs parameter, while the other types is\nnot required).\n\nLooking at the generated code for the IPADataSerializer<>\nspecialization for the custom IPA types you already have the notion of\nwhen the fds member of the tuple returned by serialize is not needed\n(as you place std::ignore there) and where the cs parameter to\ndeserialize() is not used (as it is marked with [[maybe_unused]]).\n\nThat said, I am not sure it is worth making a specialization of the\nmap/vector IPADataSerializer<> specialization, as this is generated\ncode and the API towards pipelines and IPAs is not impacted by the\nadditional parameter requirements.\n\nAll in all, I think what you have here is fine :)\n\n> > > +\tstatic T deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t     std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t     ControlSerializer *cs);\n> > > +\n> > > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t     std::vector<int32_t> &fds,\n> > > +\t\t\t     ControlSerializer *cs);\n> > > +\tstatic T deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t     ControlSerializer *cs);\n> >\n> > Where can I see this used ? As I wonder why this always take one\n\nSeems like I left the question half written...\nI think what I wanted to ask was if all of these methods needs to be\nmade public...\n\n>\n> All of these functions are meant to be used by the proxy and the proxy\n> worker, so they can be seen in the template code :)\n>\n> This last one can be seen in the generated code for the raspberrypi\n> proxy worker:\n>\n>        \tRPiConfigInput ipaConfig = IPADataSerializer<RPiConfigInput>::deserialize(\n>                \t_message.data.begin() + ipaConfigStart,\n>                \t_message.data.end(),\n>                \t_message.fds.begin() + ipaConfigFdStart,\n>                \t_message.fds.end());\n\nBut looking at this example, it seems the answer is 'yes'\n\n>\n> Where _message is a IPCUnixSocket::Payload, which carries a data vector\n> and an fd vector.\n>\n> > > +#endif /* __DOXYGEN__ */\n> > > +};\n> > > +\n> > > +#ifndef __DOXYGEN__\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> data_vec;\n> > > +\t\tstd::vector<int32_t> fds_vec;\n> > > +\n> > > +\t\t// serialize the length\n> >\n> > // ?\n>\n> ack\n>\n> > > +\t\tuint32_t vec_len = data.size();\n> > > +\t\tappendUInt<uint32_t>(data_vec, vec_len);\n> > > +\n> > > +\t\t// serialize the members\n> > > +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > > +\n> > > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> > > +\t\t}\n> > > +\n> > > +\t\treturn {data_vec, fds_vec};\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<int32_t> fds;\n> > > +\t\treturn IPADataSerializer<std::vector<V>>::deserialize(it1, it2,\n> > > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > > +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),\n> > > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > > +\t\t\t\t\t\t\t\t      cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tuint32_t vec_len = readUInt<uint32_t>(data_it1);\n> > > +\t\tstd::vector<V> ret(vec_len);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> > > +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> > > +\t\tfor (uint32_t i = 0; i < vec_len; i++) {\n> > > +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> > > +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > > +\n> > > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n> > > +\t\t\t\t\t\t\t\t   data_it + 8 + sizeof_data,\n> > > +\t\t\t\t\t\t\t\t   fd_it,\n> > > +\t\t\t\t\t\t\t\t   fd_it + sizeof_fds,\n> > > +\t\t\t\t\t\t\t\t   cs);\n> > > +\n> > > +\t\t\tdata_it += 8 + sizeof_data;\n> > > +\t\t\tfd_it += sizeof_fds;\n> > > +\t\t}\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\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> data_vec;\n> > > +\t\tstd::vector<int32_t> fds_vec;\n> > > +\n> > > +\t\t// serialize the length\n> > > +\t\tuint32_t map_len = data.size();\n> > > +\t\tappendUInt<uint32_t>(data_vec, map_len);\n> > > +\n> > > +\t\t// serialize the members\n> > > +\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > > +\n> > > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfds_vec.insert(fds_vec.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\tappendUInt<uint32_t>(data_vec, dvec.size());\n> > > +\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n> > > +\n> > > +\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n> > > +\t\t}\n> > > +\n> > > +\t\treturn {data_vec, fds_vec};\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<int32_t> fds;\n> > > +\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,\n> > > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > > +\t\t\t\t\t\t\t\t      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 IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),\n> > > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n> > > +\t\t\t\t\t\t\t\t      cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::map<K, V> ret;\n> > > +\n> > > +\t\tuint32_t map_len = readUInt<uint32_t>(data_it1);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n> > > +\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n> > > +\t\tfor (uint32_t i = 0; i < map_len; i++) {\n> > > +\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n> > > +\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > > +\n> > > +\t\t\tK key = IPADataSerializer<K>::deserialize(data_it + 8,\n> > > +\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> > > +\t\t\t\t\t\t\t\t  fd_it,\n> > > +\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> > > +\t\t\t\t\t\t\t\t  cs);\n> > > +\n> > > +\t\t\tdata_it += 8 + sizeof_data;\n> > > +\t\t\tfd_it += sizeof_fds;\n> > > +\t\t\tsizeof_data = readUInt<uint32_t>(data_it);\n> > > +\t\t\tsizeof_fds  = readUInt<uint32_t>(data_it + 4);\n> > > +\n> > > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(data_it + 8,\n> > > +\t\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n> > > +\t\t\t\t\t\t\t\t\t  fd_it,\n> > > +\t\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n> > > +\t\t\t\t\t\t\t\t\t  cs);\n> > > +\t\t\tret.insert({key, value});\n> > > +\n> > > +\t\t\tdata_it += 8 + sizeof_data;\n> > > +\t\t\tfd_it += sizeof_fds;\n> > > +\t\t}\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\n> > > +\n> > > +#define DECLARE_INTEGRAL_SERIALIZER(type)\t\t\t\t\\\n> > > +template<>\t\t\t\t\t\t\t\t\\\n> > > +class IPADataSerializer<type>\t\t\t\t\t\t\\\n> > > +{\t\t\t\t\t\t\t\t\t\\\n> > > +public:\t\t\t\t\t\t\t\t\t\\\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\t\\\n> > > +\tserialize(const type data,\t\t\t\t\t\\\n> > > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\t\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\tstd::vector<uint8_t> data_vec;\t\t\t\t\\\n> > > +\t\tappendUInt<type>(data_vec, data);\t\t\t\\\n> > > +\t\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn {data_vec, {}};\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> > > +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> > > +\t\t\t\t\t\t\t    data.end());\\\n> > > +\t}\t\t\t\t\t\t\t\t\\\n> > > +\t\t\t\t\t\t\t\t\t\\\n> > > +\tstatic type deserialize(std::vector<uint8_t>::iterator it1,\t\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn readUInt<type>(it1);\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> > > +\tstatic type deserialize(std::vector<uint8_t> &data,\t\t\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn IPADataSerializer<type>::deserialize(data.begin(),\\\n> > > +\t\t\t\t\t\t\t    data.end());\\\n> > > +\t}\t\t\t\t\t\t\t\t\\\n> > > +\t\t\t\t\t\t\t\t\t\\\n> > > +\tstatic type deserialize(std::vector<uint8_t>::iterator data_it1,\\\n> > > +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn IPADataSerializer<type>::deserialize(data_it1,\t\\\n> > > +\t\t\t\t\t\t\t    data_it2);\t\\\n> > > +\t}\t\t\t\t\t\t\t\t\\\n> > > +};\n> > > +\n> > > +DECLARE_INTEGRAL_SERIALIZER(uint8_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(uint16_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(uint32_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(uint64_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(int8_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(int16_t)\n> > > +DECLARE_INTEGRAL_SERIALIZER(int32_t)\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<int64_t>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const int64_t data,\n> > > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\t\tappendUInt<uint64_t>(data_vec, data);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic int64_t deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<int64_t>::deserialize(data.begin(),\n> > > +\t\t\t\t\t\t\t       data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t   [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tif (std::distance(it1, it2) < 8) {\n> > > +\t\t\tLOG(IPADataSerializer, Info)\n> > > +\t\t\t\t<< \"Not enough bytes to deserialize int64_t, returning 0\";\n> > > +\t\t\treturn 0;\n> > > +\t\t}\n> > > +\n> > > +\t\tuint32_t lower = readUInt<uint32_t>(it1);\n> > > +\t\tuint32_t upper = readUInt<uint32_t>(it1 + 4);\n> > > +\n> > > +\t\treturn lower + (uint64_t(upper) << 32);\n> > > +\t}\n> > > +\n> > > +\tstatic int64_t 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 IPADataSerializer<int64_t>::deserialize(data.begin(),\n> > > +\t\t\t\t\t\t\t       data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic int64_t deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t   std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t   [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> >\n> > In what is the int64_t specialization different that the other ones\n> > defined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you\n> > don't have a  readUInt<int64_t> ?\n>\n> Yeah... I was having bit shifting problems with readUInt<int64_t>.\n> I reimplemented it with memcpy though, and now the macro can generate\n> code for serializing int64_t, bool, float, and double as well :)\n>\n\nGreat!\n\nVery nice overall!!\n\n\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<bool>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const bool data,\n> > > +\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\t\tappendUInt<uint8_t>(data_vec, data);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic bool deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<bool>::deserialize(data.begin(),\n> > > +\t\t\t\t\t\t\t    data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic bool deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn readUInt<uint8_t>(it1);\n> >\n> > The sizeof(bool) seems to be a bit an issue actually... Can you solve\n> > it by declarting a\n> >\n> > template<> bool readUInt(std::vector<uint8_t>::iterator it)\n> > ?\n> >\n> > Actually it seems to me the current implementation might work with\n> > bool too...\n> >\n> > > +\t}\n> > > +\n> > > +\tstatic bool 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 IPADataSerializer<bool>::deserialize(data.begin(),\n> > > +\t\t\t\t\t\t\t    data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic bool deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<bool>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<float>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tuint8_t arr[4];\n> > > +\t\tmemcpy(arr, &data, sizeof(arr));\n> >\n> > I wonder if we should no be using memcpy in the readUInt() function\n> > too. Would this help you with floats ?\n>\n> Apparently yes.\n>\n>\n> Thanks,\n>\n> Paul\n> > > +\n> > > +\t\tstd::vector<uint8_t> data_vec(arr, arr + 4);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic float deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic float deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tif (std::distance(it1, it2) < 4) {\n> > > +\t\t\tLOG(IPADataSerializer, Info)\n> > > +\t\t\t\t<< \"Not enough bytes to deserialize float, returning 0\";\n> > > +\t\t\treturn 0;\n> > > +\t\t}\n> > > +\n> > > +\t\tstd::vector data(it1, it2);\n> > > +\t\tfloat ret;\n> > > +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic float 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 IPADataSerializer<float>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic float deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<float>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<double>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tuint8_t arr[8];\n> > > +\t\tmemcpy(arr, &data, sizeof(arr));\n> > > +\n> > > +\t\tstd::vector<uint8_t> data_vec(arr, arr + 8);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic double deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic double deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tif (std::distance(it1, it2) < 8) {\n> > > +\t\t\tLOG(IPADataSerializer, Info)\n> > > +\t\t\t\t<< \"Not enough bytes to deserialize double, returning 0\";\n> > > +\t\t\treturn 0;\n> > > +\t\t}\n> > > +\n> > > +\t\tstd::vector data(it1, it2);\n> > > +\t\tdouble ret;\n> > > +\t\tmemcpy(&ret, data.data(), sizeof(ret));\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic double 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 IPADataSerializer<double>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic double deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<double>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\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\tstd::vector<uint8_t> data_vec(data.begin(), data.end());\n> > > +\n> > > +\t\treturn {data_vec, {}};\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::string str(it1, it2);\n> > > +\n> > > +\t\treturn str;\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 IPADataSerializer<std::string>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<std::string>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<FileDescriptor>\n> >\n> > Ah, that's why you have the fds parameter in deserializer and the\n> > std::vector<int32_t> in the serializer return parameter.\n> >\n> > I think this would be cleaner if we could use enable_if and define an\n> > ad-hoc function signature for FileDescriptor and ControlList. It would\n> > remove the fds and cs parameters from all other types (the ones I've\n> > see so far at least).\n> >\n> > I think I'll stop here, there's enough to discuss on\n> > Thanks\n> >    j\n> >\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> data_vec = { data.isValid() };\n> > > +\t\tstd::vector<int32_t> fd_vec;\n> > > +\t\tif (data.isValid())\n> > > +\t\t\tfd_vec.push_back(data.fd());\n> > > +\n> > > +\t\treturn {data_vec, fd_vec};\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 IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),\n> > > +\t\t\t\t\t\t\t\t      fds.begin(), fds.end());\n> > > +\t}\n> > > +\n> > > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tif (std::distance(data_it1, data_it2) < 1)\n> > > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > > +\n> > > +\t\tbool valid = *data_it1;\n> > > +\n> > > +\t\tif (valid && std::distance(fds_it1, fds_it2) < 1)\n> > > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > > +\n> > > +\t\treturn valid ? FileDescriptor(*fds_it1) : FileDescriptor();\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<IPASettings>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<std::string>::serialize(data.configurationFile);\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tIPASettings ret;\n> > > +\t\tret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings 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 IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<CameraSensorInfo>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\n> > > +\t\tuint32_t str_len = data.model.size();\n> > > +\t\tappendUInt<uint32_t>(data_vec, str_len);\n> > > +\n> > > +\t\tdata_vec.insert(data_vec.end(), data.model.begin(), data.model.end());\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.bitsPerPixel);\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.width);\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.height);\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));\n> > > +\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.width);\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.height);\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.width);\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.outputSize.height);\n> > > +\n> > > +\t\tappendUInt<uint64_t>(data_vec, data.pixelRate);\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.lineLength);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tCameraSensorInfo ret;\n> > > +\n> > > +\t\tuint32_t str_len = readUInt<uint32_t>(it1);\n> > > +\t\tstd::string str(it1 + 4, it1 + 4 + str_len);\n> > > +\t\tret.model = str;\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = it1 + 4 + str_len;\n> > > +\n> > > +\t\tret.bitsPerPixel = readUInt<uint32_t>(it);\n> > > +\n> > > +\t\tret.activeAreaSize.width = readUInt<uint32_t>(it + 4);\n> > > +\t\tret.activeAreaSize.height = readUInt<uint32_t>(it + 8);\n> > > +\n> > > +\t\tret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));\n> > > +\t\tret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));\n> > > +\t\tret.analogCrop.width = readUInt<uint32_t>(it + 20);\n> > > +\t\tret.analogCrop.height = readUInt<uint32_t>(it + 24);\n> > > +\n> > > +\t\tret.outputSize.width = readUInt<uint32_t>(it + 28);\n> > > +\t\tret.outputSize.height = readUInt<uint32_t>(it + 32);\n> > > +\n> > > +\t\tret.pixelRate = readUInt<uint64_t>(it + 36);\n> > > +\n> > > +\t\tret.lineLength = readUInt<uint64_t>(it + 44);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo 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    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t    std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<IPAStream>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.pixelFormat);\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.size.width);\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.size.height);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tIPAStream ret;\n> > > +\n> > > +\t\tret.pixelFormat = readUInt<uint32_t>(it1);\n> > > +\n> > > +\t\tret.size.width = readUInt<uint32_t>(it1 + 4);\n> > > +\t\tret.size.height = readUInt<uint32_t>(it1 + 8);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream 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 IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<const ControlList>\n> > > +{\n> > > +public:\n> > > +\t// map arg will be generated, since it's per-pipeline anyway\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const ControlList &data, const ControlInfoMap &map,\n> > > +\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 serialization of ControlList\";\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 ControlList's ControlInfoMap\";\n> > > +\t\t\treturn {{}, {}};\n> > > +\t\t}\n> > > +\n> > > +\t\tsize = cs->binarySize(data);\n> > > +\t\tstd::vector<uint8_t> listData(size);\n> > > +\t\tbuffer = ByteStreamBuffer(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> data_vec;\n> > > +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> > > +\t\tappendUInt<uint32_t>(data_vec, listData.size());\n> > > +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> > > +\t\tdata_vec.insert(data_vec.end(), listData.begin(), listData.end());\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> > > +\t\tuint32_t listDataSize = readUInt<uint32_t>(it1 + 4);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = it1 + 8;\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\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> > > +\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> > > +\t\t/* It's fine if map is empty. */\n> > > +\t\tif (buffer.overflow()) {\n> > > +\t\t\tLOG(IPADataSerializer, Error)\n> > > +\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> > > +\t\t\treturn ControlList();\n> > > +\t\t}\n> > > +\n> > > +\t\tbuffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());\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> > > +\t\tif (list.empty())\n> > > +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: empty list\";\n> > > +\n> > > +\t\treturn list;\n> > > +\t}\n> > > +\n> > > +\tstatic const 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 IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);\n> > > +\t}\n> > > +};\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, const ControlInfoMap &map,\n> > > +\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tconst ControlList &list_const = const_cast<const ControlList &>(data);\n> > > +\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\t\tstd::tie(data_vec, std::ignore) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic ControlList deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlList ret;\n> > > +\t\tconst ControlList &list = ret;\n> > > +\t\tconst_cast<ControlList &>(list) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlList ret;\n> > > +\t\tconst ControlList &list = ret;\n> > > +\t\tconst_cast<ControlList &>(list) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::deserialize(it1, it2, cs);\n> > > +\n> > > +\t\treturn ret;\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\tControlList ret;\n> > > +\t\tconst ControlList &list = ret;\n> > > +\t\tconst_cast<ControlList &>(list) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data, fds, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlList ret;\n> > > +\t\tconst ControlList &list = ret;\n> > > +\t\tconst_cast<ControlList &>(list) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,\n> > > +\t\t\t\t\t\t\t\t\t  fds_it1, fds_it2, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<const 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\treturn {{}, {}};\n> > > +\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n> > > +\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t\tControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t\t\tControlSerializer *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\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = it1 + 4;\n> > > +\n> > > +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> > > +\n> > > +\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> > > +\t\tconst ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> > > +\n> > > +\t\treturn map;\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t\tControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);\n> > > +\t}\n> > > +};\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, [[maybe_unused]] ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tconst ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);\n> > > +\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\t\tstd::tie(data_vec, std::ignore) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > > +\n> > > +\t\treturn {data_vec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlInfoMap ret;\n> > > +\t\tconst ControlInfoMap &map = ret;\n> > > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n> > > +\t\t\t\t\t\tControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlInfoMap ret;\n> > > +\t\tconst ControlInfoMap &map = ret;\n> > > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t\tControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlInfoMap ret;\n> > > +\t\tconst ControlInfoMap &map = ret;\n> > > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tControlInfoMap ret;\n> > > +\t\tconst ControlInfoMap &map = ret;\n> > > +\t\tconst_cast<ControlInfoMap &>(map) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,\n> > > +\t\t\t\t\t\t\t\t\t     fds_it1, fds_it2, cs);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\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> data_vec;\n> > > +\t\tstd::vector<int32_t> fds_vec;\n> > > +\n> > > +\t\t// fd\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\tdata_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());\n> > > +\t\tfds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());\n> > > +\n> > > +\t\t// length\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.length);\n> > > +\n> > > +\t\treturn {data_vec, fds_vec};\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 IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),\n> > > +\t\t\t\t\t\t\t\t\t  fds.begin(), fds.end(),\n> > > +\t\t\t\t\t\t\t\t\t  cs);\n> > > +\t}\n> > > +\n> > > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t\t      std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\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(data_it1, data_it1 + 1,\n> > > +\t\t\t\t\t\t\t\t\tfds_it1, fds_it1 + 1);\n> > > +\t\tret.length = readUInt<uint32_t>(data_it1 + 1);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\n> > > +\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<IPABuffer>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const IPABuffer &data, ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> data_vec;\n> > > +\n> > > +\t\tappendUInt<uint32_t>(data_vec, data.id);\n> > > +\n> > > +\t\tstd::vector<uint8_t> planes_data_vec;\n> > > +\t\tstd::vector<int32_t> planes_fds_vec;\n> > > +\t\tstd::tie(planes_data_vec, planes_fds_vec) =\n> > > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > > +\n> > > +\t\tdata_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());\n> > > +\n> > > +\t\treturn {data_vec, planes_fds_vec};\n> > > +\t}\n> > > +\n> > > +\tstatic IPABuffer deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t     std::vector<int32_t> &fds,\n> > > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),\n> > > +\t\t\t\t\t\t\t\t fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,\n> > > +\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n> > > +\t\t\t\t     std::vector<int32_t>::iterator fds_it1,\n> > > +\t\t\t\t     std::vector<int32_t>::iterator fds_it2,\n> > > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tIPABuffer ret;\n> > > +\n> > > +\t\tret.id = readUInt<uint32_t>(data_it1);\n> > > +\n> > > +\t\tret.planes =\n> > > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > > +\t\t\t\tdata_it1 + 4, data_it2, fds_it1, fds_it2, cs);\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..5029cdf6\n> > > --- /dev/null\n> > > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > > @@ -0,0 +1,154 @@\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> > > +\n> > > +/**\n> > > + * \\fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)\n> > > + * \\brief Append uint to end of byte vector, in little-endian order\n> > > + * \\tparam T Type of uint to append\n> > > + * \\param[in] vec Byte vector to append to\n> > > + * \\param[in] val Value of uint to append\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)\n> > > + * \\brief Read uint from byte vector, in little-endian order\n> > > + * \\tparam T Type of uint to read\n> > > + * \\param[in] vec Byte vector to read from\n> > > + * \\param[in] pos Index in vec to start reading from\n> > > + *\n> > > + * \\return The uint read from \\a vec, or 0 if reading goes past end of \\a vec\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)\n> > > + * \\brief Read uint from byte vector, in little-endian order\n> > > + * \\tparam T Type of uint to read\n> > > + * \\param[in] it Iterator of byte vector to read from\n> > > + *\n> > > + * \\return The uint read from \\a vec\n> > > + */\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> > > + * \tstd::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>::iterator it1,\n> > > + * \tstd::vector<uint8_t>::iterator it2,\n> > > + * \tControlSerializer *cs = nullptr)\n> > > + * \\brief Deserialize byte vector into an object\n> > > + * \\tparam T Type of object to deserialize to\n> > > + * \\param[in] it1 Begin iterator of byte vector to deserialize from\n> > > + * \\param[in] it2 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> > > + * \tstd::vector<uint8_t> &data,\n> > > + * \tstd::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>::iterator data_it1,\n> > > + * \tstd::vector<uint8_t>::iterator data_it2,\n> > > + * \tstd::vector<int32_t>::iterator fds_it1,\n> > > + * \tstd::vector<int32_t>::iterator fds_it2,\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_it1 Begin iterator of byte vector to deserialize from\n> > > + * \\param[in] data_it2 End iterator of byte vector to deserialize from\n> > > + * \\param[in] fds_it1 Begin iterator of fd vector to deserialize from\n> > > + * \\param[in] fds_it2 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> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > index 9d7442fa..61aad08e 100644\n> > > --- a/src/libcamera/meson.build\n> > > +++ b/src/libcamera/meson.build\n> > > @@ -23,6 +23,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',\n> > > --\n> > > 2.27.0\n> > >\n> > > _______________________________________________\n> > > libcamera-devel mailing list\n> > > libcamera-devel@lists.libcamera.org\n> > > https://lists.libcamera.org/listinfo/libcamera-devel","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 2017DC3B5C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 24 Sep 2020 09:49:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 91B4F62FF8;\n\tThu, 24 Sep 2020 11:49:05 +0200 (CEST)","from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net\n\t[217.70.183.195])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CE86D62FD8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 24 Sep 2020 11:49:03 +0200 (CEST)","from uno.localdomain (93-34-118-233.ip49.fastwebnet.it\n\t[93.34.118.233]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 1554E6000B;\n\tThu, 24 Sep 2020 09:49:02 +0000 (UTC)"],"X-Originating-IP":"93.34.118.233","Date":"Thu, 24 Sep 2020 11:52:56 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"paul.elder@ideasonboard.com","Message-ID":"<20200924095256.lbkxwdpxgrm67yo5@uno.localdomain>","References":"<20200922133537.258098-1-paul.elder@ideasonboard.com>\n\t<20200922133537.258098-14-paul.elder@ideasonboard.com>\n\t<20200923153218.gkfk5rfbzrqxakas@uno.localdomain>\n\t<20200924071700.GL45948@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200924071700.GL45948@pyrite.rasen.tech>","Subject":"Re: [libcamera-devel] [PATCH 13/38] libcamera: Add IPADataSerializer","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=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]