[{"id":13687,"web_url":"https://patchwork.libcamera.org/comment/13687/","msgid":"<20201112112501.GI1480295@oden.dyn.berto.se>","date":"2020-11-12T11:25:01","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Paul,\n\nThanks for your patch.\n\nOn 2020-11-06 19:36:38 +0900, Paul Elder wrote:\n> Add an IPADataSerializer which implments de/serialization of built-in\n> (PODs, vector, map, string) and libcamera data structures. This is\n> intended to be used by the proxy and the proxy worker in the IPC layer.\n\nAs this is a rather large patch in a rather large series I wonder if \nthis could be broken out into it's own series together with it's test \ncases. Perhaps this patch could even be broken up to smaller bits each \ndealing with a specific data type?\n\nIf so I think this together with the tests could be merged as soon as \nthey are ready and just listed as a dependency to the main series. What \ndo others think about this?\n\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n> \n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n> \n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n>  src/libcamera/meson.build                     |    1 +\n>  3 files changed, 1183 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..9a18f914\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,1004 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T>\n> +void appendPOD(std::vector<uint8_t> &vec, T val)\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n> +\tstd::vector<uint8_t> v(byteWidth);\n> +\tmemcpy(&v[0], &val, byteWidth);\n> +\n> +\tvec.insert(vec.end(), v.begin(), v.end());\n> +}\n> +\n> +template<typename T>\n> +T readPOD(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\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n> +\n> +\tmemcpy(&ret, &vec[pos], byteWidth);\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> +\t  std::vector<uint8_t>::iterator end)\n> +{\n> +\tT ret = 0;\n> +\tsize_t byteWidth = sizeof(ret);\n> +\n> +\tif (pos + it >= end)\n> +\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n> +\n> +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> +\tmemcpy(&ret, v.data(), byteWidth);\n> +\treturn ret;\n> +}\n> +\n> +} /* namespace */\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> +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs);\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +};\n> +\n> +DECLARE_POD_SERIALIZER(bool)\n> +DECLARE_POD_SERIALIZER(uint8_t)\n> +DECLARE_POD_SERIALIZER(uint16_t)\n> +DECLARE_POD_SERIALIZER(uint32_t)\n> +DECLARE_POD_SERIALIZER(uint64_t)\n> +DECLARE_POD_SERIALIZER(int8_t)\n> +DECLARE_POD_SERIALIZER(int16_t)\n> +DECLARE_POD_SERIALIZER(int32_t)\n> +DECLARE_POD_SERIALIZER(int64_t)\n> +DECLARE_POD_SERIALIZER(float)\n> +DECLARE_POD_SERIALIZER(double)\n> +\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {{data.begin(), data.end()}, {}};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn {dataVec, fdVec};\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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> dataVec;\n> +\n> +\t\tuint32_t strLen = data.model.size();\n> +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> +\n> +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> +\n> +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tCameraSensorInfo ret;\n> +\n> +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> +\t\tret.model = str;\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> +\n> +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> +\n> +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> +\n> +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> +\n> +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> +\n> +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> +\n> +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPAStream ret;\n> +\n> +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> +\n> +\t\treturn {dataVec, {}};\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 dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\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 dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> +\n> +\t\tstd::vector<uint8_t> planesDataVec;\n> +\t\tstd::vector<int32_t> planesFdsVec;\n> +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> +\n> +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> +\n> +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPABuffer ret;\n> +\n> +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.planes =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,178 @@\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> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(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> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tstd::vector<int32_t>::iterator fdsBegin,\n> + * \tstd::vector<int32_t>::iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 99b27e66..01bcffd4 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -24,6 +24,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 99A02BE082\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 12 Nov 2020 11:25:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1790D63167;\n\tThu, 12 Nov 2020 12:25:05 +0100 (CET)","from mail-lj1-x235.google.com (mail-lj1-x235.google.com\n\t[IPv6:2a00:1450:4864:20::235])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 16ED66310F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 12 Nov 2020 12:25:04 +0100 (CET)","by mail-lj1-x235.google.com with SMTP id 11so5710813ljf.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 12 Nov 2020 03:25:04 -0800 (PST)","from localhost (h-209-203.A463.priv.bahnhof.se. [155.4.209.203])\n\tby smtp.gmail.com with ESMTPSA id\n\tw20sm134251lfl.50.2020.11.12.03.25.01\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 12 Nov 2020 03:25:02 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=ragnatech-se.20150623.gappssmtp.com\n\theader.i=@ragnatech-se.20150623.gappssmtp.com\n\theader.b=\"z9dKQ9Tp\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=oVBn3ZUhho+FEp1aaZrh9en/o5n6oMHio8nYa5t1u5o=;\n\tb=z9dKQ9TpdwI1vEaQFE4Y8O+6i+KVfp4jr3YOd97ThBpm4VQn579xOwErZBNK6neJwJ\n\tuWshcW2GRBY4r/NlO1d7tgsrEifDGDiNiL6O335XI5tEww0mRrGeULh3IM7KPLaFoQJK\n\tCvbAR8IOI8Z/HCC7NWUrX8umlI84NlS59X1GtgkXmGWVR+nMKRSpOBASrp40F2a4NsWe\n\t9tAX9W8Fv6En72r3N+oXuaX0NELw4gwE2PU7goInEgtDevAY2eqD9HikuD3YdYL/oFNc\n\tAOj+1apvm+fH76BhJV+IWcKVYUpUPZMP8ZP5u224M0gBh9CD3ONrnil3kTYTCQx4wsC3\n\tPmYQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=oVBn3ZUhho+FEp1aaZrh9en/o5n6oMHio8nYa5t1u5o=;\n\tb=JFUjdyV1O4ooL0zySXDQn2e2RBek9/6CjpwzGAlqRRNV2Rha/ztns7PSSACgs0U+Ix\n\t0ZgAgji0LDxsVJHD2c9yL9gO4GyYaOkZAh2wMw/nQMSMYTXVjqH0UQlCDtaZgv368ygY\n\t7BTToB4AVjMWtgmhYlXWtJhtuigG7Wt9wTWhcz1zBTTmqN3mcgCRiXFUqUAwznQK+Lqc\n\tIXA6bkszkYl/+WhOYGCkYuOYZLl0B1oP8IgjCzUjiC9yPncEroSqiS2btBjJwdxyPrK5\n\t0nUotTvo3yi/yOP6OJZZtAQ3gYAbNnI+DjS1JASCk89/RpTiIyYxXS3WrMVnG77rCbCR\n\timOw==","X-Gm-Message-State":"AOAM533k/C7ZqXhqt7AVlPgLLHQiKf96/rE+ZBQaKfHU6+pmVUPr2tfK\n\tSEEQUffks9yFQJLTxvSDMkgJCi5Q7Wmc5w==","X-Google-Smtp-Source":"ABdhPJwQtBQtJA+zw8+CJW7LT0z6Fga9pPJAT7xxEBp8q5KrdPsJX/N0uC9V/RG4ZdaNQcB4sCGEJQ==","X-Received":"by 2002:a2e:804b:: with SMTP id p11mr9604278ljg.54.1605180303162;\n\tThu, 12 Nov 2020 03:25:03 -0800 (PST)","Date":"Thu, 12 Nov 2020 12:25:01 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20201112112501.GI1480295@oden.dyn.berto.se>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201106103707.49660-9-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"iso-8859-1\"","Content-Transfer-Encoding":"quoted-printable","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13694,"web_url":"https://patchwork.libcamera.org/comment/13694/","msgid":"<20201113064148.GD1811@pyrite.rasen.tech>","date":"2020-11-13T06:41:48","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Niklas,\n\nOn Thu, Nov 12, 2020 at 12:25:01PM +0100, Niklas Söderlund wrote:\n> Hi Paul,\n> \n> Thanks for your patch.\n> \n> On 2020-11-06 19:36:38 +0900, Paul Elder wrote:\n> > Add an IPADataSerializer which implments de/serialization of built-in\n> > (PODs, vector, map, string) and libcamera data structures. This is\n> > intended to be used by the proxy and the proxy worker in the IPC layer.\n> \n> As this is a rather large patch in a rather large series I wonder if \n> this could be broken out into it's own series together with it's test \n> cases. Perhaps this patch could even be broken up to smaller bits each \n> dealing with a specific data type?\n\nMaybe? It's like:\n- read/appendPOD()\n- base de/serializer (to appease doxygen)\n- vector and map de/serializer\n- PODs and std::string de/serializer\n- libcamera IPA-related structs de/serializer\n- docs\n\nThe bulk of the code, the de/serializers, are all slighly modified\nversions of each other.\n\n> If so I think this together with the tests could be merged as soon as \n> they are ready and just listed as a dependency to the main series. What \n> do others think about this?\n\nI'm fine with it of course :)\n\n\nPaul\n\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > Changes in v4:\n> > - rename readUInt/appendUInt to readPOD/appendPOD\n> >   - put them in anonymous namespace\n> >   - and add length check\n> >     - fatal if failure, because it means not enough data to deserialize,\n> >       which is technically a segfault\n> >   - use the new readPOD/appendPOD with length protections\n> >   - expand on their docs correspondingly\n> > - change snake_case to camelCase\n> > - add optimizations to the hand-written de/serializers\n> > - reserve vector length where trivially possible\n> > - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n> >   they're calling a specialization from the same specialization)\n> > \n> > Changes in v3:\n> > - reimplement append/readUInt with memcpy (intead of bit shifting)\n> > - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n> >   - use this for int64_t, bool, float, and double\n> > - fix comment style\n> > \n> > Changes in v2:\n> > - added serializers for all integer types, bool, and string\n> > - use string serializer for IPASettings serializer\n> > - add documentation\n> > - add serializer for const ControlList\n> > ---\n> >  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n> >  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n> >  src/libcamera/meson.build                     |    1 +\n> >  3 files changed, 1183 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..9a18f914\n> > --- /dev/null\n> > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > @@ -0,0 +1,1004 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> > + */\n> > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +\n> > +#include <deque>\n> > +#include <iostream>\n> > +#include <string.h>\n> > +#include <tuple>\n> > +#include <vector>\n> > +\n> > +#include <libcamera/buffer.h>\n> > +#include <libcamera/control_ids.h>\n> > +#include <libcamera/geometry.h>\n> > +#include <libcamera/ipa/ipa_interface.h>\n> > +\n> > +#include \"libcamera/internal/byte_stream_buffer.h\"\n> > +#include \"libcamera/internal/camera_sensor.h\"\n> > +#include \"libcamera/internal/control_serializer.h\"\n> > +#include \"libcamera/internal/log.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> > +\n> > +namespace {\n> > +\n> > +template<typename T>\n> > +void appendPOD(std::vector<uint8_t> &vec, T val)\n> > +{\n> > +\tsize_t byteWidth = sizeof(val);\n> > +\tstd::vector<uint8_t> v(byteWidth);\n> > +\tmemcpy(&v[0], &val, byteWidth);\n> > +\n> > +\tvec.insert(vec.end(), v.begin(), v.end());\n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(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\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > +\n> > +\tmemcpy(&ret, &vec[pos], byteWidth);\n> > +\treturn ret;\n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > +\t  std::vector<uint8_t>::iterator end)\n> > +{\n> > +\tT ret = 0;\n> > +\tsize_t byteWidth = sizeof(ret);\n> > +\n> > +\tif (pos + it >= end)\n> > +\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > +\n> > +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> > +\tmemcpy(&ret, v.data(), byteWidth);\n> > +\treturn ret;\n> > +}\n> > +\n> > +} /* namespace */\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> > +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t     ControlSerializer *cs);\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t vecLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::vector<V> ret(vecLen);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t   fdIter,\n> > +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t   cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t mapLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::map<K, V> ret;\n> > +\n> > +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t  cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t\t  cs);\n> > +\t\t\tret.insert({key, value});\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t}\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> > +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> > +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +};\n> > +\n> > +DECLARE_POD_SERIALIZER(bool)\n> > +DECLARE_POD_SERIALIZER(uint8_t)\n> > +DECLARE_POD_SERIALIZER(uint16_t)\n> > +DECLARE_POD_SERIALIZER(uint32_t)\n> > +DECLARE_POD_SERIALIZER(uint64_t)\n> > +DECLARE_POD_SERIALIZER(int8_t)\n> > +DECLARE_POD_SERIALIZER(int16_t)\n> > +DECLARE_POD_SERIALIZER(int32_t)\n> > +DECLARE_POD_SERIALIZER(int64_t)\n> > +DECLARE_POD_SERIALIZER(float)\n> > +DECLARE_POD_SERIALIZER(double)\n> > +\n> > +template<>\n> > +class IPADataSerializer<std::string>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {{data.begin(), data.end()}, {}};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<FileDescriptor>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> > +\t\tstd::vector<int32_t> fdVec;\n> > +\t\tif (data.isValid())\n> > +\t\t\tfdVec.push_back(data.fd());\n> > +\n> > +\t\treturn {dataVec, fdVec};\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > +\n> > +\t\tbool valid = !!(*dataBegin);\n> > +\n> > +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > +\n> > +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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> dataVec;\n> > +\n> > +\t\tuint32_t strLen = data.model.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> > +\n> > +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tCameraSensorInfo ret;\n> > +\n> > +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> > +\t\tret.model = str;\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> > +\n> > +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> > +\n> > +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> > +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> > +\n> > +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> > +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> > +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> > +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> > +\n> > +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> > +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> > +\n> > +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> > +\n> > +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPAStream ret;\n> > +\n> > +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tif (!cs)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> > +\n> > +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\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 dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\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 dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\tstd::vector<uint8_t> fdBuf;\n> > +\t\tstd::vector<int32_t> fdFds;\n> > +\t\tstd::tie(fdBuf, fdFds) =\n> > +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> > +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t      std::vector<int32_t> &fds,\n> > +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tFrameBuffer::Plane ret;\n> > +\n> > +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> > +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> > +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> > +\n> > +\t\tstd::vector<uint8_t> planesDataVec;\n> > +\t\tstd::vector<int32_t> planesFdsVec;\n> > +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> > +\n> > +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPABuffer ret;\n> > +\n> > +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.planes =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> > --- /dev/null\n> > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > @@ -0,0 +1,178 @@\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> > +namespace {\n> > +\n> > +/**\n> > + * \\fn template<typename T> void appendPOD(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> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] vec Byte vector to read from\n> > + * \\param[in] pos Index in vec to start reading from\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> > + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> > + * for deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a vec at index \\a pos\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] it Iterator of byte vector to read from\n> > + * \\param[in] pos Index in byte vector to read from\n> > + * \\param[in] end Iterator marking end of byte vector\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> > + * a fata error will occur, as it means there is insufficient data for\n> > + * deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a it at index \\a pos\n> > + */\n> > +\n> > +} /* namespace */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> > + * \tT data,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Serialize an object into byte vector and fd vector\n> > + * \\tparam T Type of object to serialize\n> > + * \\param[in] data Object to serialize\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> > + * of \\a data\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() can be used if the object type \\a T and its\n> > + * members don't have any FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tstd::vector<int32_t>::iterator fdsBegin,\n> > + * \tstd::vector<int32_t>::iterator fdsEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector and fd vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> > + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() (or the vector version) must be used if\n> > + * the object type \\a T or its members contain FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 99b27e66..01bcffd4 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -24,6 +24,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\n> \n> -- \n> Regards,\n> Niklas Söderlund","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 2BC61BE082\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 13 Nov 2020 06:41:59 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8FCE1631B4;\n\tFri, 13 Nov 2020 07:41:58 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E4B063149\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 13 Nov 2020 07:41:56 +0100 (CET)","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 8B95631A;\n\tFri, 13 Nov 2020 07:41:54 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Ob//zLvt\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1605249716;\n\tbh=pLu6AaRiberhgk6Ac60me47qv/vmh7/QgSlvhvJGu3g=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Ob//zLvtD1A9ugmjn7mW8ZSMDyA7gtizGqyZhpz+O3TILKAd1N37WSo1PVzz8pUem\n\thgCdOY0KRFIjeDhivYRRND+ibWpGNGfI1sadHkBCRv7r51pTulP67ny5zTr4q58gHf\n\tdZV0me4MxoYDsM19PkIUp9tn3ybpI7nmlDHDLDDo=","Date":"Fri, 13 Nov 2020 15:41:48 +0900","From":"paul.elder@ideasonboard.com","To":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","Message-ID":"<20201113064148.GD1811@pyrite.rasen.tech>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>\n\t<20201112112501.GI1480295@oden.dyn.berto.se>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201112112501.GI1480295@oden.dyn.berto.se>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"iso-8859-1\"","Content-Transfer-Encoding":"quoted-printable","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13749,"web_url":"https://patchwork.libcamera.org/comment/13749/","msgid":"<20201117144805.u4epmgoh6zyw6l4y@uno.localdomain>","date":"2020-11-17T14:48:05","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Paul,\n\nOn Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:\n> Add an IPADataSerializer which implments de/serialization of built-in\n> (PODs, vector, map, string) and libcamera data structures. This is\n> intended to be used by the proxy and the proxy worker in the IPC layer.\n>\n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n>\n> ---\n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n>\n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n>\n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n>  src/libcamera/meson.build                     |    1 +\n>  3 files changed, 1183 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..9a18f914\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,1004 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T>\n> +void appendPOD(std::vector<uint8_t> &vec, T val)\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n> +\tstd::vector<uint8_t> v(byteWidth);\n> +\tmemcpy(&v[0], &val, byteWidth);\n> +\n> +\tvec.insert(vec.end(), v.begin(), v.end());\n> +}\n> +\n> +template<typename T>\n> +T readPOD(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\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n> +\n> +\tmemcpy(&ret, &vec[pos], byteWidth);\n> +\treturn ret;\n> +}\n\nCan this function just be:\n\treturn readPOD<T>(vec.begin(), pos, vec.end());\n\n> +\n> +template<typename T>\n> +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> +\t  std::vector<uint8_t>::iterator end)\n> +{\n> +\tT ret = 0;\n> +\tsize_t byteWidth = sizeof(ret);\n> +\nCan you align to this style (empty line between the variable declarations\nand the if()) in the two readPOD implementations ?\n\n> +\tif (pos + it >= end)\n> +\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n> +\n> +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> +\tmemcpy(&ret, v.data(), byteWidth);\n\nCan you\n\tit += pos;\n\tmemcpy(&ret, &(*it), byteWidth);\n\nInstead of going through a vector ?\n\nThe end result looks like:\n\ntemplate<typename T>\nT readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n\t  std::vector<uint8_t>::iterator end)\n{\n\tif (pos + it >= end)\n\t\tLOG(IPADataSerializer, Fatal)\n\t\t\t<< \"Not enough data to deserialize POD\";\n\n\tT ret = 0;\n\tmemcpy(&ret, &(*(it + pos)), sizeof(ret));\n\n\treturn ret;\n}\n\ntemplate<typename T>\nT readPOD(std::vector<uint8_t> &vec, size_t pos)\n{\n\treturn readPOD<T>(vec.begin(), pos, vec.end());\n}\n\n(I would have liked to run tests, but serialization tests get skipped\nand I've not investigated tbh)\n\n> +\treturn ret;\n> +}\n> +\n> +} /* namespace */\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> +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs);\n> +#endif /* __DOXYGEN__ */\n\nI've already expressed my perplexity for this #ifdef, and I know you\nhave answered but I didn't get it :) you want this parsed by doxygen,\nisn't it equal from removing the ifdef guard ?\n\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +};\n> +\n> +DECLARE_POD_SERIALIZER(bool)\n> +DECLARE_POD_SERIALIZER(uint8_t)\n> +DECLARE_POD_SERIALIZER(uint16_t)\n> +DECLARE_POD_SERIALIZER(uint32_t)\n> +DECLARE_POD_SERIALIZER(uint64_t)\n> +DECLARE_POD_SERIALIZER(int8_t)\n> +DECLARE_POD_SERIALIZER(int16_t)\n> +DECLARE_POD_SERIALIZER(int32_t)\n> +DECLARE_POD_SERIALIZER(int64_t)\n> +DECLARE_POD_SERIALIZER(float)\n> +DECLARE_POD_SERIALIZER(double)\n> +\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {{data.begin(), data.end()}, {}};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn {dataVec, fdVec};\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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> dataVec;\n> +\n> +\t\tuint32_t strLen = data.model.size();\n> +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> +\n> +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n\nCan this be done using the <string> IPADataSerializer specialization ?\n\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n\nDepending on how many users we could potentially have <Size> and\n<Rectangle> serializer might be an helpful helper\n\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> +\n> +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tCameraSensorInfo ret;\n> +\n> +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> +\t\tret.model = str;\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> +\n> +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> +\n> +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> +\n> +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> +\n> +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> +\n> +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> +\n> +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPAStream ret;\n> +\n> +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> +\n> +\t\treturn {dataVec, {}};\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 dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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\nError message maybe ?\n\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\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 dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> +\n> +\t\tstd::vector<uint8_t> planesDataVec;\n> +\t\tstd::vector<int32_t> planesFdsVec;\n> +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> +\n> +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> +\n> +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPABuffer ret;\n> +\n> +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.planes =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,178 @@\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> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> + * \\brief Append uint to end of byte vector, in little-endian order\n\ns/uint/POD/\n\n> + * \\tparam T Type of uint to append\n\ns/uint/POD/\n\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value of uint to append\n\ns/of uint//\n\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tstd::vector<int32_t>::iterator fdsBegin,\n> + * \tstd::vector<int32_t>::iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 99b27e66..01bcffd4 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -24,6 +24,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\nStill wishing for the serialization format for each type to be\ndescribed, but this version is very nice according to my tastes!\n\nThanks for keep pushing and addressing the numerous comments!\n\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nThanks\n  j\n\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 4B7D6BE082\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 17 Nov 2020 14:48:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B9B2063326;\n\tTue, 17 Nov 2020 15:48:05 +0100 (CET)","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 4AC5863282\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Nov 2020 15:48:04 +0100 (CET)","from uno.localdomain (93-34-118-233.ip49.fastwebnet.it\n\t[93.34.118.233]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay2-d.mail.gandi.net (Postfix) with ESMTPSA id B061E40017;\n\tTue, 17 Nov 2020 14:48:03 +0000 (UTC)"],"X-Originating-IP":"93.34.118.233","Date":"Tue, 17 Nov 2020 15:48:05 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20201117144805.u4epmgoh6zyw6l4y@uno.localdomain>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201106103707.49660-9-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13770,"web_url":"https://patchwork.libcamera.org/comment/13770/","msgid":"<20201118104734.nhblwjli65b3zcy5@uno.localdomain>","date":"2020-11-18T10:47:34","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Paul,\n\nOn Wed, Nov 18, 2020 at 07:20:14PM +0900, paul.elder@ideasonboard.com wrote:\n> Hi Jacopo,\n>\n> On Tue, Nov 17, 2020 at 03:48:05PM +0100, Jacopo Mondi wrote:\n> > Hi Paul,\n> >\n> > On Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:\n> > > Add an IPADataSerializer which implments de/serialization of built-in\n> > > (PODs, vector, map, string) and libcamera data structures. This is\n> > > intended to be used by the proxy and the proxy worker in the IPC layer.\n> > >\n> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > >\n> > > ---\n> > > Changes in v4:\n> > > - rename readUInt/appendUInt to readPOD/appendPOD\n> > >   - put them in anonymous namespace\n> > >   - and add length check\n> > >     - fatal if failure, because it means not enough data to deserialize,\n> > >       which is technically a segfault\n> > >   - use the new readPOD/appendPOD with length protections\n> > >   - expand on their docs correspondingly\n> > > - change snake_case to camelCase\n> > > - add optimizations to the hand-written de/serializers\n> > > - reserve vector length where trivially possible\n> > > - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n> > >   they're calling a specialization from the same specialization)\n> > >\n> > > Changes in v3:\n> > > - reimplement append/readUInt with memcpy (intead of bit shifting)\n> > > - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n> > >   - use this for int64_t, bool, float, and double\n> > > - fix comment style\n> > >\n> > > Changes in v2:\n> > > - added serializers for all integer types, bool, and string\n> > > - use string serializer for IPASettings serializer\n> > > - add documentation\n> > > - add serializer for const ControlList\n> > > ---\n> > >  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n> > >  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n> > >  src/libcamera/meson.build                     |    1 +\n> > >  3 files changed, 1183 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..9a18f914\n> > > --- /dev/null\n> > > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > > @@ -0,0 +1,1004 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2020, Google Inc.\n> > > + *\n> > > + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> > > + */\n> > > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > > +\n> > > +#include <deque>\n> > > +#include <iostream>\n> > > +#include <string.h>\n> > > +#include <tuple>\n> > > +#include <vector>\n> > > +\n> > > +#include <libcamera/buffer.h>\n> > > +#include <libcamera/control_ids.h>\n> > > +#include <libcamera/geometry.h>\n> > > +#include <libcamera/ipa/ipa_interface.h>\n> > > +\n> > > +#include \"libcamera/internal/byte_stream_buffer.h\"\n> > > +#include \"libcamera/internal/camera_sensor.h\"\n> > > +#include \"libcamera/internal/control_serializer.h\"\n> > > +#include \"libcamera/internal/log.h\"\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> > > +\n> > > +namespace {\n> > > +\n> > > +template<typename T>\n> > > +void appendPOD(std::vector<uint8_t> &vec, T val)\n> > > +{\n> > > +\tsize_t byteWidth = sizeof(val);\n> > > +\tstd::vector<uint8_t> v(byteWidth);\n> > > +\tmemcpy(&v[0], &val, byteWidth);\n> > > +\n> > > +\tvec.insert(vec.end(), v.begin(), v.end());\n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +T readPOD(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\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > > +\n> > > +\tmemcpy(&ret, &vec[pos], byteWidth);\n> > > +\treturn ret;\n> > > +}\n> >\n> > Can this function just be:\n> > \treturn readPOD<T>(vec.begin(), pos, vec.end());\n>\n> Oh, it can. It has to be moved below the next one, though.\n>\n> > > +\n> > > +template<typename T>\n> > > +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > > +\t  std::vector<uint8_t>::iterator end)\n> > > +{\n> > > +\tT ret = 0;\n> > > +\tsize_t byteWidth = sizeof(ret);\n> > > +\n> > Can you align to this style (empty line between the variable declarations\n> > and the if()) in the two readPOD implementations ?\n> >\n> > > +\tif (pos + it >= end)\n> > > +\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > > +\n> > > +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> > > +\tmemcpy(&ret, v.data(), byteWidth);\n> >\n> > Can you\n> > \tit += pos;\n> > \tmemcpy(&ret, &(*it), byteWidth);\n> >\n> > Instead of going through a vector ?\n> >\n> > The end result looks like:\n> >\n> > template<typename T>\n> > T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > \t  std::vector<uint8_t>::iterator end)\n> > {\n> > \tif (pos + it >= end)\n> > \t\tLOG(IPADataSerializer, Fatal)\n> > \t\t\t<< \"Not enough data to deserialize POD\";\n> >\n> > \tT ret = 0;\n> > \tmemcpy(&ret, &(*(it + pos)), sizeof(ret));\n> >\n> > \treturn ret;\n> > }\n> >\n> > template<typename T>\n> > T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> > {\n> > \treturn readPOD<T>(vec.begin(), pos, vec.end());\n> > }\n>\n> Yeah, it passes the tests!\n>\n> > (I would have liked to run tests, but serialization tests get skipped\n> > and I've not investigated tbh)\n>\n> The IPA serialization test needs vimc.\n>\n\nTIL `modprobe vivid vimc` only loads vivid :/\nSorry about this\n\n> > > +\treturn ret;\n> > > +}\n> > > +\n> > > +} /* namespace */\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> > > +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> > > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t     ControlSerializer *cs);\n> > > +#endif /* __DOXYGEN__ */\n> >\n> > I've already expressed my perplexity for this #ifdef, and I know you\n> > have answered but I didn't get it :) you want this parsed by doxygen,\n> > isn't it equal from removing the ifdef guard ?\n>\n> Oh huh, for some reason I was worried about template resolution\n> problems. You're right, it works even without the ifdef guard.\n>\n\nGreat, let's drop it!\n\n> > > +};\n> > > +\n\n[snip]\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> dataVec;\n> > > +\n> > > +\t\tuint32_t strLen = data.model.size();\n> > > +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> > > +\n> > > +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> >\n> > Can this be done using the <string> IPADataSerializer specialization ?\n>\n> It can, but this is hand-written so it can be hand-optimized.\n>\n\nYeah, we save a few calls indeed\n\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> > > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> >\n> > Depending on how many users we could potentially have <Size> and\n> > <Rectangle> serializer might be an helpful helper\n>\n> Oh yeah good idea, I'll provide Size and Rectangle as available structs\n> in IPA interfaces.\n>\n> But here it'll probably still be hand-optimized :)\n>\n\nThat's ok and you can do on-top not to delay this series even more\n\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n\n[snip]\n\n> >\n> > Still wishing for the serialization format for each type to be\n>\n> I added it; it's in the next version :)\n>\n\ngreat!\n\nThank you!\n\n\n> > described, but this version is very nice according to my tastes!\n>\n> \\o/\n>\n> > Thanks for keep pushing and addressing the numerous comments!\n> >\n> > Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n>\n>\n> Thanks,\n>\n> Paul\n>\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 D0D54BE08A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Nov 2020 10:47:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 57C0061569;\n\tWed, 18 Nov 2020 11:47:34 +0100 (CET)","from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0582B61564\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Nov 2020 11:47:31 +0100 (CET)","from uno.localdomain (93-34-118-233.ip49.fastwebnet.it\n\t[93.34.118.233]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id 4CD95240002;\n\tWed, 18 Nov 2020 10:47:30 +0000 (UTC)"],"Date":"Wed, 18 Nov 2020 11:47:34 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"paul.elder@ideasonboard.com","Message-ID":"<20201118104734.nhblwjli65b3zcy5@uno.localdomain>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>\n\t<20201117144805.u4epmgoh6zyw6l4y@uno.localdomain>\n\t<20201118102014.GG1856@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201118102014.GG1856@pyrite.rasen.tech>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13809,"web_url":"https://patchwork.libcamera.org/comment/13809/","msgid":"<20201119172431.GD4563@pendragon.ideasonboard.com>","date":"2020-11-19T17:24:31","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nOn Thu, Nov 12, 2020 at 12:25:01PM +0100, Niklas Söderlund wrote:\n> Hi Paul,\n> \n> Thanks for your patch.\n> \n> On 2020-11-06 19:36:38 +0900, Paul Elder wrote:\n> > Add an IPADataSerializer which implments de/serialization of built-in\n> > (PODs, vector, map, string) and libcamera data structures. This is\n> > intended to be used by the proxy and the proxy worker in the IPC layer.\n> \n> As this is a rather large patch in a rather large series I wonder if \n> this could be broken out into it's own series together with it's test \n> cases. Perhaps this patch could even be broken up to smaller bits each \n> dealing with a specific data type?\n> \n> If so I think this together with the tests could be merged as soon as \n> they are ready and just listed as a dependency to the main series. What \n> do others think about this?\n\nI think we need to bite the bullet and review it anyway. Review of other\npatches in the series may require changes to this one. I don't think\nwe'll gain much by merging in small chunks unfortunately.\n\n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > Changes in v4:\n> > - rename readUInt/appendUInt to readPOD/appendPOD\n> >   - put them in anonymous namespace\n> >   - and add length check\n> >     - fatal if failure, because it means not enough data to deserialize,\n> >       which is technically a segfault\n> >   - use the new readPOD/appendPOD with length protections\n> >   - expand on their docs correspondingly\n> > - change snake_case to camelCase\n> > - add optimizations to the hand-written de/serializers\n> > - reserve vector length where trivially possible\n> > - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n> >   they're calling a specialization from the same specialization)\n> > \n> > Changes in v3:\n> > - reimplement append/readUInt with memcpy (intead of bit shifting)\n> > - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n> >   - use this for int64_t, bool, float, and double\n> > - fix comment style\n> > \n> > Changes in v2:\n> > - added serializers for all integer types, bool, and string\n> > - use string serializer for IPASettings serializer\n> > - add documentation\n> > - add serializer for const ControlList\n> > ---\n> >  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n> >  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n> >  src/libcamera/meson.build                     |    1 +\n> >  3 files changed, 1183 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..9a18f914\n> > --- /dev/null\n> > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > @@ -0,0 +1,1004 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> > + */\n> > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +\n> > +#include <deque>\n> > +#include <iostream>\n> > +#include <string.h>\n> > +#include <tuple>\n> > +#include <vector>\n> > +\n> > +#include <libcamera/buffer.h>\n> > +#include <libcamera/control_ids.h>\n> > +#include <libcamera/geometry.h>\n> > +#include <libcamera/ipa/ipa_interface.h>\n> > +\n> > +#include \"libcamera/internal/byte_stream_buffer.h\"\n> > +#include \"libcamera/internal/camera_sensor.h\"\n> > +#include \"libcamera/internal/control_serializer.h\"\n> > +#include \"libcamera/internal/log.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> > +\n> > +namespace {\n> > +\n> > +template<typename T>\n> > +void appendPOD(std::vector<uint8_t> &vec, T val)\n> > +{\n> > +\tsize_t byteWidth = sizeof(val);\n> > +\tstd::vector<uint8_t> v(byteWidth);\n> > +\tmemcpy(&v[0], &val, byteWidth);\n> > +\n> > +\tvec.insert(vec.end(), v.begin(), v.end());\n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(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\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > +\n> > +\tmemcpy(&ret, &vec[pos], byteWidth);\n> > +\treturn ret;\n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > +\t  std::vector<uint8_t>::iterator end)\n> > +{\n> > +\tT ret = 0;\n> > +\tsize_t byteWidth = sizeof(ret);\n> > +\n> > +\tif (pos + it >= end)\n> > +\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > +\n> > +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> > +\tmemcpy(&ret, v.data(), byteWidth);\n> > +\treturn ret;\n> > +}\n> > +\n> > +} /* namespace */\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> > +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t     ControlSerializer *cs);\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t vecLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::vector<V> ret(vecLen);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t   fdIter,\n> > +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t   cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t mapLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::map<K, V> ret;\n> > +\n> > +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t  cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t\t  cs);\n> > +\t\t\tret.insert({key, value});\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t}\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> > +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> > +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +};\n> > +\n> > +DECLARE_POD_SERIALIZER(bool)\n> > +DECLARE_POD_SERIALIZER(uint8_t)\n> > +DECLARE_POD_SERIALIZER(uint16_t)\n> > +DECLARE_POD_SERIALIZER(uint32_t)\n> > +DECLARE_POD_SERIALIZER(uint64_t)\n> > +DECLARE_POD_SERIALIZER(int8_t)\n> > +DECLARE_POD_SERIALIZER(int16_t)\n> > +DECLARE_POD_SERIALIZER(int32_t)\n> > +DECLARE_POD_SERIALIZER(int64_t)\n> > +DECLARE_POD_SERIALIZER(float)\n> > +DECLARE_POD_SERIALIZER(double)\n> > +\n> > +template<>\n> > +class IPADataSerializer<std::string>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {{data.begin(), data.end()}, {}};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<FileDescriptor>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> > +\t\tstd::vector<int32_t> fdVec;\n> > +\t\tif (data.isValid())\n> > +\t\t\tfdVec.push_back(data.fd());\n> > +\n> > +\t\treturn {dataVec, fdVec};\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > +\n> > +\t\tbool valid = !!(*dataBegin);\n> > +\n> > +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > +\n> > +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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> dataVec;\n> > +\n> > +\t\tuint32_t strLen = data.model.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> > +\n> > +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tCameraSensorInfo ret;\n> > +\n> > +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> > +\t\tret.model = str;\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> > +\n> > +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> > +\n> > +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> > +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> > +\n> > +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> > +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> > +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> > +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> > +\n> > +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> > +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> > +\n> > +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> > +\n> > +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPAStream ret;\n> > +\n> > +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tif (!cs)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> > +\n> > +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\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 dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\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 dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\tstd::vector<uint8_t> fdBuf;\n> > +\t\tstd::vector<int32_t> fdFds;\n> > +\t\tstd::tie(fdBuf, fdFds) =\n> > +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> > +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t      std::vector<int32_t> &fds,\n> > +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tFrameBuffer::Plane ret;\n> > +\n> > +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> > +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> > +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> > +\n> > +\t\tstd::vector<uint8_t> planesDataVec;\n> > +\t\tstd::vector<int32_t> planesFdsVec;\n> > +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> > +\n> > +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPABuffer ret;\n> > +\n> > +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.planes =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> > --- /dev/null\n> > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > @@ -0,0 +1,178 @@\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> > +namespace {\n> > +\n> > +/**\n> > + * \\fn template<typename T> void appendPOD(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> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] vec Byte vector to read from\n> > + * \\param[in] pos Index in vec to start reading from\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> > + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> > + * for deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a vec at index \\a pos\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] it Iterator of byte vector to read from\n> > + * \\param[in] pos Index in byte vector to read from\n> > + * \\param[in] end Iterator marking end of byte vector\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> > + * a fata error will occur, as it means there is insufficient data for\n> > + * deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a it at index \\a pos\n> > + */\n> > +\n> > +} /* namespace */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> > + * \tT data,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Serialize an object into byte vector and fd vector\n> > + * \\tparam T Type of object to serialize\n> > + * \\param[in] data Object to serialize\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> > + * of \\a data\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() can be used if the object type \\a T and its\n> > + * members don't have any FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tstd::vector<int32_t>::iterator fdsBegin,\n> > + * \tstd::vector<int32_t>::iterator fdsEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector and fd vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> > + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() (or the vector version) must be used if\n> > + * the object type \\a T or its members contain FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 99b27e66..01bcffd4 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -24,6 +24,7 @@ libcamera_sources = files([\n> >      'geometry.cpp',\n> >      'ipa_context_wrapper.cpp',\n> >      'ipa_controls.cpp',\n> > +    'ipa_data_serializer.cpp',\n> >      'ipa_interface.cpp',\n> >      'ipa_manager.cpp',\n> >      'ipa_module.cpp',","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D3CBFBE08A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 19 Nov 2020 17:24:39 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 590DA615B4;\n\tThu, 19 Nov 2020 18:24:39 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5A9AC60D4B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 19 Nov 2020 18:24:38 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B41F324D;\n\tThu, 19 Nov 2020 18:24:37 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"hpVyoc6I\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1605806678;\n\tbh=z1Yxg9yKz0+SziDmPr17EAExcEj59dzOR3tNkBf409o=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=hpVyoc6IBFKhCdMQnWcPA8776mGjZQW4Ajd+QV5Bws+RR5bdAgmh9k6jmdSjjWuNs\n\tgIIbQMUNim1G7FJRoQirpR5F0uMtrV76qUQoJNXx9WTevqXRHFpbmKvsgLq0ulhw/b\n\tdyfZs7cqyWbCkxFo8OIQRJLWgGa5Zp2O8hVxbxq4=","Date":"Thu, 19 Nov 2020 19:24:31 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Message-ID":"<20201119172431.GD4563@pendragon.ideasonboard.com>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>\n\t<20201112112501.GI1480295@oden.dyn.berto.se>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201112112501.GI1480295@oden.dyn.berto.se>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13811,"web_url":"https://patchwork.libcamera.org/comment/13811/","msgid":"<20201120084343.GE4563@pendragon.ideasonboard.com>","date":"2020-11-20T08:43:43","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:\n> Add an IPADataSerializer which implments de/serialization of built-in\n\ns/implments/implements/\n\n> (PODs, vector, map, string) and libcamera data structures. This is\n> intended to be used by the proxy and the proxy worker in the IPC layer.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n> \n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n> \n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n>  src/libcamera/meson.build                     |    1 +\n>  3 files changed, 1183 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..9a18f914\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,1004 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T>\n> +void appendPOD(std::vector<uint8_t> &vec, T val)\n\nAn std::enable_if<std::is_arithmetic_v<T>> would help ensuring there's\nno misuse of this template.\n\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n> +\tstd::vector<uint8_t> v(byteWidth);\n> +\tmemcpy(&v[0], &val, byteWidth);\n> +\n> +\tvec.insert(vec.end(), v.begin(), v.end());\n\n\tvec.resize(vec.size() + byteWidth);\n\tmemcpy(vec.end() - byteWidth, &val, byteWidth);\n\nIf we want to be efficient I think we'll need to implement our own\ncontainer instead of using std::vector<>, in order to minimize the\nnumber of reallocations.\n\nIn general I think the serialization and deserialization code have room\nfor optimization, which is completely expected. Perfection can wait for\na follow-up series ;-) I'd like to focus on the interface with the\npipeline handlers and the base IPC mechanism as the target to get this\nseries merged. The internal implementation can then be reworked later.\n\n> +}\n> +\n> +template<typename T>\n> +T readPOD(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\nThere's a risk of an integer overflow if the incoming message has a size\nset to UINT_MAX for instance. This check will pass, and you'll write\ndata to vec[-1] below.\n\n> +\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n\nIf you want to make this fatal, then there should be checks in the\n(generated) caller code to handle the issue gracefully. We don't want to\ncrash because of a malformed message.\n\n> +\n> +\tmemcpy(&ret, &vec[pos], byteWidth);\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> +\t  std::vector<uint8_t>::iterator end)\n> +{\n> +\tT ret = 0;\n> +\tsize_t byteWidth = sizeof(ret);\n> +\n> +\tif (pos + it >= end)\n> +\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t<< \"Not enough data to deserialize POD\";\n> +\n> +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> +\tmemcpy(&ret, v.data(), byteWidth);\n> +\treturn ret;\n> +}\n\nDo we need both functions ?\n\nAs commented in the review of 04/37, using a Span<const uint8_t> would\nlikely be best for deserialization.\n\n> +\n> +} /* namespace */\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\ns/T/const T &/\n\n> +\n> +\tstatic T deserialize(std::vector<uint8_t> &data,\n\nconst. Same everywhere below.\n\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs);\n\nIf you switch to span, I think all these could be merged into a single\nfunction.\n\n\tstatic T deserialize(Span<const uint8_t> data,\n\t\t\t     Span<const int32_t> fds,\n\t\t\t     ControlSerializer *cs);\n\nThis can be called with\n\n\tdeserialize<T>(data, {}, cs);\n\nwhen no fds is available.\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n\ndataEnd is used.\n\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n\nThis could go past fdsEnd.\n\nThe deserializer code will need to be hardened, which will likely\ninvolve a refactoring. I think it can be merged with a few issues, we'll\nfix them on top.\n\nThese two comments apply to the code below as well.\n\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdataIter += 8 + sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n\nAll the specializations below can go to a .cpp file.\n\n> +\n> +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> +\t\t\t\t\t\t\t\t\t\\\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\t\t\t\t\t\t\t\t\\\n> +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> +\t}\t\t\t\t\t\t\t\t\\\n> +};\n> +\n> +DECLARE_POD_SERIALIZER(bool)\n> +DECLARE_POD_SERIALIZER(uint8_t)\n> +DECLARE_POD_SERIALIZER(uint16_t)\n> +DECLARE_POD_SERIALIZER(uint32_t)\n> +DECLARE_POD_SERIALIZER(uint64_t)\n> +DECLARE_POD_SERIALIZER(int8_t)\n> +DECLARE_POD_SERIALIZER(int16_t)\n> +DECLARE_POD_SERIALIZER(int32_t)\n> +DECLARE_POD_SERIALIZER(int64_t)\n> +DECLARE_POD_SERIALIZER(float)\n> +DECLARE_POD_SERIALIZER(double)\n> +\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {{data.begin(), data.end()}, {}};\n\nShouldn't we prefix strings by their length, like we do for vectors,\ninstead of relying on the terminating 0 ?\n\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {data.begin(), data.end()};\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn {dataBegin, dataEnd};\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n\nI think you should store an int32_t here, set to -1 when the fd is\ninvalid. Ideally it should contain the index of the entry in the fds\nvector for valid fds, but that will require a refactoring of the\nserialization code to pass the data and fds vectors to the serialization\nfunctions. It can thus be done later.\n\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn {dataVec, fdVec};\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> +\t}\n> +\n> +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\n> +\t}\n> +};\n\nWould it make sense to define the IPASettings structure in a .mojom file\nto avoid manually-written serialization code ? Same for IPAStream and\nIPABuffer.\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> dataVec;\n> +\n> +\t\tuint32_t strLen = data.model.size();\n> +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> +\n> +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> +\n> +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tCameraSensorInfo ret;\n> +\n> +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> +\t\tret.model = str;\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> +\n> +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> +\n> +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> +\n> +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> +\n> +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> +\n> +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> +\n> +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> +\n> +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPAStream ret;\n> +\n> +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> +\t}\n> +\n> +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd);\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\nThis means we will serialize the ControlInfoMap every single time we\nserialize a ControlList. That's very inefficient. The ControlInfoMap\ninstances are supposed to be serialized and transmitted upfront, at\nconfigure() time, and then reused.\n\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> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +template<>\n> +class IPADataSerializer<ControlList>\n\nRequiring two specializations, one for const ControlList and one for\nControlList, is a sign that something is wrong somewhere. Same for\nControlInfoMap below.\n\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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> +\n> +\t\treturn {dataVec, {}};\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 dataBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t\tControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> +\t\tstd::tie(dataVec, std::ignore) =\n> +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> +\n> +\t\treturn {dataVec, {}};\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\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 dataBegin,\n> +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn {dataVec, fdsVec};\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +\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> dataVec;\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> +\n> +\t\tstd::vector<uint8_t> planesDataVec;\n> +\t\tstd::vector<int32_t> planesFdsVec;\n> +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> +\n> +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> +\n> +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> +\t\t\t\t     ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tIPABuffer ret;\n> +\n> +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tret.planes =\n> +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,178 @@\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> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n\nPOD also includes structures and arrays (see\nhttps://en.cppreference.com/w/cpp/named_req/PODType). The correct term\nhere is \"arithmetic type\" (see\nhttps://en.cppreference.com/w/cpp/types/is_arithmetic).\nappendArithmetic() sounds a bit wrong though.\n\n> + * \\brief Append uint to end of byte vector, in little-endian order\n\ns/uint/\\a val/ as it can also be a bool or a float. Same below, the\ndocumentation should talk about arithmetic types and values, not uint.\n\n> + * \\tparam T Type of uint to append\n\nOh, I didn't know about tparam. Interesting.\n\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value of uint to append\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \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 dataBegin,\n> + * \tstd::vector<uint8_t>::iterator dataEnd,\n> + * \tstd::vector<int32_t>::iterator fdsBegin,\n> + * \tstd::vector<int32_t>::iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 99b27e66..01bcffd4 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -24,6 +24,7 @@ libcamera_sources = files([\n>      'geometry.cpp',\n>      'ipa_context_wrapper.cpp',\n>      'ipa_controls.cpp',\n> +    'ipa_data_serializer.cpp',\n>      'ipa_interface.cpp',\n>      'ipa_manager.cpp',\n>      'ipa_module.cpp',","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 7E94FBE08A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Nov 2020 08:43:53 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E5A8B615B7;\n\tFri, 20 Nov 2020 09:43:52 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B6E9360337\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Nov 2020 09:43:51 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C65B4240;\n\tFri, 20 Nov 2020 09:43:50 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"USGQCo83\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1605861831;\n\tbh=qLLdN2MfOjgdOFMQEdeI06hterH+7WXlqtzn4omYusA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=USGQCo83YizaFGjlXweHtlaHLYT0ulrQqrp++XZEsuwFVdKjT7Mg6CiMFQuaqzp2F\n\tGdwtSY4F+Jy0FkqHVKSk/2M9nrhavvUV8C6poAzCDXFLTCnmhARniYZSYDecZ/hcvJ\n\tfOTCHgkuvhpkJlv6EIp/miBBzhWGpPEsT0KtdTGo=","Date":"Fri, 20 Nov 2020 10:43:43 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<20201120084343.GE4563@pendragon.ideasonboard.com>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201106103707.49660-9-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13896,"web_url":"https://patchwork.libcamera.org/comment/13896/","msgid":"<20201126094935.GC2050@pyrite.rasen.tech>","date":"2020-11-26T09:49:35","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Laurent,\n\nThank you for the review.\n\nOn Fri, Nov 20, 2020 at 10:43:43AM +0200, Laurent Pinchart wrote:\n> Hi Paul,\n> \n> Thank you for the patch.\n> \n> On Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:\n> > Add an IPADataSerializer which implments de/serialization of built-in\n> \n> s/implments/implements/\n> \n> > (PODs, vector, map, string) and libcamera data structures. This is\n> > intended to be used by the proxy and the proxy worker in the IPC layer.\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > Changes in v4:\n> > - rename readUInt/appendUInt to readPOD/appendPOD\n> >   - put them in anonymous namespace\n> >   - and add length check\n> >     - fatal if failure, because it means not enough data to deserialize,\n> >       which is technically a segfault\n> >   - use the new readPOD/appendPOD with length protections\n> >   - expand on their docs correspondingly\n> > - change snake_case to camelCase\n> > - add optimizations to the hand-written de/serializers\n> > - reserve vector length where trivially possible\n> > - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n> >   they're calling a specialization from the same specialization)\n> > \n> > Changes in v3:\n> > - reimplement append/readUInt with memcpy (intead of bit shifting)\n> > - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n> >   - use this for int64_t, bool, float, and double\n> > - fix comment style\n> > \n> > Changes in v2:\n> > - added serializers for all integer types, bool, and string\n> > - use string serializer for IPASettings serializer\n> > - add documentation\n> > - add serializer for const ControlList\n> > ---\n> >  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n> >  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n> >  src/libcamera/meson.build                     |    1 +\n> >  3 files changed, 1183 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..9a18f914\n> > --- /dev/null\n> > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > @@ -0,0 +1,1004 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2020, Google Inc.\n> > + *\n> > + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> > + */\n> > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > +\n> > +#include <deque>\n> > +#include <iostream>\n> > +#include <string.h>\n> > +#include <tuple>\n> > +#include <vector>\n> > +\n> > +#include <libcamera/buffer.h>\n> > +#include <libcamera/control_ids.h>\n> > +#include <libcamera/geometry.h>\n> > +#include <libcamera/ipa/ipa_interface.h>\n> > +\n> > +#include \"libcamera/internal/byte_stream_buffer.h\"\n> > +#include \"libcamera/internal/camera_sensor.h\"\n> > +#include \"libcamera/internal/control_serializer.h\"\n> > +#include \"libcamera/internal/log.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> > +\n> > +namespace {\n> > +\n> > +template<typename T>\n> > +void appendPOD(std::vector<uint8_t> &vec, T val)\n> \n> An std::enable_if<std::is_arithmetic_v<T>> would help ensuring there's\n> no misuse of this template.\n\nAh okay.\n\n> > +{\n> > +\tsize_t byteWidth = sizeof(val);\n> > +\tstd::vector<uint8_t> v(byteWidth);\n> > +\tmemcpy(&v[0], &val, byteWidth);\n> > +\n> > +\tvec.insert(vec.end(), v.begin(), v.end());\n> \n> \tvec.resize(vec.size() + byteWidth);\n> \tmemcpy(vec.end() - byteWidth, &val, byteWidth);\n> \n> If we want to be efficient I think we'll need to implement our own\n> container instead of using std::vector<>, in order to minimize the\n> number of reallocations.\n> \n> In general I think the serialization and deserialization code have room\n> for optimization, which is completely expected. Perfection can wait for\n> a follow-up series ;-) I'd like to focus on the interface with the\n> pipeline handlers and the base IPC mechanism as the target to get this\n> series merged. The internal implementation can then be reworked later.\n> \n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(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> \n> There's a risk of an integer overflow if the incoming message has a size\n> set to UINT_MAX for instance. This check will pass, and you'll write\n> data to vec[-1] below.\n\nI've since removed this and it takes the iterators and calls the\niterator version instead, so that should solve the integer overflow\nissue.\n\n> > +\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> \n> If you want to make this fatal, then there should be checks in the\n> (generated) caller code to handle the issue gracefully. We don't want to\n> crash because of a malformed message.\n\nSo the caller has protections against trying to read more data than\nexists, either because that's what the message said, or because the\nmessage got truncated (eg. in the generated code):\n\n\tif (dataSize < 4) {\n\t\tLOG(IPADataSerializer, Error)\n\t\t\t<< \"Failed to deserialize \" << \"staggeredWriteResultSize\"\n\t\t\t<< \": not enough data, expected \"\n\t\t\t<< (4) << \", got \" << (dataSize);\n\t\treturn ret;\n\t}\n\tconst size_t staggeredWriteResultSize = readPOD<uint32_t>(m, 0, dataEnd);\n\tm += 4;\n\tdataSize -= 4;\n\nSo if readPOD really receives not enough data, then something is\n/really/ wrong. Or is the caller's protection sufficient and we can\nremove the Fatal from readPOD?\n\n> > +\n> > +\tmemcpy(&ret, &vec[pos], byteWidth);\n> > +\treturn ret;\n> > +}\n> > +\n> > +template<typename T>\n> > +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > +\t  std::vector<uint8_t>::iterator end)\n> > +{\n> > +\tT ret = 0;\n> > +\tsize_t byteWidth = sizeof(ret);\n> > +\n> > +\tif (pos + it >= end)\n> > +\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > +\n> > +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> > +\tmemcpy(&ret, v.data(), byteWidth);\n> > +\treturn ret;\n> > +}\n> \n> Do we need both functions ?\n\nNo, I made the vector one call the iterator one.\n\n> As commented in the review of 04/37, using a Span<const uint8_t> would\n> likely be best for deserialization.\n> \n> > +\n> > +} /* namespace */\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> s/T/const T &/\n> \n> > +\n> > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> \n> const. Same everywhere below.\n> \n> > +\t\t\t     ControlSerializer *cs);\n> > +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t     ControlSerializer *cs);\n> \n> If you switch to span, I think all these could be merged into a single\n> function.\n> \n> \tstatic T deserialize(Span<const uint8_t> data,\n> \t\t\t     Span<const int32_t> fds,\n> \t\t\t     ControlSerializer *cs);\n> \n> This can be called with\n> \n> \tdeserialize<T>(data, {}, cs);\n> \n> when no fds is available.\n\nYeah, that would be nice...\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t vecLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> \n> dataEnd is used.\n> \n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::vector<V> ret(vecLen);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t   fdIter,\n> > +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> \n> This could go past fdsEnd.\n\nOh shoot yeah...\n\n> The deserializer code will need to be hardened, which will likely\n> involve a refactoring. I think it can be merged with a few issues, we'll\n> fix them on top.\n\nFor now I could just jam in protection checks like what we have in the\ngenerated code. I guess at this point the generated code is more\nhardened haha...\n\n> These two comments apply to the code below as well.\n> \n> > +\t\t\t\t\t\t\t\t   cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\t/* Serialize the length. */\n> > +\t\tuint32_t mapLen = data.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> > +\n> > +\t\t/* Serialize the members. */\n> > +\t\tfor (auto const &it : data) {\n> > +\t\t\tstd::vector<uint8_t> dvec;\n> > +\t\t\tstd::vector<int32_t> fvec;\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\n> > +\t\t\tstd::tie(dvec, fvec) =\n> > +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> > +\n> > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > +\n> > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > +\t\t}\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<int32_t> fds;\n> > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::map<K, V> ret;\n> > +\n> > +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t  cs);\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > +\n> > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > +\t\t\t\t\t\t\t\t\t  fdIter,\n> > +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > +\t\t\t\t\t\t\t\t\t  cs);\n> > +\t\t\tret.insert({key, value});\n> > +\n> > +\t\t\tdataIter += 8 + sizeofData;\n> > +\t\t\tfdIter += sizeofFds;\n> > +\t\t}\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> \n> All the specializations below can go to a .cpp file.\n\nThey can? I thought then they won't be visible to anybody else?\n\nIndeed the compiler complains that the the specialized serializers and\nthe proxy workers can't find the appropriate specialization.\n\n> > +\n> > +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> > +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> > +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> > +\t\t\t\t\t\t\t\t\t\\\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > +\t{\t\t\t\t\t\t\t\t\\\n> > +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> > +\t}\t\t\t\t\t\t\t\t\\\n> > +};\n> > +\n> > +DECLARE_POD_SERIALIZER(bool)\n> > +DECLARE_POD_SERIALIZER(uint8_t)\n> > +DECLARE_POD_SERIALIZER(uint16_t)\n> > +DECLARE_POD_SERIALIZER(uint32_t)\n> > +DECLARE_POD_SERIALIZER(uint64_t)\n> > +DECLARE_POD_SERIALIZER(int8_t)\n> > +DECLARE_POD_SERIALIZER(int16_t)\n> > +DECLARE_POD_SERIALIZER(int32_t)\n> > +DECLARE_POD_SERIALIZER(int64_t)\n> > +DECLARE_POD_SERIALIZER(float)\n> > +DECLARE_POD_SERIALIZER(double)\n> > +\n> > +template<>\n> > +class IPADataSerializer<std::string>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {{data.begin(), data.end()}, {}};\n> \n> Shouldn't we prefix strings by their length, like we do for vectors,\n> instead of relying on the terminating 0 ?\n\nWe actually don't rely on the terminating zero. All\ncontainers of strings (function parameter, struct, vector/map)\nalready record the length, and pass the full vector or iterator pair to\nthe deserializer, so this is actually sufficient.\n\nI do the same trick with vectors and maps, actually. Notice they don't\ncontain their length in bytes, but actually their length in number of\nelements. This is for the same reason; every container (function\nparameter, struct, vector/map) of vectors/maps already contains the length\nin bytes.\n\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {data.begin(), data.end()};\n> > +\t}\n> > +\n> > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn {dataBegin, dataEnd};\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<FileDescriptor>\n> > +{\n> > +public:\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> \n> I think you should store an int32_t here, set to -1 when the fd is\n> invalid. Ideally it should contain the index of the entry in the fds\n\nI don't see how that's better than the scheme that I have here now...\n\n> vector for valid fds, but that will require a refactoring of the\n> serialization code to pass the data and fds vectors to the serialization\n> functions. It can thus be done later.\n> \n> > +\t\tstd::vector<int32_t> fdVec;\n> > +\t\tif (data.isValid())\n> > +\t\t\tfdVec.push_back(data.fd());\n> > +\n> > +\t\treturn {dataVec, fdVec};\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> > +\t}\n> > +\n> > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > +\n> > +\t\tbool valid = !!(*dataBegin);\n> > +\n> > +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > +\n> > +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> > +\t}\n> > +\n> > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\n> > +\t}\n> > +};\n> \n> Would it make sense to define the IPASettings structure in a .mojom file\n> to avoid manually-written serialization code ? Same for IPAStream and\n> IPABuffer.\n\nFor all the structs defined in ipa_interface.h that have no member\nfunctions, yes. It would mean another header would have to be included\nby generated serializers and proxy workers, but they're generated anyway :)\n\nI'd have to add a serializer for Size, and we'd have to keep\nthe FrameBuffer::Plane serializer, though.\n\nHm I'll have to shuffle things around in meson though... I'll put it in\nthe todo list.\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> dataVec;\n> > +\n> > +\t\tuint32_t strLen = data.model.size();\n> > +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> > +\n> > +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tCameraSensorInfo ret;\n> > +\n> > +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> > +\t\tret.model = str;\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> > +\n> > +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> > +\n> > +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> > +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> > +\n> > +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> > +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> > +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> > +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> > +\n> > +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> > +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> > +\n> > +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> > +\n> > +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> > +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> > +\n> > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPAStream ret;\n> > +\n> > +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> > +\t}\n> > +\n> > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd);\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> \n> This means we will serialize the ControlInfoMap every single time we\n> serialize a ControlList. That's very inefficient. The ControlInfoMap\n> instances are supposed to be serialized and transmitted upfront, at\n> configure() time, and then reused.\n\nYeah...\n\nI'll put this in the todo list as we discussed.\n\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> dataVec;\n> > +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\tif (!cs)\n> > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> > +\n> > +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t       ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> > +\t}\n> > +};\n> > +\n> > +template<>\n> > +class IPADataSerializer<ControlList>\n> \n> Requiring two specializations, one for const ControlList and one for\n> ControlList, is a sign that something is wrong somewhere. Same for\n> ControlInfoMap below.\n> \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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\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 dataBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\tControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > +\t\tstd::tie(dataVec, std::ignore) =\n> > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > +\n> > +\t\treturn {dataVec, {}};\n> > +\t}\n> > +\n> > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t  ControlSerializer *cs)\n> > +\t{\n> > +\t\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 dataBegin,\n> > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> > +\t\tstd::vector<int32_t> fdsVec;\n> > +\n> > +\t\tstd::vector<uint8_t> fdBuf;\n> > +\t\tstd::vector<int32_t> fdFds;\n> > +\t\tstd::tie(fdBuf, fdFds) =\n> > +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> > +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> > +\n> > +\t\treturn {dataVec, fdsVec};\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> > +\t\t\t\t\t      std::vector<int32_t> &fds,\n> > +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tFrameBuffer::Plane ret;\n> > +\n> > +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> > +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> > +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> > +\n> > +\t\treturn ret;\n> > +\t}\n> > +};\n> > +\n> > +\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> dataVec;\n> > +\n> > +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> > +\n> > +\t\tstd::vector<uint8_t> planesDataVec;\n> > +\t\tstd::vector<int32_t> planesFdsVec;\n> > +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > +\n> > +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> > +\n> > +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > +\t}\n> > +\n> > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > +\t{\n> > +\t\tIPABuffer ret;\n> > +\n> > +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > +\n> > +\t\tret.planes =\n> > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> > --- /dev/null\n> > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > @@ -0,0 +1,178 @@\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> > +namespace {\n> > +\n> > +/**\n> > + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> \n> POD also includes structures and arrays (see\n> https://en.cppreference.com/w/cpp/named_req/PODType). The correct term\n> here is \"arithmetic type\" (see\n> https://en.cppreference.com/w/cpp/types/is_arithmetic).\n> appendArithmetic() sounds a bit wrong though.\n\nYeah...\n\n> > + * \\brief Append uint to end of byte vector, in little-endian order\n> \n> s/uint/\\a val/ as it can also be a bool or a float. Same below, the\n> documentation should talk about arithmetic types and values, not uint.\n> \n> > + * \\tparam T Type of uint to append\n> \n> Oh, I didn't know about tparam. Interesting.\n> \n> > + * \\param[in] vec Byte vector to append to\n> > + * \\param[in] val Value of uint to append\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] vec Byte vector to read from\n> > + * \\param[in] pos Index in vec to start reading from\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> > + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> > + * for deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a vec at index \\a pos\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> > + * \\brief Read POD from byte vector, in little-endian order\n> > + * \\tparam T Type of POD to read\n> > + * \\param[in] it Iterator of byte vector to read from\n> > + * \\param[in] pos Index in byte vector to read from\n> > + * \\param[in] end Iterator marking end of byte vector\n> > + *\n> > + * This function is meant to be used by the IPA data serializer, and the\n> > + * generated IPA proxies.\n> > + *\n> > + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> > + * a fata error will occur, as it means there is insufficient data for\n> > + * deserialization, which should never happen.\n> > + *\n> > + * \\return The POD read from \\a it at index \\a pos\n> > + */\n> > +\n> > +} /* namespace */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> > + * \tT data,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Serialize an object into byte vector and fd vector\n> > + * \\tparam T Type of object to serialize\n> > + * \\param[in] data Object to serialize\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> > + * of \\a data\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() can be used if the object type \\a T and its\n> > + * members don't have any FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +/**\n> > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > + * \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 dataBegin,\n> > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > + * \tstd::vector<int32_t>::iterator fdsBegin,\n> > + * \tstd::vector<int32_t>::iterator fdsEnd,\n> > + * \tControlSerializer *cs = nullptr)\n> > + * \\brief Deserialize byte vector and fd vector into an object\n> > + * \\tparam T Type of object to deserialize to\n> > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> > + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> > + * \\param[in] cs ControlSerializer\n> > + *\n> > + * This version of deserialize() (or the vector version) must be used if\n> > + * the object type \\a T or its members contain FileDescriptor.\n> > + *\n> > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > + * ControlList or ControlInfoMap.\n> > + *\n> > + * \\return The deserialized object\n> > + */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 99b27e66..01bcffd4 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -24,6 +24,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\nThanks,\n\nPaul","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 2069BBE08A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Nov 2020 09:49:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8271C63441;\n\tThu, 26 Nov 2020 10:49:45 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 571B560331\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Nov 2020 10:49:44 +0100 (CET)","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 EB814A1B;\n\tThu, 26 Nov 2020 10:49:41 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"M9/10bMe\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1606384183;\n\tbh=+5FXStLZllMMNaxDhTXIZizfyH5gbEfh55qMnHu43JA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=M9/10bMec4w3+QhLQIHdIiOGEcIFFQ4I4GGPDJDpdWJW/qbW3rok7gFt23MTUHeE/\n\tyggf5MFE8Wk5j88zRrBy1tsa5Kl81XVBfoiu249Oi3ipf8mVyLQoqvf6DbTrgUpuhd\n\tv/sAwq1j0gLyMpuS7xz50DM0Z2M/PSRcT6sPmTZc=","Date":"Thu, 26 Nov 2020 18:49:35 +0900","From":"paul.elder@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20201126094935.GC2050@pyrite.rasen.tech>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>\n\t<20201120084343.GE4563@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201120084343.GE4563@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":13935,"web_url":"https://patchwork.libcamera.org/comment/13935/","msgid":"<20201126162812.GY3905@pendragon.ideasonboard.com>","date":"2020-11-26T16:28:12","subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nOn Thu, Nov 26, 2020 at 06:49:35PM +0900, paul.elder@ideasonboard.com wrote:\n> On Fri, Nov 20, 2020 at 10:43:43AM +0200, Laurent Pinchart wrote:\n> > On Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:\n> > > Add an IPADataSerializer which implments de/serialization of built-in\n> > \n> > s/implments/implements/\n> > \n> > > (PODs, vector, map, string) and libcamera data structures. This is\n> > > intended to be used by the proxy and the proxy worker in the IPC layer.\n> > > \n> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > \n> > > ---\n> > > Changes in v4:\n> > > - rename readUInt/appendUInt to readPOD/appendPOD\n> > >   - put them in anonymous namespace\n> > >   - and add length check\n> > >     - fatal if failure, because it means not enough data to deserialize,\n> > >       which is technically a segfault\n> > >   - use the new readPOD/appendPOD with length protections\n> > >   - expand on their docs correspondingly\n> > > - change snake_case to camelCase\n> > > - add optimizations to the hand-written de/serializers\n> > > - reserve vector length where trivially possible\n> > > - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n> > >   they're calling a specialization from the same specialization)\n> > > \n> > > Changes in v3:\n> > > - reimplement append/readUInt with memcpy (intead of bit shifting)\n> > > - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n> > >   - use this for int64_t, bool, float, and double\n> > > - fix comment style\n> > > \n> > > Changes in v2:\n> > > - added serializers for all integer types, bool, and string\n> > > - use string serializer for IPASettings serializer\n> > > - add documentation\n> > > - add serializer for const ControlList\n> > > ---\n> > >  .../libcamera/internal/ipa_data_serializer.h  | 1004 +++++++++++++++++\n> > >  src/libcamera/ipa_data_serializer.cpp         |  178 +++\n> > >  src/libcamera/meson.build                     |    1 +\n> > >  3 files changed, 1183 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..9a18f914\n> > > --- /dev/null\n> > > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > > @@ -0,0 +1,1004 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2020, Google Inc.\n> > > + *\n> > > + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> > > + */\n> > > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> > > +\n> > > +#include <deque>\n> > > +#include <iostream>\n> > > +#include <string.h>\n> > > +#include <tuple>\n> > > +#include <vector>\n> > > +\n> > > +#include <libcamera/buffer.h>\n> > > +#include <libcamera/control_ids.h>\n> > > +#include <libcamera/geometry.h>\n> > > +#include <libcamera/ipa/ipa_interface.h>\n> > > +\n> > > +#include \"libcamera/internal/byte_stream_buffer.h\"\n> > > +#include \"libcamera/internal/camera_sensor.h\"\n> > > +#include \"libcamera/internal/control_serializer.h\"\n> > > +#include \"libcamera/internal/log.h\"\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> > > +\n> > > +namespace {\n> > > +\n> > > +template<typename T>\n> > > +void appendPOD(std::vector<uint8_t> &vec, T val)\n> > \n> > An std::enable_if<std::is_arithmetic_v<T>> would help ensuring there's\n> > no misuse of this template.\n> \n> Ah okay.\n> \n> > > +{\n> > > +\tsize_t byteWidth = sizeof(val);\n> > > +\tstd::vector<uint8_t> v(byteWidth);\n> > > +\tmemcpy(&v[0], &val, byteWidth);\n> > > +\n> > > +\tvec.insert(vec.end(), v.begin(), v.end());\n> > \n> > \tvec.resize(vec.size() + byteWidth);\n> > \tmemcpy(vec.end() - byteWidth, &val, byteWidth);\n> > \n> > If we want to be efficient I think we'll need to implement our own\n> > container instead of using std::vector<>, in order to minimize the\n> > number of reallocations.\n> > \n> > In general I think the serialization and deserialization code have room\n> > for optimization, which is completely expected. Perfection can wait for\n> > a follow-up series ;-) I'd like to focus on the interface with the\n> > pipeline handlers and the base IPC mechanism as the target to get this\n> > series merged. The internal implementation can then be reworked later.\n> > \n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +T readPOD(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> > \n> > There's a risk of an integer overflow if the incoming message has a size\n> > set to UINT_MAX for instance. This check will pass, and you'll write\n> > data to vec[-1] below.\n> \n> I've since removed this and it takes the iterators and calls the\n> iterator version instead, so that should solve the integer overflow\n> issue.\n> \n> > > +\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > \n> > If you want to make this fatal, then there should be checks in the\n> > (generated) caller code to handle the issue gracefully. We don't want to\n> > crash because of a malformed message.\n> \n> So the caller has protections against trying to read more data than\n> exists, either because that's what the message said, or because the\n> message got truncated (eg. in the generated code):\n> \n> \tif (dataSize < 4) {\n> \t\tLOG(IPADataSerializer, Error)\n> \t\t\t<< \"Failed to deserialize \" << \"staggeredWriteResultSize\"\n> \t\t\t<< \": not enough data, expected \"\n> \t\t\t<< (4) << \", got \" << (dataSize);\n> \t\treturn ret;\n> \t}\n> \tconst size_t staggeredWriteResultSize = readPOD<uint32_t>(m, 0, dataEnd);\n> \tm += 4;\n> \tdataSize -= 4;\n> \n> So if readPOD really receives not enough data, then something is\n> /really/ wrong. Or is the caller's protection sufficient and we can\n> remove the Fatal from readPOD?\n\nWith guaranteed protection in the caller, we would at least turn this\ninto an ASSERT().\n\n> > > +\n> > > +\tmemcpy(&ret, &vec[pos], byteWidth);\n> > > +\treturn ret;\n> > > +}\n> > > +\n> > > +template<typename T>\n> > > +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > > +\t  std::vector<uint8_t>::iterator end)\n> > > +{\n> > > +\tT ret = 0;\n> > > +\tsize_t byteWidth = sizeof(ret);\n> > > +\n> > > +\tif (pos + it >= end)\n> > > +\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t<< \"Not enough data to deserialize POD\";\n> > > +\n> > > +\tstd::vector<uint8_t> v(it + pos, it + pos + byteWidth);\n> > > +\tmemcpy(&ret, v.data(), byteWidth);\n> > > +\treturn ret;\n> > > +}\n> > \n> > Do we need both functions ?\n> \n> No, I made the vector one call the iterator one.\n> \n> > As commented in the review of 04/37, using a Span<const uint8_t> would\n> > likely be best for deserialization.\n> > \n> > > +\n> > > +} /* namespace */\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> > s/T/const T &/\n> > \n> > > +\n> > > +\tstatic T deserialize(std::vector<uint8_t> &data,\n> > \n> > const. Same everywhere below.\n> > \n> > > +\t\t\t     ControlSerializer *cs);\n> > > +\tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\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 dataBegin,\n> > > +\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t     ControlSerializer *cs);\n> > \n> > If you switch to span, I think all these could be merged into a single\n> > function.\n> > \n> > \tstatic T deserialize(Span<const uint8_t> data,\n> > \t\t\t     Span<const int32_t> fds,\n> > \t\t\t     ControlSerializer *cs);\n> > \n> > This can be called with\n> > \n> > \tdeserialize<T>(data, {}, cs);\n> > \n> > when no fds is available.\n> \n> Yeah, that would be nice...\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> dataVec;\n> > > +\t\tstd::vector<int32_t> fdsVec;\n> > > +\n> > > +\t\t/* Serialize the length. */\n> > > +\t\tuint32_t vecLen = data.size();\n> > > +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> > > +\n> > > +\t\t/* Serialize the members. */\n> > > +\t\tfor (auto const &it : data) {\n> > > +\t\t\tstd::vector<uint8_t> dvec;\n> > > +\t\t\tstd::vector<int32_t> fvec;\n> > > +\n> > > +\t\t\tstd::tie(dvec, fvec) =\n> > > +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > > +\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > > +\n> > > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > > +\t\t}\n> > > +\n> > > +\t\treturn {dataVec, fdsVec};\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<int32_t> fds;\n> > > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > \n> > dataEnd is used.\n> > \n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\t\tstd::vector<V> ret(vecLen);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > > +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> > > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > > +\n> > > +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > > +\t\t\t\t\t\t\t\t   dataIter + 8 + sizeofData,\n> > > +\t\t\t\t\t\t\t\t   fdIter,\n> > > +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> > \n> > This could go past fdsEnd.\n> \n> Oh shoot yeah...\n> \n> > The deserializer code will need to be hardened, which will likely\n> > involve a refactoring. I think it can be merged with a few issues, we'll\n> > fix them on top.\n> \n> For now I could just jam in protection checks like what we have in the\n> generated code. I guess at this point the generated code is more\n> hardened haha...\n\nLet's stop writing code manually then. We only need a code generator\nthat is generated from a ... hmmm... :-)\n\n> > These two comments apply to the code below as well.\n> > \n> > > +\t\t\t\t\t\t\t\t   cs);\n> > > +\n> > > +\t\t\tdataIter += 8 + sizeofData;\n> > > +\t\t\tfdIter += sizeofFds;\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> dataVec;\n> > > +\t\tstd::vector<int32_t> fdsVec;\n> > > +\n> > > +\t\t/* Serialize the length. */\n> > > +\t\tuint32_t mapLen = data.size();\n> > > +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> > > +\n> > > +\t\t/* Serialize the members. */\n> > > +\t\tfor (auto const &it : data) {\n> > > +\t\t\tstd::vector<uint8_t> dvec;\n> > > +\t\t\tstd::vector<int32_t> fvec;\n> > > +\n> > > +\t\t\tstd::tie(dvec, fvec) =\n> > > +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > > +\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > > +\n> > > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > > +\n> > > +\t\t\tstd::tie(dvec, fvec) =\n> > > +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> > > +\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> > > +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> > > +\n> > > +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> > > +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> > > +\t\t}\n> > > +\n> > > +\t\treturn {dataVec, fdsVec};\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<int32_t> fds;\n> > > +\t\treturn deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::map<K, V> ret;\n> > > +\n> > > +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator dataIter = dataBegin + 4;\n> > > +\t\tstd::vector<int32_t>::iterator fdIter = fdsBegin;\n> > > +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> > > +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > > +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > > +\n> > > +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter + 8,\n> > > +\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > > +\t\t\t\t\t\t\t\t  fdIter,\n> > > +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > > +\t\t\t\t\t\t\t\t  cs);\n> > > +\n> > > +\t\t\tdataIter += 8 + sizeofData;\n> > > +\t\t\tfdIter += sizeofFds;\n> > > +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > > +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > > +\n> > > +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter + 8,\n> > > +\t\t\t\t\t\t\t\t\t  dataIter + 8 + sizeofData,\n> > > +\t\t\t\t\t\t\t\t\t  fdIter,\n> > > +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> > > +\t\t\t\t\t\t\t\t\t  cs);\n> > > +\t\t\tret.insert({key, value});\n> > > +\n> > > +\t\t\tdataIter += 8 + sizeofData;\n> > > +\t\t\tfdIter += sizeofFds;\n> > > +\t\t}\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\n> > \n> > All the specializations below can go to a .cpp file.\n> \n> They can? I thought then they won't be visible to anybody else?\n> \n> Indeed the compiler complains that the the specialized serializers and\n> the proxy workers can't find the appropriate specialization.\n\nHere's what I meant:\n\ndiff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\nindex 574d8f445983..467b964f6d93 100644\n--- a/include/libcamera/internal/ipa_data_serializer.h\n+++ b/include/libcamera/internal/ipa_data_serializer.h\n@@ -66,22 +66,22 @@ class IPADataSerializer\n {\n public:\n \tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n-\tserialize(T data, ControlSerializer *cs);\n+\tserialize(T data, ControlSerializer *cs = nullptr);\n\n \tstatic T deserialize(std::vector<uint8_t> &data,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n \tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n \t\t\t     std::vector<uint8_t>::iterator dataEnd,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\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+\t\t\t     ControlSerializer *cs = nullptr);\n \tstatic T deserialize(std::vector<uint8_t>::iterator dataBegin,\n \t\t\t     std::vector<uint8_t>::iterator dataEnd,\n \t\t\t     std::vector<int32_t>::iterator fdsBegin,\n \t\t\t     std::vector<int32_t>::iterator fdsEnd,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n };\n\n #ifndef __DOXYGEN__\n@@ -293,64 +293,6 @@ public:\n \t}\n };\n\n-#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n-\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n-\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n-\t\t\t\t\t\t\t\t\t\\\n-\t\treturn { dataVec, {} };\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n-\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n-\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n-\t{\t\t\t\t\t\t\t\t\\\n-\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n-\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n-\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n-\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n-\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n-\t{\t\t\t\t\t\t\t\t\\\n-\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n-\t}\t\t\t\t\t\t\t\t\\\n-};\n-\n-DECLARE_POD_SERIALIZER(bool)\n-DECLARE_POD_SERIALIZER(uint8_t)\n-DECLARE_POD_SERIALIZER(uint16_t)\n-DECLARE_POD_SERIALIZER(uint32_t)\n-DECLARE_POD_SERIALIZER(uint64_t)\n-DECLARE_POD_SERIALIZER(int8_t)\n-DECLARE_POD_SERIALIZER(int16_t)\n-DECLARE_POD_SERIALIZER(int32_t)\n-DECLARE_POD_SERIALIZER(int64_t)\n-DECLARE_POD_SERIALIZER(float)\n-DECLARE_POD_SERIALIZER(double)\n-\n /*\n  * Strings are serialized simply by converting by {string.begin(), string.end()}.\n  * The size of the string is recorded by the container (struct, vector, map, or\ndiff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\nindex 7a228e1470df..53ddfcfa80ba 100644\n--- a/src/libcamera/ipa_data_serializer.cpp\n+++ b/src/libcamera/ipa_data_serializer.cpp\n@@ -175,4 +175,62 @@ namespace {\n  * \\return The deserialized object\n  */\n\n+#define DECLARE_POD_SERIALIZER(type)\t\t\t\t\t\\\n+template<>\t\t\t\t\t\t\t\\\n+std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\t\\\n+IPADataSerializer<type>::serialize(const type data,\t\t\t\t\t\\\n+\t  [[maybe_unused]] ControlSerializer *cs)\t\\\n+{\t\t\t\t\t\t\t\t\\\n+\tstd::vector<uint8_t> dataVec;\t\t\t\t\\\n+\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n+\tappendPOD<type>(dataVec, data);\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+\treturn { dataVec, {} };\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+template<>\t\t\t\t\t\t\t\\\n+type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::iterator dataBegin,\\\n+\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n+\t\t\t[[maybe_unused]] ControlSerializer *cs)\\\n+{\t\t\t\t\t\t\t\t\\\n+\treturn readPOD<type>(dataBegin, 0, dataEnd);\t\t\\\n+}\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+template<>\t\t\t\t\t\t\t\\\n+type IPADataSerializer<type>::deserialize(std::vector<uint8_t> &data,\t\t\\\n+\t\t\t[[maybe_unused]] ControlSerializer *cs)\\\n+{\t\t\t\t\t\t\t\t\\\n+\treturn deserialize(data.begin(), data.end());\t\t\\\n+}\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+template<>\t\t\t\t\t\t\t\\\n+type IPADataSerializer<type>::deserialize(std::vector<uint8_t> &data,\t\t\\\n+\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\\\n+\t\t\t[[maybe_unused]] ControlSerializer *cs)\\\n+{\t\t\t\t\t\t\t\t\\\n+\treturn deserialize(data.begin(), data.end());\t\t\\\n+}\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+template<>\t\t\t\t\t\t\t\\\n+type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::iterator dataBegin,\\\n+\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n+\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n+\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n+\t\t\t[[maybe_unused]] ControlSerializer *cs)\\\n+{\t\t\t\t\t\t\t\t\\\n+\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n+}\t\t\t\t\t\t\t\t\\\n+\n+DECLARE_POD_SERIALIZER(bool)\n+DECLARE_POD_SERIALIZER(uint8_t)\n+DECLARE_POD_SERIALIZER(uint16_t)\n+DECLARE_POD_SERIALIZER(uint32_t)\n+DECLARE_POD_SERIALIZER(uint64_t)\n+DECLARE_POD_SERIALIZER(int8_t)\n+DECLARE_POD_SERIALIZER(int16_t)\n+DECLARE_POD_SERIALIZER(int32_t)\n+DECLARE_POD_SERIALIZER(int64_t)\n+DECLARE_POD_SERIALIZER(float)\n+DECLARE_POD_SERIALIZER(double)\n+\n } /* namespace libcamera */\n\n\nSame for the other specilizations. Only specializations that have\ntemplate arguments (map and vector) need to be inlined. All the other\nones can go to the .cpp file, and will cause the compiler to generate a\nsingle implementation, instead of inlining them.\n\n> > > +\n> > > +#define DECLARE_POD_SERIALIZER(type)\t\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> dataVec;\t\t\t\t\\\n> > > +\t\tdataVec.reserve(sizeof(type));\t\t\t\t\\\n> > > +\t\tappendPOD<type>(dataVec, data);\t\t\t\\\n> > > +\t\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn readPOD<type>(dataBegin, 0, dataEnd);\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 deserialize(data.begin(), data.end());\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>::iterator dataBegin,\\\n> > > +\t\t\t\tstd::vector<uint8_t>::iterator dataEnd,\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\\\n> > > +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\\\n> > > +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> > > +\t{\t\t\t\t\t\t\t\t\\\n> > > +\t\treturn deserialize(dataBegin, dataEnd);\t\t\t\\\n> > > +\t}\t\t\t\t\t\t\t\t\\\n> > > +};\n> > > +\n> > > +DECLARE_POD_SERIALIZER(bool)\n> > > +DECLARE_POD_SERIALIZER(uint8_t)\n> > > +DECLARE_POD_SERIALIZER(uint16_t)\n> > > +DECLARE_POD_SERIALIZER(uint32_t)\n> > > +DECLARE_POD_SERIALIZER(uint64_t)\n> > > +DECLARE_POD_SERIALIZER(int8_t)\n> > > +DECLARE_POD_SERIALIZER(int16_t)\n> > > +DECLARE_POD_SERIALIZER(int32_t)\n> > > +DECLARE_POD_SERIALIZER(int64_t)\n> > > +DECLARE_POD_SERIALIZER(float)\n> > > +DECLARE_POD_SERIALIZER(double)\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<std::string>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn {{data.begin(), data.end()}, {}};\n> > \n> > Shouldn't we prefix strings by their length, like we do for vectors,\n> > instead of relying on the terminating 0 ?\n> \n> We actually don't rely on the terminating zero. All\n> containers of strings (function parameter, struct, vector/map)\n> already record the length, and pass the full vector or iterator pair to\n> the deserializer, so this is actually sufficient.\n> \n> I do the same trick with vectors and maps, actually. Notice they don't\n> contain their length in bytes, but actually their length in number of\n> elements. This is for the same reason; every container (function\n> parameter, struct, vector/map) of vectors/maps already contains the length\n> in bytes.\n> \n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn {data.begin(), data.end()};\n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn {dataBegin, dataEnd};\n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn {data.begin(), data.end()};\n> > > +\t}\n> > > +\n> > > +\tstatic std::string deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn {dataBegin, dataEnd};\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<FileDescriptor>\n> > > +{\n> > > +public:\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> > > +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> > \n> > I think you should store an int32_t here, set to -1 when the fd is\n> > invalid. Ideally it should contain the index of the entry in the fds\n> \n> I don't see how that's better than the scheme that I have here now...\n\nThe idea, with storing an int32_t, is that deserialization could then be\ndone using views instead of copying data. The serialized buffer would be\nupdated in-place to replace the fd in the data vector with the actual\nvalue, and then view classes would allow accessing data directly in the\ndata vector. That's an optimization for later.\n\n> > vector for valid fds, but that will require a refactoring of the\n> > serialization code to pass the data and fds vectors to the serialization\n> > functions. It can thus be done later.\n> > \n> > > +\t\tstd::vector<int32_t> fdVec;\n> > > +\t\tif (data.isValid())\n> > > +\t\t\tfdVec.push_back(data.fd());\n> > > +\n> > > +\t\treturn {dataVec, fdVec};\n> > > +\t}\n> > > +\n> > > +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end());\n> > > +\t}\n> > > +\n> > > +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t  std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tif (std::distance(dataBegin, dataEnd) < 1)\n> > > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n> > > +\n> > > +\t\tbool valid = !!(*dataBegin);\n> > > +\n> > > +\t\tif (valid && std::distance(fdsBegin, fdsEnd) < 1)\n> > > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n> > > +\n> > > +\t\treturn valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\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<std::string>::deserialize(data.begin(), data.end()) };\n> > > +\t}\n> > > +\n> > > +\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };\n> > > +\t}\n> > > +};\n> > \n> > Would it make sense to define the IPASettings structure in a .mojom file\n> > to avoid manually-written serialization code ? Same for IPAStream and\n> > IPABuffer.\n> \n> For all the structs defined in ipa_interface.h that have no member\n> functions, yes. It would mean another header would have to be included\n> by generated serializers and proxy workers, but they're generated anyway :)\n> \n> I'd have to add a serializer for Size, and we'd have to keep\n> the FrameBuffer::Plane serializer, though.\n\nA serializer for Size (and likely later for other geometry classes)\nwould make sense.\n\n> Hm I'll have to shuffle things around in meson though... I'll put it in\n> the todo list.\n\nFeel free to give it a go whenever you'll have \"free\" time ;-)\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> dataVec;\n> > > +\n> > > +\t\tuint32_t strLen = data.model.size();\n> > > +\t\tappendPOD<uint32_t>(dataVec, strLen);\n> > > +\n> > > +\t\tdataVec.insert(dataVec.end(), data.model.begin(), data.model.end());\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.bitsPerPixel);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.activeAreaSize.height);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));\n> > > +\t\tappendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.analogCrop.height);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.outputSize.height);\n> > > +\n> > > +\t\tappendPOD<uint64_t>(dataVec, data.pixelRate);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.lineLength);\n> > > +\n> > > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tCameraSensorInfo ret;\n> > > +\n> > > +\t\tuint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\t\tstd::string str(dataBegin + 4, dataBegin + 4 + strLen);\n> > > +\t\tret.model = str;\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;\n> > > +\n> > > +\t\tret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);\n> > > +\n> > > +\t\tret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);\n> > > +\t\tret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);\n> > > +\n> > > +\t\tret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));\n> > > +\t\tret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));\n> > > +\t\tret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);\n> > > +\t\tret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);\n> > > +\n> > > +\t\tret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);\n> > > +\t\tret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);\n> > > +\n> > > +\t\tret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);\n> > > +\n> > > +\t\tret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);\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 deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t    std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(dataBegin, dataEnd);\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> dataVec;\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.pixelFormat);\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.size.width);\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.size.height);\n> > > +\n> > > +\t\treturn {dataVec, {}};\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 deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tIPAStream ret;\n> > > +\n> > > +\t\tret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\n> > > +\t\tret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > > +\t\tret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);\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 deserialize(data.begin(), data.end());\n> > > +\t}\n> > > +\n> > > +\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(dataBegin, dataEnd);\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> > \n> > This means we will serialize the ControlInfoMap every single time we\n> > serialize a ControlList. That's very inefficient. The ControlInfoMap\n> > instances are supposed to be serialized and transmitted upfront, at\n> > configure() time, and then reused.\n> \n> Yeah...\n> \n> I'll put this in the todo list as we discussed.\n\nIf \\todo had a priority argument, it should be high for this one. As\ncommented today in replies to other patches in the series, I'd really\nlike to avoid the global ControlInfoMap instances, so this code may need\nto be reworked sooner than expected.\n\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> dataVec;\n> > > +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> > > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > > +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> > > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > > +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> > > +\n> > > +\t\treturn {dataVec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\tif (!cs)\n> > > +\t\t\tLOG(IPADataSerializer, Fatal)\n> > > +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> > > +\n> > > +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t       ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> > > +\t}\n> > > +};\n> > > +\n> > > +template<>\n> > > +class IPADataSerializer<ControlList>\n> > \n> > Requiring two specializations, one for const ControlList and one for\n> > ControlList, is a sign that something is wrong somewhere. Same for\n> > ControlInfoMap below.\n> > \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> dataVec;\n> > > +\t\tstd::tie(dataVec, std::ignore) =\n> > > +\t\t\tIPADataSerializer<const ControlList>::serialize(list_const, map, cs);\n> > > +\n> > > +\t\treturn {dataVec, {}};\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 dataBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > > +\t\t\t\t       std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > > +\t\t\t\t\t\t\t\t\t  fdsBegin, fdsEnd, 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> dataVec;\n> > > +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> > > +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> > > +\n> > > +\t\treturn {dataVec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t\tControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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 = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\n> > > +\t\tstd::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\treturn deserialize(dataBegin, dataEnd, 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> dataVec;\n> > > +\t\tstd::tie(dataVec, std::ignore) =\n> > > +\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n> > > +\n> > > +\t\treturn {dataVec, {}};\n> > > +\t}\n> > > +\n> > > +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t  ControlSerializer *cs)\n> > > +\t{\n> > > +\t\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 dataBegin,\n> > > +\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\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(dataBegin, dataEnd, 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 dataBegin,\n> > > +\t\t\t\t\t  std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\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(dataBegin, dataEnd,\n> > > +\t\t\t\t\t\t\t\t\t     fdsBegin, fdsEnd, 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> dataVec;\n> > > +\t\tstd::vector<int32_t> fdsVec;\n> > > +\n> > > +\t\tstd::vector<uint8_t> fdBuf;\n> > > +\t\tstd::vector<int32_t> fdFds;\n> > > +\t\tstd::tie(fdBuf, fdFds) =\n> > > +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > > +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> > > +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> > > +\n> > > +\t\treturn {dataVec, fdsVec};\n> > > +\t}\n> > > +\n> > > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t      std::vector<int32_t> &fds,\n> > > +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\treturn deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t\t      std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tFrameBuffer::Plane ret;\n> > > +\n> > > +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> > > +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> > > +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> > > +\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +};\n> > > +\n> > > +\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> dataVec;\n> > > +\n> > > +\t\tappendPOD<uint32_t>(dataVec, data.id);\n> > > +\n> > > +\t\tstd::vector<uint8_t> planesDataVec;\n> > > +\t\tstd::vector<int32_t> planesFdsVec;\n> > > +\t\tstd::tie(planesDataVec, planesFdsVec) =\n> > > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n> > > +\n> > > +\t\tdataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());\n> > > +\n> > > +\t\treturn {dataVec, planesFdsVec};\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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);\n> > > +\t}\n> > > +\n> > > +\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,\n> > > +\t\t\t\t     std::vector<uint8_t>::iterator dataEnd,\n> > > +\t\t\t\t     std::vector<int32_t>::iterator fdsBegin,\n> > > +\t\t\t\t     std::vector<int32_t>::iterator fdsEnd,\n> > > +\t\t\t\t     ControlSerializer *cs = nullptr)\n> > > +\t{\n> > > +\t\tIPABuffer ret;\n> > > +\n> > > +\t\tret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > > +\n> > > +\t\tret.planes =\n> > > +\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n> > > +\t\t\t\tdataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362\n> > > --- /dev/null\n> > > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > > @@ -0,0 +1,178 @@\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> > > +namespace {\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> > \n> > POD also includes structures and arrays (see\n> > https://en.cppreference.com/w/cpp/named_req/PODType). The correct term\n> > here is \"arithmetic type\" (see\n> > https://en.cppreference.com/w/cpp/types/is_arithmetic).\n> > appendArithmetic() sounds a bit wrong though.\n> \n> Yeah...\n> \n> > > + * \\brief Append uint to end of byte vector, in little-endian order\n> > \n> > s/uint/\\a val/ as it can also be a bool or a float. Same below, the\n> > documentation should talk about arithmetic types and values, not uint.\n> > \n> > > + * \\tparam T Type of uint to append\n> > \n> > Oh, I didn't know about tparam. Interesting.\n> > \n> > > + * \\param[in] vec Byte vector to append to\n> > > + * \\param[in] val Value of uint to append\n> > > + *\n> > > + * This function is meant to be used by the IPA data serializer, and the\n> > > + * generated IPA proxies.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> > > + * \\brief Read POD from byte vector, in little-endian order\n> > > + * \\tparam T Type of POD to read\n> > > + * \\param[in] vec Byte vector to read from\n> > > + * \\param[in] pos Index in vec to start reading from\n> > > + *\n> > > + * This function is meant to be used by the IPA data serializer, and the\n> > > + * generated IPA proxies.\n> > > + *\n> > > + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> > > + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> > > + * for deserialization, which should never happen.\n> > > + *\n> > > + * \\return The POD read from \\a vec at index \\a pos\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> > > + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> > > + * \\brief Read POD from byte vector, in little-endian order\n> > > + * \\tparam T Type of POD to read\n> > > + * \\param[in] it Iterator of byte vector to read from\n> > > + * \\param[in] pos Index in byte vector to read from\n> > > + * \\param[in] end Iterator marking end of byte vector\n> > > + *\n> > > + * This function is meant to be used by the IPA data serializer, and the\n> > > + * generated IPA proxies.\n> > > + *\n> > > + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> > > + * a fata error will occur, as it means there is insufficient data for\n> > > + * deserialization, which should never happen.\n> > > + *\n> > > + * \\return The POD read from \\a it at index \\a pos\n> > > + */\n> > > +\n> > > +} /* namespace */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> > > + * \tT data,\n> > > + * \tControlSerializer *cs = nullptr)\n> > > + * \\brief Serialize an object into byte vector and fd vector\n> > > + * \\tparam T Type of object to serialize\n> > > + * \\param[in] data Object to serialize\n> > > + * \\param[in] cs ControlSerializer\n> > > + *\n> > > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > > + * ControlList or ControlInfoMap.\n> > > + *\n> > > + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> > > + * of \\a data\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > > + * \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 dataBegin,\n> > > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > > + * \tControlSerializer *cs = nullptr)\n> > > + * \\brief Deserialize byte vector into an object\n> > > + * \\tparam T Type of object to deserialize to\n> > > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > > + * \\param[in] cs ControlSerializer\n> > > + *\n> > > + * This version of deserialize() can be used if the object type \\a T and its\n> > > + * members don't have any FileDescriptor.\n> > > + *\n> > > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > > + * ControlList or ControlInfoMap.\n> > > + *\n> > > + * \\return The deserialized object\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > > + * \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 dataBegin,\n> > > + * \tstd::vector<uint8_t>::iterator dataEnd,\n> > > + * \tstd::vector<int32_t>::iterator fdsBegin,\n> > > + * \tstd::vector<int32_t>::iterator fdsEnd,\n> > > + * \tControlSerializer *cs = nullptr)\n> > > + * \\brief Deserialize byte vector and fd vector into an object\n> > > + * \\tparam T Type of object to deserialize to\n> > > + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> > > + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> > > + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> > > + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> > > + * \\param[in] cs ControlSerializer\n> > > + *\n> > > + * This version of deserialize() (or the vector version) must be used if\n> > > + * the object type \\a T or its members contain FileDescriptor.\n> > > + *\n> > > + * \\a cs is only necessary if the object type \\a T or its members contain\n> > > + * ControlList or ControlInfoMap.\n> > > + *\n> > > + * \\return The deserialized object\n> > > + */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > index 99b27e66..01bcffd4 100644\n> > > --- a/src/libcamera/meson.build\n> > > +++ b/src/libcamera/meson.build\n> > > @@ -24,6 +24,7 @@ libcamera_sources = files([\n> > >      'geometry.cpp',\n> > >      'ipa_context_wrapper.cpp',\n> > >      'ipa_controls.cpp',\n> > > +    'ipa_data_serializer.cpp',\n> > >      'ipa_interface.cpp',\n> > >      'ipa_manager.cpp',\n> > >      'ipa_module.cpp',","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 84CABBE08A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Nov 2020 16:28:24 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0420863481;\n\tThu, 26 Nov 2020 17:28:24 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1EFF46346B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Nov 2020 17:28:22 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 631FBA1B;\n\tThu, 26 Nov 2020 17:28:21 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"qe6qhMdQ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1606408101;\n\tbh=Joyoewn//QbxGcFq5YhBOgPM6oXBRap9smDRIWXr0Hg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=qe6qhMdQHhqGv+l25JiUuyxRq+3n7caES0CCRCVp+r7nYqiv/i/CHjNFUjmQ5V7GY\n\tzuAYZ3129S6o9iRFOiiVWhhJSsicQVnDWZ63srt3O+i7edVAdZcO96dJAx+eLhSWl1\n\tum1qH4TEJuNKyLToFMbXRnAxLMnPS5eJ/8C6wGdg=","Date":"Thu, 26 Nov 2020 18:28:12 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"paul.elder@ideasonboard.com","Message-ID":"<20201126162812.GY3905@pendragon.ideasonboard.com>","References":"<20201106103707.49660-1-paul.elder@ideasonboard.com>\n\t<20201106103707.49660-9-paul.elder@ideasonboard.com>\n\t<20201120084343.GE4563@pendragon.ideasonboard.com>\n\t<20201126094935.GC2050@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201126094935.GC2050@pyrite.rasen.tech>","Subject":"Re: [libcamera-devel] [PATCH v4 08/37] libcamera: Add\n\tIPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]