{"id":9615,"url":"https://patchwork.libcamera.org/api/patches/9615/?format=json","web_url":"https://patchwork.libcamera.org/patch/9615/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200915142038.28757-7-paul.elder@ideasonboard.com>","date":"2020-09-15T14:20:21","name":"[libcamera-devel,06/23] libcamera: Add IPADataSerializer","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"00686c8b7d9db2fae2eaa536cec3579ff7cab069","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9615/mbox/","series":[{"id":1291,"url":"https://patchwork.libcamera.org/api/series/1291/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1291","date":"2020-09-15T14:20:16","name":"IPA isolation implementation","version":1,"mbox":"https://patchwork.libcamera.org/series/1291/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9615/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9615/checks/","tags":{},"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 CB64EBF01C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 15 Sep 2020 14:21:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 96B1862E30;\n\tTue, 15 Sep 2020 16:21:09 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7A35762E13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 15 Sep 2020 16:21:07 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BB62A276;\n\tTue, 15 Sep 2020 16:21:05 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Q5Ies8Mf\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1600179667;\n\tbh=Df0owFvela1+ZFtd72Fed89eR96xckPniG6lY6aY7N0=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=Q5Ies8MfYFdHeHHdCHRGQolvqj3sw++DYlDhC6e3vT7bwphRlTANHPW5puboyOsSE\n\tdd9R8T3vWDWx8w62owW+eWFQI9MFP9C3s4CBu5ftCyrwL07CGCJz0B/GRMsbaqLzNQ\n\tr7v2zLvv+UDkprIbdFnQvgNO9jAgN1cZgztkfl44=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Tue, 15 Sep 2020 23:20:21 +0900","Message-Id":"<20200915142038.28757-7-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.27.0","In-Reply-To":"<20200915142038.28757-1-paul.elder@ideasonboard.com>","References":"<20200915142038.28757-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Subject":"[libcamera-devel] [PATCH 06/23] libcamera: Add IPADataSerializer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","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>"},"content":"Add an IPADataSerializer which implments de/serialization of built-in\nand libcamera data structures. This is intended to be used by the proxy\nand the proxy worker in the IPC layer.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n .../libcamera/internal/ipa_data_serializer.h  | 876 ++++++++++++++++++\n src/libcamera/ipa_data_serializer.cpp         |  29 +\n src/libcamera/meson.build                     |   1 +\n 3 files changed, 906 insertions(+)\n create mode 100644 include/libcamera/internal/ipa_data_serializer.h\n create mode 100644 src/libcamera/ipa_data_serializer.cpp","diff":"diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\nnew file mode 100644\nindex 00000000..6765e4e2\n--- /dev/null\n+++ b/include/libcamera/internal/ipa_data_serializer.h\n@@ -0,0 +1,876 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * ipa_data_serializer.h - Image Processing Algorithm data serializer\n+ */\n+#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n+#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__\n+\n+#include <deque>\n+#include <iostream>\n+#include <tuple>\n+#include <vector>\n+\n+#include <libcamera/buffer.h>\n+#include <libcamera/control_ids.h>\n+#include <libcamera/geometry.h>\n+#include <libcamera/ipa/ipa_interface.h>\n+\n+#include \"libcamera/internal/byte_stream_buffer.h\"\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/control_serializer.h\"\n+#include \"libcamera/internal/log.h\"\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(IPADataSerializer)\n+\n+template<typename T>\n+void appendUInt(std::vector<uint8_t> &vec, T val)\n+{\n+\tsize_t byteWidth = sizeof(val);\n+\tfor (size_t i = 0; i < byteWidth; i++)\n+\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n+}\n+\n+template<typename T>\n+T readUInt(std::vector<uint8_t> &vec, size_t pos)\n+{\n+\tT ret = 0;\n+\tsize_t byteWidth = sizeof(ret);\n+\tif (pos + byteWidth > vec.size())\n+\t\treturn ret;\n+\n+\tfor (size_t i = 0; i < byteWidth; i++)\n+\t\tret |= vec[pos + i] << 8*i;\n+\treturn ret;\n+}\n+\n+template<typename T>\n+T readUInt(std::vector<uint8_t>::iterator it)\n+{\n+\tT ret = 0;\n+\tsize_t byteWidth = sizeof(ret);\n+\tfor (size_t i = 0; i < byteWidth; i++)\n+\t\tret |= *(it + i) << 8*i;\n+\treturn ret;\n+}\n+\n+template<typename T>\n+class IPADataSerializer\n+{\n+};\n+\n+template<typename V>\n+class IPADataSerializer<std::vector<V>>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tstd::vector<int32_t> fds_vec;\n+\n+\t\t// serialize the length\n+\t\tuint32_t vec_len = data.size();\n+\t\tappendUInt<uint32_t>(data_vec, vec_len);\n+\n+\t\t// serialize the members\n+\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\n+\t\t\tstd::vector<uint8_t> dvec;\n+\t\t\tstd::vector<int32_t> fvec;\n+\n+\t\t\tstd::tie(dvec, fvec) =\n+\t\t\t\tIPADataSerializer<V>::serialize(*it, cs);\n+\n+\t\t\tappendUInt<uint32_t>(data_vec, dvec.size());\n+\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n+\n+\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n+\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n+\t\t}\n+\n+\t\treturn {data_vec, fds_vec};\n+\t}\n+\n+\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<int32_t> fds;\n+\t\treturn IPADataSerializer<std::vector<V>>::deserialize(it1, it2,\n+\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n+\t\t\t\t\t\t\t\t      cs);\n+\t}\n+\n+\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),\n+\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n+\t\t\t\t\t\t\t\t      cs);\n+\t}\n+\n+\tstatic std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\tuint32_t vec_len = readUInt<uint32_t>(data_it1);\n+\t\tstd::vector<V> ret(vec_len);\n+\n+\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n+\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n+\t\tfor (uint32_t i = 0; i < vec_len; i++) {\n+\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n+\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n+\n+\t\t\tret[i] = IPADataSerializer<V>::deserialize(data_it + 8,\n+\t\t\t\t\t\t\t\t   data_it + 8 + sizeof_data,\n+\t\t\t\t\t\t\t\t   fd_it,\n+\t\t\t\t\t\t\t\t   fd_it + sizeof_fds,\n+\t\t\t\t\t\t\t\t   cs);\n+\n+\t\t\tdata_it += 8 + sizeof_data;\n+\t\t\tfd_it += sizeof_fds;\n+\t\t}\n+\n+\t\treturn ret;\n+\t}\n+};\n+\n+template<typename K, typename V>\n+class IPADataSerializer<std::map<K, V>>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tstd::vector<int32_t> fds_vec;\n+\n+\t\t// serialize the length\n+\t\tuint32_t map_len = data.size();\n+\t\tappendUInt<uint32_t>(data_vec, map_len);\n+\n+\t\t// serialize the members\n+\t\tfor (auto it = data.begin(); it != data.end(); ++it) {\n+\t\t\tstd::vector<uint8_t> dvec;\n+\t\t\tstd::vector<int32_t> fvec;\n+\n+\t\t\tstd::tie(dvec, fvec) =\n+\t\t\t\tIPADataSerializer<K>::serialize(it->first, cs);\n+\n+\t\t\tappendUInt<uint32_t>(data_vec, dvec.size());\n+\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n+\n+\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n+\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n+\n+\t\t\tstd::tie(dvec, fvec) =\n+\t\t\t\tIPADataSerializer<V>::serialize(it->second, cs);\n+\n+\t\t\tappendUInt<uint32_t>(data_vec, dvec.size());\n+\t\t\tappendUInt<uint32_t>(data_vec, fvec.size());\n+\n+\t\t\tdata_vec.insert(data_vec.end(), dvec.begin(), dvec.end());\n+\t\t\tfds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());\n+\t\t}\n+\n+\t\treturn {data_vec, fds_vec};\n+\t}\n+\n+\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t  std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<int32_t> fds;\n+\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,\n+\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n+\t\t\t\t\t\t\t\t      cs);\n+\t}\n+\n+\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),\n+\t\t\t\t\t\t\t\t      fds.begin(), fds.end(),\n+\t\t\t\t\t\t\t\t      cs);\n+\t}\n+\n+\tstatic std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t  ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::map<K, V> ret;\n+\n+\t\tuint32_t map_len = readUInt<uint32_t>(data_it1);\n+\n+\t\tstd::vector<uint8_t>::iterator data_it = data_it1 + 4;\n+\t\tstd::vector<int32_t>::iterator fd_it = fds_it1;\n+\t\tfor (uint32_t i = 0; i < map_len; i++) {\n+\t\t\tuint32_t sizeof_data = readUInt<uint32_t>(data_it);\n+\t\t\tuint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);\n+\n+\t\t\tK key = IPADataSerializer<K>::deserialize(data_it + 8,\n+\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n+\t\t\t\t\t\t\t\t  fd_it,\n+\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n+\t\t\t\t\t\t\t\t  cs);\n+\n+\t\t\tdata_it += 8 + sizeof_data;\n+\t\t\tfd_it += sizeof_fds;\n+\t\t\tsizeof_data = readUInt<uint32_t>(data_it);\n+\t\t\tsizeof_fds  = readUInt<uint32_t>(data_it + 4);\n+\n+\t\t\tconst V value = IPADataSerializer<V>::deserialize(data_it + 8,\n+\t\t\t\t\t\t\t\t\t  data_it + 8 + sizeof_data,\n+\t\t\t\t\t\t\t\t\t  fd_it,\n+\t\t\t\t\t\t\t\t\t  fd_it + sizeof_fds,\n+\t\t\t\t\t\t\t\t\t  cs);\n+\t\t\tret.insert({key, value});\n+\n+\t\t\tdata_it += 8 + sizeof_data;\n+\t\t\tfd_it += sizeof_fds;\n+\t\t}\n+\n+\t\treturn ret;\n+\t}\n+};\n+\n+// TODO implement this for all primitives\n+template<>\n+class IPADataSerializer<uint32_t>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const uint32_t data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tappendUInt<uint32_t>(data_vec, data);\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic uint32_t deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<uint32_t>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic uint32_t deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn readUInt<uint32_t>(it1);\n+\t}\n+\n+\tstatic uint32_t deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<uint32_t>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic uint32_t deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\tstd::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<uint32_t>::deserialize(data_it1, data_it2);\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> data_vec = { data.isValid() };\n+\t\tstd::vector<int32_t> fd_vec;\n+\t\tif (data.isValid())\n+\t\t\tfd_vec.push_back(data.fd());\n+\n+\t\treturn {data_vec, fd_vec};\n+\t}\n+\n+\tstatic FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,\n+\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),\n+\t\t\t\t\t\t\t\t      fds.begin(), fds.end());\n+\t}\n+\n+\tstatic FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t  std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t  std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tif (std::distance(data_it1, data_it2) < 1)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"Invalid data to deserialize FileDescriptor\";\n+\n+\t\tbool valid = *data_it1;\n+\n+\t\tif (valid && std::distance(fds_it1, fds_it2) < 1)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"Invalid fds to deserialize FileDescriptor\";\n+\n+\t\treturn valid ? FileDescriptor(*fds_it1) : FileDescriptor();\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<IPASettings>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec(data.configurationFile.begin(),\n+\t\t\t\t\t      data.configurationFile.end());\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic IPASettings deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t       std::vector<uint8_t>::iterator it2,\n+\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::string str(it1, it2);\n+\n+\t\tIPASettings ret;\n+\t\tret.configurationFile = str;\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic IPASettings deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t       [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<CameraSensorInfo>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\n+\t\tuint32_t str_len = data.model.size();\n+\t\tappendUInt<uint32_t>(data_vec, str_len);\n+\n+\t\tdata_vec.insert(data_vec.end(), data.model.begin(), data.model.end());\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.bitsPerPixel);\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.width);\n+\t\tappendUInt<uint32_t>(data_vec, data.activeAreaSize.height);\n+\n+\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));\n+\t\tappendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));\n+\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.width);\n+\t\tappendUInt<uint32_t>(data_vec, data.analogCrop.height);\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.outputSize.width);\n+\t\tappendUInt<uint32_t>(data_vec, data.outputSize.height);\n+\n+\t\tappendUInt<uint64_t>(data_vec, data.pixelRate);\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.lineLength);\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic CameraSensorInfo deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tCameraSensorInfo ret;\n+\n+\t\tuint32_t str_len = readUInt<uint32_t>(it1);\n+\t\tstd::string str(it1 + 4, it1 + 4 + str_len);\n+\t\tret.model = str;\n+\n+\t\tstd::vector<uint8_t>::iterator it = it1 + 4 + str_len;\n+\n+\t\tret.bitsPerPixel = readUInt<uint32_t>(it);\n+\n+\t\tret.activeAreaSize.width = readUInt<uint32_t>(it + 4);\n+\t\tret.activeAreaSize.height = readUInt<uint32_t>(it + 8);\n+\n+\t\tret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));\n+\t\tret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));\n+\t\tret.analogCrop.width = readUInt<uint32_t>(it + 20);\n+\t\tret.analogCrop.height = readUInt<uint32_t>(it + 24);\n+\n+\t\tret.outputSize.width = readUInt<uint32_t>(it + 28);\n+\t\tret.outputSize.height = readUInt<uint32_t>(it + 32);\n+\n+\t\tret.pixelRate = readUInt<uint64_t>(it + 36);\n+\n+\t\tret.lineLength = readUInt<uint64_t>(it + 44);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic CameraSensorInfo deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t    std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<IPAStream>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.pixelFormat);\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.size.width);\n+\t\tappendUInt<uint32_t>(data_vec, data.size.height);\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic IPAStream deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t     [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tIPAStream ret;\n+\n+\t\tret.pixelFormat = readUInt<uint32_t>(it1);\n+\n+\t\tret.size.width = readUInt<uint32_t>(it1 + 4);\n+\t\tret.size.height = readUInt<uint32_t>(it1 + 8);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic IPAStream deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t     [[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t     [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<ControlList>\n+{\n+public:\n+\t// map arg will be generated, since it's per-pipeline anyway\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const ControlList &data, const ControlInfoMap &map,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tif (!cs)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlList\";\n+\n+\t\tsize_t size = cs->binarySize(map);\n+\t\tstd::vector<uint8_t> infoData(size);\n+\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n+\t\tint ret = cs->serialize(map, buffer);\n+\n+\t\tif (ret < 0 || buffer.overflow()) {\n+\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList's ControlInfoMap\";\n+\t\t\treturn {{}, {}};\n+\t\t}\n+\n+\t\tsize = cs->binarySize(data);\n+\t\tstd::vector<uint8_t> listData(size);\n+\t\tbuffer = ByteStreamBuffer(listData.data(), listData.size());\n+\t\tret = cs->serialize(data, buffer);\n+\n+\t\tif (ret < 0 || buffer.overflow()) {\n+\t\t\tLOG(IPADataSerializer, Error) << \"Failed to serialize ControlList\";\n+\t\t\treturn {{}, {}};\n+\t\t}\n+\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n+\t\tappendUInt<uint32_t>(data_vec, listData.size());\n+\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n+\t\tdata_vec.insert(data_vec.end(), listData.begin(), listData.end());\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<ControlList>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic ControlList deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t       [[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t       ControlSerializer *cs)\n+\t{\n+\t\tif (!cs)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlList\";\n+\n+\t\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n+\t\tuint32_t listDataSize = readUInt<uint32_t>(it1 + 4);\n+\n+\t\tstd::vector<uint8_t>::iterator it = it1 + 8;\n+\n+\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n+\t\tstd::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);\n+\n+\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n+\t\tControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n+\t\t/* It's fine if map is empty. */\n+\t\tif (buffer.overflow()) {\n+\t\t\tLOG(IPADataSerializer, Error)\n+\t\t\t\t<< \"Failed to deserialize ControlLists's ControlInfoMap: buffer overflow\";\n+\t\t\treturn ControlList();\n+\t\t}\n+\n+\t\tbuffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());\n+\t\tControlList list = cs->deserialize<ControlList>(buffer);\n+\t\tif (buffer.overflow())\n+\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: buffer overflow\";\n+\t\tif (list.empty())\n+\t\t\tLOG(IPADataSerializer, Error) << \"Failed to deserialize ControlList: empty list\";\n+\n+\t\treturn list;\n+\t}\n+\n+\tstatic ControlList deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t       ControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<ControlList>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic ControlList deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t       std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t       ControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<ControlList>::deserialize(data_it1, data_it2, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<const ControlInfoMap>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const ControlInfoMap &map, ControlSerializer *cs)\n+\t{\n+\t\tif (!cs)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"ControlSerializer not provided for serialization of ControlInfoMap\";\n+\n+\t\tsize_t size = cs->binarySize(map);\n+\t\tstd::vector<uint8_t> infoData(size);\n+\t\tByteStreamBuffer buffer(infoData.data(), infoData.size());\n+\t\tint ret = cs->serialize(map, buffer);\n+\n+\t\tif (ret < 0 || buffer.overflow())\n+\t\t\treturn {{}, {}};\n+\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tappendUInt<uint32_t>(data_vec, infoData.size());\n+\t\tdata_vec.insert(data_vec.end(), infoData.begin(), infoData.end());\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\tControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t\tControlSerializer *cs)\n+\t{\n+\t\tif (!cs)\n+\t\t\tLOG(IPADataSerializer, Fatal)\n+\t\t\t\t<< \"ControlSerializer not provided for deserialization of ControlInfoMap\";\n+\n+\t\tuint32_t infoDataSize = readUInt<uint32_t>(it1);\n+\n+\t\tstd::vector<uint8_t>::iterator it = it1 + 4;\n+\n+\t\tstd::vector<uint8_t> infoData(it, it + infoDataSize);\n+\n+\t\tByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());\n+\t\tconst ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);\n+\n+\t\t/*\n+\t\tControlInfoMap::Map ctrls;\n+\t\tfor (auto pair : map)\n+\t\t\tctrls.emplace(controls::controls.at(pair.first->id()),\n+\t\t\t\t      pair.second);\n+\n+\t\tControlInfoMap ret = std::move(ctrls);\n+\t\treturn ret;\n+\t\t*/\n+\n+\t\treturn map;\n+\t}\n+\n+\tstatic const ControlInfoMap deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t\t\tControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);\n+\t}\n+\n+\tstatic const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t  ControlSerializer *cs)\n+\t{\n+\t\treturn IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<ControlInfoMap>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize([[maybe_unused]] const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs)\n+\t{\n+\t\tconst ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);\n+\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tstd::tie(data_vec, std::ignore) =\n+\t\t\tIPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t  ControlSerializer *cs)\n+\t{\n+\t\tControlInfoMap ret;\n+\t\tconst ControlInfoMap &map = ret;\n+\t\tconst_cast<ControlInfoMap &>(map) =\n+\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t\t[[maybe_unused]] std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t\tControlSerializer *cs)\n+\t{\n+\t\tControlInfoMap ret;\n+\t\tconst ControlInfoMap &map = ret;\n+\t\tconst_cast<ControlInfoMap &>(map) =\n+\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic ControlInfoMap deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds,\n+\t\t\t\t\t\tControlSerializer *cs)\n+\t{\n+\t\tControlInfoMap ret;\n+\t\tconst ControlInfoMap &map = ret;\n+\t\tconst_cast<ControlInfoMap &>(map) =\n+\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t  std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t  ControlSerializer *cs)\n+\t{\n+\t\tControlInfoMap ret;\n+\t\tconst ControlInfoMap &map = ret;\n+\t\tconst_cast<ControlInfoMap &>(map) =\n+\t\t\tIPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,\n+\t\t\t\t\t\t\t\t\t     fds_it1, fds_it2, cs);\n+\n+\t\treturn ret;\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<FrameBuffer::Plane>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tstd::vector<int32_t> fds_vec;\n+\n+\t\t// fd\n+\t\tstd::vector<uint8_t> fdBuf;\n+\t\tstd::vector<int32_t> fdFds;\n+\t\tstd::tie(fdBuf, fdFds) =\n+\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n+\t\tdata_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());\n+\t\tfds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());\n+\n+\t\t// length\n+\t\tappendUInt<uint32_t>(data_vec, data.length);\n+\n+\t\treturn {data_vec, fds_vec};\n+\t}\n+\n+\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t      std::vector<int32_t> &fds,\n+\t\t\t\t\t      ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),\n+\t\t\t\t\t\t\t\t\t  fds.begin(), fds.end(),\n+\t\t\t\t\t\t\t\t\t  cs);\n+\t}\n+\n+\tstatic FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t\t      std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t\t      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tFrameBuffer::Plane ret;\n+\n+\t\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,\n+\t\t\t\t\t\t\t\t\tfds_it1, fds_it1 + 1);\n+\t\tret.length = readUInt<uint32_t>(data_it1 + 1);\n+\n+\t\treturn ret;\n+\t}\n+};\n+\n+\n+template<>\n+class IPADataSerializer<IPABuffer>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const IPABuffer &data, ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\n+\t\tappendUInt<uint32_t>(data_vec, data.id);\n+\n+\t\tstd::vector<uint8_t> planes_data_vec;\n+\t\tstd::vector<int32_t> planes_fds_vec;\n+\t\tstd::tie(planes_data_vec, planes_fds_vec) =\n+\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);\n+\n+\t\tdata_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());\n+\n+\t\treturn {data_vec, planes_fds_vec};\n+\t}\n+\n+\tstatic IPABuffer deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t     std::vector<int32_t> &fds,\n+\t\t\t\t     ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),\n+\t\t\t\t\t\t\t\t fds.begin(), fds.end(), cs);\n+\t}\n+\n+\tstatic IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,\n+\t\t\t\t     std::vector<uint8_t>::iterator data_it2,\n+\t\t\t\t     std::vector<int32_t>::iterator fds_it1,\n+\t\t\t\t     std::vector<int32_t>::iterator fds_it2,\n+\t\t\t\t     ControlSerializer *cs = nullptr)\n+\t{\n+\t\tIPABuffer ret;\n+\n+\t\tret.id = readUInt<uint32_t>(data_it1);\n+\n+\t\tret.planes =\n+\t\t\tIPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(\n+\t\t\t\tdata_it1 + 4, data_it2, fds_it1, fds_it2, cs);\n+\n+\t\treturn ret;\n+\t}\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */\ndiff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\nnew file mode 100644\nindex 00000000..86332abc\n--- /dev/null\n+++ b/src/libcamera/ipa_data_serializer.cpp\n@@ -0,0 +1,29 @@\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_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+ */\n+\n+// TODO the rest of the documentation\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex af2f3d95..91710520 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -23,6 +23,7 @@ libcamera_sources = files([\n     'geometry.cpp',\n     'ipa_context_wrapper.cpp',\n     'ipa_controls.cpp',\n+    'ipa_data_serializer.cpp',\n     'ipa_interface.cpp',\n     'ipa_manager.cpp',\n     'ipa_module.cpp',\n","prefixes":["libcamera-devel","06/23"]}