[{"id":14359,"web_url":"https://patchwork.libcamera.org/comment/14359/","msgid":"<X+h5n2byjlnDffUt@wyvern>","date":"2020-12-27T12:10:07","subject":"Re: [libcamera-devel] [PATCH v6 6/9] 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 work.\n\nOn 2020-12-24 17:15:31 +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> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nI have read it all and looks really nice, I have not compared each \nlibcamera type ser/des matches the class implementations in master as of \ntoday. It's nice work.\n\nAcked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\n> \n> ---\n> Changes in v6:\n> - remove (de)serializers for IPASettings, CameraSensorInfo, IPAStream,\n>   and IPABuffer as they are now defined in core.mojom and generated\n> - ControlList (de)serializer no longer needs a ControlInfoMap\n> - ControlList (de)serializer now checks the ControlSerializer if the\n>   ControlList's ControlInfoMap is cached\n> - add some todos for optimization and hardening\n> \n> Changes in v5:\n> - fix style of: {{}, {}} -> { {}, {} }\n> - add documentation on serialization formats (not in doxygen, though)\n> - compress readPOD\n>   - use memcpy\n>   - use ASSERT\n> - remove ifdef DOXYGEN guard from base IPADataSerializer\n> - remove integer overflow risk when calculating vector offsets\n> - use const iterators and const references\n> - remove const ControlList and const ControlInfoMap specializations\n> \n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n> \n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n> \n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 701 ++++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         | 185 +++++\n>  src/libcamera/meson.build                     |   1 +\n>  3 files changed, 887 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..5468a0b7\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,701 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <type_traits>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T>\n> +void appendPOD(std::vector<uint8_t> &vec,\n> +\t       typename std::enable_if<std::is_arithmetic_v<T>, T>::type val)\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n> +\tvec.resize(vec.size() + byteWidth);\n> +\tmemcpy(&*(vec.end() - byteWidth), &val, byteWidth);\n> +}\n> +\n> +template<typename T>\n> +typename std::enable_if<std::is_arithmetic_v<T>, T>::type\n> +readPOD(std::vector<uint8_t>::const_iterator it, size_t pos,\n> +\tstd::vector<uint8_t>::const_iterator end)\n> +{\n> +\tASSERT(pos + it < end);\n> +\n> +\tT ret = 0;\n> +\tmemcpy(&ret, &(*(it + pos)), sizeof(ret));\n> +\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +typename std::enable_if<std::is_arithmetic_v<T>, T>::type\n> +readPOD(std::vector<uint8_t> &vec, size_t pos)\n> +{\n> +\treturn readPOD<T>(vec.cbegin(), pos, vec.end());\n> +}\n> +\n> +} /* namespace */\n> +\n> +template<typename T>\n> +class IPADataSerializer\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const T &data, ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     const std::vector<int32_t> &fds,\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs);\n> +};\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +/*\n> + * Serialization format for vector of type V:\n> + *\n> + * 4 bytes - uint32_t Length of vector, in number of elements\n> + *\n> + * For every element in the vector:\n> + *\n> + * 4 bytes - uint32_t Size of element, in bytes\n> + * 4 bytes - uint32_t Size of fds for the element, in number of fds\n> + * X bytes - Serialized element\n> + */\n> +template<typename V>\n> +class IPADataSerializer<std::vector<V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t   dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +/*\n> + * Serialization format for map of key type K and value type V:\n> + *\n> + * 4 bytes - uint32_t Length of map, in number of pairs\n> + *\n> + * For every pair in the map:\n> + *\n> + * 4 bytes - uint32_t Size of key, in bytes\n> + * 4 bytes - uint32_t Size of fds for the key, in number of fds\n> + * X bytes - Serialized key\n> + * 4 bytes - uint32_t Size of value, in bytes\n> + * 4 bytes - uint32_t Size of fds for the value, in number of fds\n> + * X bytes - Serialized value\n> + */\n> +template<typename K, typename V>\n> +class IPADataSerializer<std::map<K, V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#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.cbegin(), 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>::const_iterator dataBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::const_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.cbegin(), 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>::const_iterator dataBegin,\\\n> +\t\t\t\tstd::vector<uint8_t>::const_iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\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.cbegin(), string.end()}.\n> + * The size of the string is recorded by the container (struct, vector, map, or\n> + * function parameter serdes).\n> + */\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { { data.cbegin(), data.end() }, {} };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +};\n> +\n> +/*\n> + * FileDescriptors are serialized into a single byte that tells if the\n> + * FileDescriptor is valid or not. If it is valid, then for serialization\n> + * the fd will be written to the fd vector, or for deserialization the\n> + * fd vector const_iterator will be valid.\n> + *\n> + * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n> + * also allows us to simply send the entire fd vector into the deserializer\n> + * and it will be recursively consumed as necessary.\n> + */\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn { dataVec, fdVec };\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tASSERT(std::distance(dataBegin, dataEnd) >= 1);\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : FileDescriptor();\n> +\t}\n> +};\n> +\n> +/*\n> + * ControlList is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * 4 bytes - uint32_t Size of serialized ControlList, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + * X bytes - Serialized ControlList (using ControlSerializer)\n> + *\n> + * If data.infoMap() is nullptr, then the default controls::controls will\n> + * be used. The serialized ControlInfoMap will have zero length.\n> + */\n> +template<>\n> +class IPADataSerializer<ControlList>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlList &data, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlList\";\n> +\n> +\t\tsize_t size;\n> +\t\tstd::vector<uint8_t> infoData;\n> +\t\tint ret;\n> +\n> +\t\tif (data.infoMap() && !cs->isCached(data.infoMap())) {\n> +\t\t\tsize = cs->binarySize(*data.infoMap());\n> +\t\t\tinfoData.resize(size);\n> +\t\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\t\tret = cs->serialize(*data.infoMap(), buffer);\n> +\n> +\t\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList's ControlInfoMap\";\n> +\t\t\t\treturn { {}, {} };\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize = cs->binarySize(data);\n> +\t\tstd::vector<uint8_t> listData(size);\n> +\t\tByteStreamBuffer buffer(listData.data(), listData.size());\n> +\t\tret = cs->serialize(data, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tif (!std::distance(dataBegin, dataEnd))\n> +\t\t\treturn {};\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator it = dataBegin + 8;\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\t\tstd::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);\n> +\n> +\t\tif (infoDataSize > 0) {\n> +\t\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n> +\t\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\t\t\t/* It's fine if map is empty. */\n> +\t\t\tif (buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error)\n> +\t\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> +\t\t\t\treturn ControlList();\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tByteStreamBuffer buffer(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 ControlList deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * const ControlInfoMap is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + */\n> +template<>\n> +class IPADataSerializer<ControlInfoMap>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlInfoMap &map, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlInfoMap\";\n> +\n> +\t\tsize_t size = cs->binarySize(map);\n> +\t\tstd::vector<uint8_t> infoData(size);\n> +\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\tint ret = cs->serialize(map, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlInfoMap\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlInfoMap\";\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_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\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\n> +\t\treturn map;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * FrameBuffer::Plane is serialized as:\n> + *\n> + * 1 byte  - FileDescriptor\n> + * 4 bytes - uint32_t Length\n> + */\n> +template<>\n> +class IPADataSerializer<FrameBuffer::Plane>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t      std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#endif /* __DOXYGEN__ */\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */\n> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> new file mode 100644\n> index 00000000..34287d6a\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,185 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer\n> + */\n> +\n> +#include \"libcamera/internal/ipa_data_serializer.h\"\n> +\n> +#include \"libcamera/internal/log.h\"\n> +\n> +/**\n> + * \\file ipa_data_serializer.h\n> + * \\brief IPA Data Serializer\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(IPADataSerializer)\n> +\n> +/**\n> + * \\class IPADataSerializer\n> + * \\brief IPA Data Serializer\n> + *\n> + * Static template class that provides functions for serializing and\n> + * deserializing IPA data.\n> + *\n> + * \\todo Switch to Span instead of byte and fd vector\n> + *\n> + * \\todo Harden the vector and map deserializer\n> + *\n> + * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> + * index into the fd array. This will allow us to use views instead of copying.\n> + */\n> +\n> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> + * \\brief Append POD to end of byte vector, in little-endian order\n> + * \\tparam T Type of POD to append\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value to append\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tconst std::vector<int32_t> &fds,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] fds Fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the iterator version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tstd::vector<int32_t>::const_iterator fdsBegin,\n> + * \tstd::vector<int32_t>::const_iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 387d5d88..1291f1b3 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -25,6 +25,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 1B74EC0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 27 Dec 2020 12:10:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 90D6861FFA;\n\tSun, 27 Dec 2020 13:10:13 +0100 (CET)","from mail-lf1-x144.google.com (mail-lf1-x144.google.com\n\t[IPv6:2a00:1450:4864:20::144])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 14724615B1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 27 Dec 2020 13:10:12 +0100 (CET)","by mail-lf1-x144.google.com with SMTP id y19so18128629lfa.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 27 Dec 2020 04:10:12 -0800 (PST)","from localhost ([185.224.57.161]) by smtp.gmail.com with ESMTPSA id\n\tb9sm5748537lji.68.2020.12.27.04.10.09\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSun, 27 Dec 2020 04:10:10 -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=\"m4Z1sWmq\"; 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=2rm3eQJp56HKJzsjdc/+Dt8c/B8j9nDIrE1LLIfmnEY=;\n\tb=m4Z1sWmq5w1HY8nAM0L/tv2jj4EoNY8x+e4f0I4qS8PJwaBs4MhzaCUp/mSiuaHeVe\n\tJutQLCyO5liTZ59nPHtvlpdKcEtZ0BcwkCYIxlFi8uZqyvOU50hSUWka0nK9b4QZ1aoD\n\tge9v+gPsJz6hizgesFOg6LdCvLpdbObcn/1TePVT7AMJvZ01Ttl4bHh4vkpWobAh/0Jj\n\ts0u7q3dlsOt9nnOIaGdYWuMqxdLt8ipqkAAJxg+cdX7cgbUFNP8AV7+JQFqg/Ae/RDr4\n\tEw/NXmK3SagOD1tHlpvUyi953QW6dTbAZRPIXbfSPAED2PNKxslqlDozR99ey94HTGH3\n\t0Mtg==","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=2rm3eQJp56HKJzsjdc/+Dt8c/B8j9nDIrE1LLIfmnEY=;\n\tb=q0HBc2gMD5RUn4ASUxQoxh4rTiGFD3tqBuNVqQlQYKcukbec9mfVopfGU5XkMLr+XU\n\tBYyDnbU4FYOAwm96UozMDlSD9nX+lgCAwb7P3m9kmIdMvyvzEEo2QgwREtCN3TusgnyV\n\tAEM2UuXZj7ZIVNvdtEqt5vyCCMu4Cp8JRW1NaxsoZhQYAQZjP2a/jbhcIsMuWhm30efO\n\tfi9wKyDkt8Tq3+rmulXvIUeGZWECz3PriqBPRdpu06Soma2FrZdBne36+++p4Ww7BV8n\n\t+dknLXYzz6+gAoFG/P8SpFXuMeawnAu9ujjfVeQTKDhYchivKUVIoRMMLDer9YM4A6FP\n\thVtA==","X-Gm-Message-State":"AOAM531fMX8CzFCbrESKGFVzuWRZTm0dweCmhyjcuBW+g/Ku5uK/16Nz\n\tBqWSELK4O+E0GpJO5958WrMuV9KPT0cTSg==","X-Google-Smtp-Source":"ABdhPJzT3MrYRJ0iJ3X477/p1mLTiSLHnb1xO4xKxs57cAyhO8a+VgLRq00P7T2owj9s91wKbh+/vQ==","X-Received":"by 2002:a19:81d5:: with SMTP id\n\tc204mr18976070lfd.366.1609071011301; \n\tSun, 27 Dec 2020 04:10:11 -0800 (PST)","Date":"Sun, 27 Dec 2020 13:10:07 +0100","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<X+h5n2byjlnDffUt@wyvern>","References":"<20201224081534.41601-1-paul.elder@ideasonboard.com>\n\t<20201224081534.41601-7-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201224081534.41601-7-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v6 6/9] 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":14894,"web_url":"https://patchwork.libcamera.org/comment/14894/","msgid":"<YBiom1ZIM1P+LyZ+@pendragon.ideasonboard.com>","date":"2021-02-02T01:19:23","subject":"Re: [libcamera-devel] [PATCH v6 6/9] libcamera: Add\n\tIPADataSerializer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Thu, Dec 24, 2020 at 05:15:31PM +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> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> \n> ---\n> Changes in v6:\n> - remove (de)serializers for IPASettings, CameraSensorInfo, IPAStream,\n>   and IPABuffer as they are now defined in core.mojom and generated\n> - ControlList (de)serializer no longer needs a ControlInfoMap\n> - ControlList (de)serializer now checks the ControlSerializer if the\n>   ControlList's ControlInfoMap is cached\n> - add some todos for optimization and hardening\n> \n> Changes in v5:\n> - fix style of: {{}, {}} -> { {}, {} }\n> - add documentation on serialization formats (not in doxygen, though)\n> - compress readPOD\n>   - use memcpy\n>   - use ASSERT\n> - remove ifdef DOXYGEN guard from base IPADataSerializer\n> - remove integer overflow risk when calculating vector offsets\n> - use const iterators and const references\n> - remove const ControlList and const ControlInfoMap specializations\n> \n> Changes in v4:\n> - rename readUInt/appendUInt to readPOD/appendPOD\n>   - put them in anonymous namespace\n>   - and add length check\n>     - fatal if failure, because it means not enough data to deserialize,\n>       which is technically a segfault\n>   - use the new readPOD/appendPOD with length protections\n>   - expand on their docs correspondingly\n> - change snake_case to camelCase\n> - add optimizations to the hand-written de/serializers\n> - reserve vector length where trivially possible\n> - remove unnecessary IPADataSerializer<type>:: explicit calls (if\n>   they're calling a specialization from the same specialization)\n> \n> Changes in v3:\n> - reimplement append/readUInt with memcpy (intead of bit shifting)\n> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER\n>   - use this for int64_t, bool, float, and double\n> - fix comment style\n> \n> Changes in v2:\n> - added serializers for all integer types, bool, and string\n> - use string serializer for IPASettings serializer\n> - add documentation\n> - add serializer for const ControlList\n> ---\n>  .../libcamera/internal/ipa_data_serializer.h  | 701 ++++++++++++++++++\n>  src/libcamera/ipa_data_serializer.cpp         | 185 +++++\n>  src/libcamera/meson.build                     |   1 +\n>  3 files changed, 887 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..5468a0b7\n> --- /dev/null\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -0,0 +1,701 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.h - Image Processing Algorithm data serializer\n> + */\n> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n> +\n> +#include <deque>\n> +#include <iostream>\n> +#include <string.h>\n> +#include <tuple>\n> +#include <type_traits>\n> +#include <vector>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/geometry.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +\n> +#include \"libcamera/internal/byte_stream_buffer.h\"\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/control_serializer.h\"\n> +#include \"libcamera/internal/log.h\"\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(IPADataSerializer)\n> +\n> +namespace {\n> +\n> +template<typename T>\n> +void appendPOD(std::vector<uint8_t> &vec,\n> +\t       typename std::enable_if<std::is_arithmetic_v<T>, T>::type val)\n\nHow about\n\ntemplate<typename T,\n\t typename std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\nvoid appendPOD(std::vector<uint8_t> &vec, T val)\n\nThe effect should be the same, but the signature of the function is\neasier to read.\n\n> +{\n> +\tsize_t byteWidth = sizeof(val);\n\n\tconstexpr size_t byteWidth = sizeof(val);\n\n> +\tvec.resize(vec.size() + byteWidth);\n> +\tmemcpy(&*(vec.end() - byteWidth), &val, byteWidth);\n> +}\n> +\n> +template<typename T>\n> +typename std::enable_if<std::is_arithmetic_v<T>, T>::type\n> +readPOD(std::vector<uint8_t>::const_iterator it, size_t pos,\n> +\tstd::vector<uint8_t>::const_iterator end)\n\nSame here,\n\ntemplate<typename T,\n\t typename std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\nT readPOD(std::vector<uint8_t>::const_iterator it, size_t pos,\n\t  std::vector<uint8_t>::const_iterator end)\n\nand for the next function too.\n\n> +{\n> +\tASSERT(pos + it < end);\n> +\n> +\tT ret = 0;\n> +\tmemcpy(&ret, &(*(it + pos)), sizeof(ret));\n> +\n> +\treturn ret;\n> +}\n> +\n> +template<typename T>\n> +typename std::enable_if<std::is_arithmetic_v<T>, T>::type\n> +readPOD(std::vector<uint8_t> &vec, size_t pos)\n> +{\n> +\treturn readPOD<T>(vec.cbegin(), pos, vec.end());\n> +}\n> +\n> +} /* namespace */\n> +\n> +template<typename T>\n> +class IPADataSerializer\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const T &data, ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     ControlSerializer *cs);\n> +\n> +\tstatic T deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t     const std::vector<int32_t> &fds,\n> +\t\t\t     ControlSerializer *cs);\n> +\tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t     std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t     ControlSerializer *cs);\n> +};\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +/*\n> + * Serialization format for vector of type V:\n> + *\n> + * 4 bytes - uint32_t Length of vector, in number of elements\n> + *\n> + * For every element in the vector:\n> + *\n> + * 4 bytes - uint32_t Size of element, in bytes\n> + * 4 bytes - uint32_t Size of fds for the element, in number of fds\n\n * 4 bytes - uint32_t Number of fds for the element\n\n> + * X bytes - Serialized element\n> + */\n> +template<typename V>\n> +class IPADataSerializer<std::vector<V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t vecLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, vecLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tstd::vector<V> ret(vecLen);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tret[i] = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t   dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t   fdIter,\n> +\t\t\t\t\t\t\t\t   fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t   cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +/*\n> + * Serialization format for map of key type K and value type V:\n> + *\n> + * 4 bytes - uint32_t Length of map, in number of pairs\n> + *\n> + * For every pair in the map:\n> + *\n> + * 4 bytes - uint32_t Size of key, in bytes\n> + * 4 bytes - uint32_t Size of fds for the key, in number of fds\n\nSame as above.\n\n> + * X bytes - Serialized key\n> + * 4 bytes - uint32_t Size of value, in bytes\n> + * 4 bytes - uint32_t Size of fds for the value, in number of fds\n\nAnd here too.\n\n> + * X bytes - Serialized value\n> + */\n> +template<typename K, typename V>\n> +class IPADataSerializer<std::map<K, V>>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\t/* Serialize the length. */\n> +\t\tuint32_t mapLen = data.size();\n> +\t\tappendPOD<uint32_t>(dataVec, mapLen);\n> +\n> +\t\t/* Serialize the members. */\n> +\t\tfor (auto const &it : data) {\n> +\t\t\tstd::vector<uint8_t> dvec;\n> +\t\t\tstd::vector<int32_t> fvec;\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\n> +\t\t\tstd::tie(dvec, fvec) =\n> +\t\t\t\tIPADataSerializer<V>::serialize(it.second, cs);\n> +\n> +\t\t\tappendPOD<uint32_t>(dataVec, dvec.size());\n> +\t\t\tappendPOD<uint32_t>(dataVec, fvec.size());\n> +\n> +\t\t\tdataVec.insert(dataVec.end(), dvec.begin(), dvec.end());\n> +\t\t\tfdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());\n> +\t\t}\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<int32_t> fds;\n> +\t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::map<K, V> ret;\n> +\n> +\t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> +\t\tstd::vector<int32_t>::const_iterator fdIter = fdsBegin;\n> +\t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> +\t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tK key = IPADataSerializer<K>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t  cs);\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t\tsizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> +\t\t\tsizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> +\t\t\tdataIter += 8;\n> +\n> +\t\t\tconst V value = IPADataSerializer<V>::deserialize(dataIter,\n> +\t\t\t\t\t\t\t\t\t  dataIter + sizeofData,\n> +\t\t\t\t\t\t\t\t\t  fdIter,\n> +\t\t\t\t\t\t\t\t\t  fdIter + sizeofFds,\n> +\t\t\t\t\t\t\t\t\t  cs);\n> +\t\t\tret.insert({key, value});\n> +\n> +\t\t\tdataIter += sizeofData;\n> +\t\t\tfdIter += sizeofFds;\n> +\t\t}\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#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.cbegin(), 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>::const_iterator dataBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::const_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.cbegin(), 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>::const_iterator dataBegin,\\\n> +\t\t\t\tstd::vector<uint8_t>::const_iterator dataEnd,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\\\n> +\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\\\n> +\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n> +\t{\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.cbegin(), string.end()}.\n> + * The size of the string is recorded by the container (struct, vector, map, or\n> + * function parameter serdes).\n> + */\n> +template<>\n> +class IPADataSerializer<std::string>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { { data.cbegin(), data.end() }, {} };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { data.cbegin(), data.cend() };\n> +\t}\n> +\n> +\tstatic std::string deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn { dataBegin, dataEnd };\n> +\t}\n> +};\n> +\n> +/*\n> + * FileDescriptors are serialized into a single byte that tells if the\n> + * FileDescriptor is valid or not. If it is valid, then for serialization\n> + * the fd will be written to the fd vector, or for deserialization the\n> + * fd vector const_iterator will be valid.\n> + *\n> + * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n> + * also allows us to simply send the entire fd vector into the deserializer\n> + * and it will be recursively consumed as necessary.\n> + */\n> +template<>\n> +class IPADataSerializer<FileDescriptor>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec = { data.isValid() };\n> +\t\tstd::vector<int32_t> fdVec;\n> +\t\tif (data.isValid())\n> +\t\t\tfdVec.push_back(data.fd());\n> +\n> +\t\treturn { dataVec, fdVec };\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n> +\t}\n> +\n> +\tstatic FileDescriptor deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tASSERT(std::distance(dataBegin, dataEnd) >= 1);\n> +\n> +\t\tbool valid = !!(*dataBegin);\n> +\n> +\t\tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n> +\n> +\t\treturn valid ? FileDescriptor(*fdsBegin) : FileDescriptor();\n> +\t}\n> +};\n> +\n> +/*\n> + * ControlList is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * 4 bytes - uint32_t Size of serialized ControlList, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + * X bytes - Serialized ControlList (using ControlSerializer)\n> + *\n> + * If data.infoMap() is nullptr, then the default controls::controls will\n> + * be used. The serialized ControlInfoMap will have zero length.\n> + */\n> +template<>\n> +class IPADataSerializer<ControlList>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlList &data, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlList\";\n> +\n> +\t\tsize_t size;\n> +\t\tstd::vector<uint8_t> infoData;\n> +\t\tint ret;\n> +\n\nThis opportunistic serialization of the control info map still makes me\nworry about the implementation being fragile, but maybe I just worry too\nmuch. A todo comment to mention this should be revisited could be nice,\nbut I'm not sure what a good rationale would be (\"This worries Laurent\"\nisn't a good rationale :-)).\n\n> +\t\tif (data.infoMap() && !cs->isCached(data.infoMap())) {\n> +\t\t\tsize = cs->binarySize(*data.infoMap());\n> +\t\t\tinfoData.resize(size);\n> +\t\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\t\tret = cs->serialize(*data.infoMap(), buffer);\n> +\n> +\t\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList's ControlInfoMap\";\n> +\t\t\t\treturn { {}, {} };\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tsize = cs->binarySize(data);\n> +\t\tstd::vector<uint8_t> listData(size);\n> +\t\tByteStreamBuffer buffer(listData.data(), listData.size());\n> +\t\tret = cs->serialize(data, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tdataVec.reserve(8 + infoData.size() + listData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tappendPOD<uint32_t>(dataVec, listData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\t\tdataVec.insert(dataVec.end(), listData.begin(), listData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n> +\n> +\t\tif (!std::distance(dataBegin, dataEnd))\n\n\t\tif (std::distance(dataBegin, dataEnd) < 8)\n\n> +\t\t\treturn {};\n> +\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\t\tuint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator it = dataBegin + 8;\n\n\t\tif (infoDataSize + listDataSize < infoDataSize ||\n\t\t    std::distance(it, dataEnd) < infoDataSize + listDataSize)\n\t\t\treturn {};\n\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\t\tstd::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);\n> +\n> +\t\tif (infoDataSize > 0) {\n> +\t\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n\nDo you need the const cast ? Passing a non-const pointer to a function\nexpecting a const shouldn't be an issue.\n\n\t\t\tByteStreamBuffer buffer(&*it, infoDataSize);\n\nand you can drop infoData.\n\n\n> +\t\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\t\t\t/* It's fine if map is empty. */\n> +\t\t\tif (buffer.overflow()) {\n> +\t\t\t\tLOG(IPADataSerializer, Error)\n> +\t\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n> +\t\t\t\treturn ControlList();\n> +\t\t\t}\n> +\t\t}\n> +\n> +\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(listData.data()), listData.size());\n\nSame here,\n\n\t\tit += infoDataSize;\n\t\tByteStreamBuffer buffer(&*it, listDataSize);\n\nand you can drop listData.\n\n> +\t\tControlList list = cs->deserialize<ControlList>(buffer);\n> +\t\tif (buffer.overflow())\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: buffer overflow\";\n> +\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 ControlList deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlList deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t       ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * const ControlInfoMap is serialized as:\n> + *\n> + * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes\n> + * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> + */\n> +template<>\n> +class IPADataSerializer<ControlInfoMap>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const ControlInfoMap &map, ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlInfoMap\";\n> +\n> +\t\tsize_t size = cs->binarySize(map);\n> +\t\tstd::vector<uint8_t> infoData(size);\n> +\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n> +\t\tint ret = cs->serialize(map, buffer);\n> +\n> +\t\tif (ret < 0 || buffer.overflow()) {\n> +\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlInfoMap\";\n> +\t\t\treturn { {}, {} };\n> +\t\t}\n> +\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tappendPOD<uint32_t>(dataVec, infoData.size());\n> +\t\tdataVec.insert(dataVec.end(), infoData.begin(), infoData.end());\n> +\n> +\t\treturn { dataVec, {} };\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\tif (!cs)\n> +\t\t\tLOG(IPADataSerializer, Fatal)\n> +\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlInfoMap\";\n> +\n\nMissing overflows check.\n\n> +\t\tuint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> +\n> +\t\tstd::vector<uint8_t>::const_iterator it = dataBegin + 4;\n> +\n> +\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n> +\n> +\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n\nSame comments as above.\n\n> +\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n> +\n> +\t\treturn map;\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t> &fds,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), cs);\n> +\t}\n> +\n> +\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t  ControlSerializer *cs)\n> +\t{\n> +\t\treturn deserialize(dataBegin, dataEnd, cs);\n> +\t}\n> +};\n> +\n> +/*\n> + * FrameBuffer::Plane is serialized as:\n> + *\n> + * 1 byte  - FileDescriptor\n> + * 4 bytes - uint32_t Length\n> + */\n> +template<>\n> +class IPADataSerializer<FrameBuffer::Plane>\n> +{\n> +public:\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n> +\tserialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tstd::vector<uint8_t> dataVec;\n> +\t\tstd::vector<int32_t> fdsVec;\n> +\n> +\t\tstd::vector<uint8_t> fdBuf;\n> +\t\tstd::vector<int32_t> fdFds;\n> +\t\tstd::tie(fdBuf, fdFds) =\n> +\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> +\t\tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> +\n> +\t\tappendPOD<uint32_t>(dataVec, data.length);\n> +\n> +\t\treturn { dataVec, fdsVec };\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n> +\t\t\t\t\t      std::vector<int32_t> &fds,\n> +\t\t\t\t\t      ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> +\t}\n> +\n> +\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n\ndataEnd is used.\n\n> +\t\t\t\t\t      std::vector<int32_t>::const_iterator fdsBegin,\n> +\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\n> +\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n> +\t{\n> +\t\tFrameBuffer::Plane ret;\n> +\n> +\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,\n> +\t\t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> +\t\tret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);\n> +\n> +\t\treturn ret;\n> +\t}\n> +};\n> +\n> +#endif /* __DOXYGEN__ */\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */\n> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> new file mode 100644\n> index 00000000..34287d6a\n> --- /dev/null\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -0,0 +1,185 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2020, Google Inc.\n> + *\n> + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer\n> + */\n> +\n> +#include \"libcamera/internal/ipa_data_serializer.h\"\n> +\n> +#include \"libcamera/internal/log.h\"\n> +\n> +/**\n> + * \\file ipa_data_serializer.h\n> + * \\brief IPA Data Serializer\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(IPADataSerializer)\n> +\n> +/**\n> + * \\class IPADataSerializer\n> + * \\brief IPA Data Serializer\n> + *\n> + * Static template class that provides functions for serializing and\n> + * deserializing IPA data.\n> + *\n> + * \\todo Switch to Span instead of byte and fd vector\n> + *\n> + * \\todo Harden the vector and map deserializer\n> + *\n> + * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> + * index into the fd array. This will allow us to use views instead of copying.\n> + */\n> +\n> +namespace {\n> +\n> +/**\n> + * \\fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)\n> + * \\brief Append POD to end of byte vector, in little-endian order\n> + * \\tparam T Type of POD to append\n> + * \\param[in] vec Byte vector to append to\n> + * \\param[in] val Value to append\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,\n> + * \t\t\t\t      std::vector<uint8_t>::iterator end)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] it Iterator of byte vector to read from\n> + * \\param[in] pos Index in byte vector to read from\n> + * \\param[in] end Iterator marking end of byte vector\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past \\a end, it is\n> + * a fata error will occur, as it means there is insufficient data for\n> + * deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a it at index \\a pos\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)\n> + * \\brief Read POD from byte vector, in little-endian order\n> + * \\tparam T Type of POD to read\n> + * \\param[in] vec Byte vector to read from\n> + * \\param[in] pos Index in vec to start reading from\n> + *\n> + * This function is meant to be used by the IPA data serializer, and the\n> + * generated IPA proxies.\n> + *\n> + * If the \\a pos plus the byte-width of the desired POD is past the end of\n> + * \\a vec, a fatal error will occur, as it means there is insufficient data\n> + * for deserialization, which should never happen.\n> + *\n> + * \\return The POD read from \\a vec at index \\a pos\n> + */\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::serialize(\n> + * \tT data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Serialize an object into byte vector and fd vector\n> + * \\tparam T Type of object to serialize\n> + * \\param[in] data Object to serialize\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return Tuple of byte vector and fd vector, that is the serialized form\n> + * of \\a data\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() can be used if the object type \\a T and its\n> + * members don't have any FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> + * \tconst std::vector<uint8_t> &data,\n> + * \tconst std::vector<int32_t> &fds,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] data Byte vector to deserialize from\n> + * \\param[in] fds Fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the iterator version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> IPADataSerializer::deserialize(\n> + * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> + * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> + * \tstd::vector<int32_t>::const_iterator fdsBegin,\n> + * \tstd::vector<int32_t>::const_iterator fdsEnd,\n> + * \tControlSerializer *cs = nullptr)\n> + * \\brief Deserialize byte vector and fd vector into an object\n> + * \\tparam T Type of object to deserialize to\n> + * \\param[in] dataBegin Begin iterator of byte vector to deserialize from\n> + * \\param[in] dataEnd End iterator of byte vector to deserialize from\n> + * \\param[in] fdsBegin Begin iterator of fd vector to deserialize from\n> + * \\param[in] fdsEnd End iterator of fd vector to deserialize from\n> + * \\param[in] cs ControlSerializer\n> + *\n> + * This version of deserialize() (or the vector version) must be used if\n> + * the object type \\a T or its members contain FileDescriptor.\n> + *\n> + * \\a cs is only necessary if the object type \\a T or its members contain\n> + * ControlList or ControlInfoMap.\n> + *\n> + * \\return The deserialized object\n> + */\n\nHow about squashing this patch in ?\n\ndiff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\nindex bde5eaf7e42c..7f29205235e8 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(const T &data, ControlSerializer *cs);\n+\tserialize(const T &data, ControlSerializer *cs = nullptr);\n\n \tstatic T deserialize(const std::vector<uint8_t> &data,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n\n \tstatic T deserialize(const std::vector<uint8_t> &data,\n \t\t\t     const std::vector<int32_t> &fds,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n \t\t\t     std::vector<int32_t>::const_iterator fdsBegin,\n \t\t\t     std::vector<int32_t>::const_iterator fdsEnd,\n-\t\t\t     ControlSerializer *cs);\n+\t\t\t     ControlSerializer *cs = nullptr);\n };\n\n #ifndef __DOXYGEN__\n@@ -296,64 +296,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.cbegin(), 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>::const_iterator dataBegin,\\\n-\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::const_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.cbegin(), 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>::const_iterator dataBegin,\\\n-\t\t\t\tstd::vector<uint8_t>::const_iterator dataEnd,\\\n-\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\\\n-\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\\\n-\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n-\t{\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.cbegin(), 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 34287d6a2601..6d98a3acfbc6 100644\n--- a/src/libcamera/ipa_data_serializer.cpp\n+++ b/src/libcamera/ipa_data_serializer.cpp\n@@ -182,4 +182,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\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.cbegin(), 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>::const_iterator dataBegin,\\\n+\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::const_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.cbegin(), 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>::const_iterator dataBegin,\\\n+\t\t\t\tstd::vector<uint8_t>::const_iterator dataEnd,\\\n+\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsBegin,\\\n+\t\t\t\t[[maybe_unused]] std::vector<int32_t>::const_iterator fdsEnd,\\\n+\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\\\n+\t{\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 } /* namespace libcamera */\n\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 387d5d88..1291f1b3 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -25,6 +25,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 733D5C33BB\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  2 Feb 2021 01:19:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 02E2F6841C;\n\tTue,  2 Feb 2021 02:19:47 +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 88CBF60106\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Feb 2021 02:19:45 +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 C4689556;\n\tTue,  2 Feb 2021 02:19:44 +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=\"NcLZ5ff0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1612228785;\n\tbh=eBq6lmMjt2u0J2voaK/HysEJWOQZvbYjGAZw54utaGc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=NcLZ5ff0pxwy+aBo4HMvUYldPq9tm66ZdTUX45O20z4tD2qYJwQ6cBVLPH7iqB0tV\n\tQrwY++qiWHEUQlzpfbnL3eWKBNSFmAEhvkDTDhTEirXHmiyiTBvtdR05orjjDae7Cj\n\tuEu6Beo7u/pIlvBvwQfdLGL/7JoZjAQqlslP9r9M=","Date":"Tue, 2 Feb 2021 03:19:23 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<YBiom1ZIM1P+LyZ+@pendragon.ideasonboard.com>","References":"<20201224081534.41601-1-paul.elder@ideasonboard.com>\n\t<20201224081534.41601-7-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20201224081534.41601-7-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v6 6/9] 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>"}}]