Show a patch.

GET /api/patches/9388/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 9388,
    "url": "https://patchwork.libcamera.org/api/patches/9388/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/9388/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20200826110926.67192-5-paul.elder@ideasonboard.com>",
    "date": "2020-08-26T11:09:13",
    "name": "[libcamera-devel,RFC,04/17] IPA: IPC: add IPADataSerializer",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "debded207f08b6591594f45df2c106b0b531de8d",
    "submitter": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/people/17/?format=api",
        "name": "Paul Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/9388/mbox/",
    "series": [
        {
            "id": 1243,
            "url": "https://patchwork.libcamera.org/api/series/1243/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1243",
            "date": "2020-08-26T11:09:09",
            "name": "[libcamera-devel,RFC,01/17] IPA: IPC: raspberrypi: Add data definition and generated header",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/1243/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/9388/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/9388/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 AC099BD87E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 26 Aug 2020 11:09:59 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 76FEA628F9;\n\tWed, 26 Aug 2020 13:09:59 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8DD2C6037B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 26 Aug 2020 13:09:58 +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 2000B53C;\n\tWed, 26 Aug 2020 13:09:55 +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=\"DuvBpjE1\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1598440198;\n\tbh=3I8Uvtq2XBRUqzTBotDWXcSdqJubJfUIb63/4k3Coo4=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=DuvBpjE1JlFfuHAAHw0ayeUuqsw1p8u1VkAuBnlnLkfZ7olTJ6go49DR3GAyC0U3C\n\tlOrOUF/e/wIOdJ+W86Pol9VylPs+dRUmgCEzmO8RZDxTkj39p2lBtGWdZ1mm82oEWz\n\tsd+ZFMQ8Hngn6ddQF+pSIMDdIQBwe5udEGp4Gv7U=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 26 Aug 2020 20:09:13 +0900",
        "Message-Id": "<20200826110926.67192-5-paul.elder@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.27.0",
        "In-Reply-To": "<20200826110926.67192-1-paul.elder@ideasonboard.com>",
        "References": "<20200826110926.67192-1-paul.elder@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [RFC PATCH 04/17] IPA: IPC: 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": "This patch adds IPADataSerializer. It is a template class that\nimplements de/serialization. ipa_data_serializer.h implements\nde/serialization for libcamera classes and primitives. This is\nhandwritten, and if there is any demand for any more stuctures to be\nadded, they must be added by hand.\n\nThere was some debate as to whether this should use some well-known wire\nformat, such as mojo or flatbuffers. As for a pre-made de/serializer,\nsuch as flatbuffers that I used in the last RFC, we noticed that I had\nto wrap the serialization anyway, so I decided I might as well just do\nthe whole thing and throw flatbuffers out the window. To my knowledge,\nthere is no serdes framework that can actually use generate serdes code\nfor a pre-defined class, except mojo, which we can't use :) So since I\nhad to implement manual serialization anyway, I could've followed a\npre-existing standard. I didn't, but this can be changed. To get this\nRFC out I just did a simple wire format. I also think that the only\nmerit to following a standard format is for performance, since we're\ngoing to be wrapping both sides of the IPC pipe in IPAProxy and\nIPAProxyWorker, so it's not like the world is going to see the wire\nformat.\n\nraspberrypi_serializer.h will be generated based on the mojo file seen\nin the last patch. Other than that, it is conceptually the same as\nipa_data_serializer.h. This header will be included by the generated IPA\nproxy and proxy worker so that they can de/serialize the data when then\ncall into IPC.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n .../libcamera/internal/ipa_data_serializer.h  | 816 ++++++++++++++++++\n .../libcamera/ipa/raspberrypi_serializer.h    | 487 +++++++++++\n src/libcamera/ipa_data_serializer.cpp         |  29 +\n 3 files changed, 1332 insertions(+)\n create mode 100644 include/libcamera/internal/ipa_data_serializer.h\n create mode 100644 include/libcamera/ipa/raspberrypi_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..368a398b\n--- /dev/null\n+++ b/include/libcamera/internal/ipa_data_serializer.h\n@@ -0,0 +1,816 @@\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+#include <iomanip>\n+\n+template<typename T> std::ostream &operator<<(std::ostream &stream, const std::vector<T> &vec)\n+{\n+\tstream << \"{ \";\n+\tfor (const T &v : vec)\n+\t\tstream << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(v) << \", \";\n+\n+\tstream << \" }\";\n+\treturn stream;\n+}\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(IPADataSerializer)\n+\n+static void appendUint32(std::vector<uint8_t> &vec, uint32_t val)\n+{\n+\tfor (int i = 0; i < 4; i++)\n+\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n+}\n+\n+static void appendUint64(std::vector<uint8_t> &vec, uint64_t val)\n+{\n+\tfor (int i = 0; i < 8; i++)\n+\t\tvec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));\n+}\n+\n+static uint32_t extractUint32(std::vector<uint8_t>::iterator it)\n+{\n+\tuint32_t ret = 0;\n+\tfor (int i = 0; i < 4; i++)\n+\t\tret |= *(it + i) << 8*i;\n+\treturn ret;\n+}\n+\n+static uint64_t extractUint64(std::vector<uint8_t>::iterator it)\n+{\n+\tuint32_t ret = 0;\n+\tfor (int i = 0; i < 8; 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\tappendUint32(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\tappendUint32(data_vec, dvec.size());\n+\t\t\tappendUint32(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 = extractUint32(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 = extractUint32(data_it);\n+\t\t\tuint32_t sizeof_fds  = extractUint32(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\tappendUint32(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\tappendUint32(data_vec, dvec.size());\n+\t\t\tappendUint32(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\tappendUint32(data_vec, dvec.size());\n+\t\t\tappendUint32(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 = extractUint32(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 = extractUint32(data_it);\n+\t\t\tuint32_t sizeof_fds  = extractUint32(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 = extractUint32(data_it);\n+\t\t\tsizeof_fds  = extractUint32(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<unsigned int>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const unsigned int data, [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tappendUint32(data_vec, data);\n+\n+\t\treturn {data_vec, {}};\n+\t}\n+\n+\tstatic unsigned int deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\treturn IPADataSerializer<unsigned int>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic unsigned int 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 extractUint32(it1);\n+\t}\n+\n+\tstatic unsigned int 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<unsigned int>::deserialize(data.begin(), data.end());\n+\t}\n+\n+\tstatic unsigned int 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<unsigned int>::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\tappendUint32(data_vec, str_len);\n+\n+\t\tdata_vec.insert(data_vec.end(), data.model.begin(), data.model.end());\n+\n+\t\tappendUint32(data_vec, data.bitsPerPixel);\n+\n+\t\tappendUint32(data_vec, data.activeAreaSize.width);\n+\t\tappendUint32(data_vec, data.activeAreaSize.height);\n+\n+\t\tappendUint32(data_vec, static_cast<uint32_t>(data.analogCrop.x));\n+\t\tappendUint32(data_vec, static_cast<uint32_t>(data.analogCrop.y));\n+\t\tappendUint32(data_vec, data.analogCrop.width);\n+\t\tappendUint32(data_vec, data.analogCrop.height);\n+\n+\t\tappendUint32(data_vec, data.outputSize.width);\n+\t\tappendUint32(data_vec, data.outputSize.height);\n+\n+\t\tappendUint64(data_vec, data.pixelRate);\n+\n+\t\tappendUint32(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 = extractUint32(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 = extractUint32(it);\n+\n+\t\tret.activeAreaSize.width = extractUint32(it + 4);\n+\t\tret.activeAreaSize.height = extractUint32(it + 8);\n+\n+\t\tret.analogCrop.x = static_cast<int32_t>(extractUint32(it + 12));\n+\t\tret.analogCrop.y = static_cast<int32_t>(extractUint32(it + 16));\n+\t\tret.analogCrop.width = extractUint32(it + 20);\n+\t\tret.analogCrop.height = extractUint32(it + 24);\n+\n+\t\tret.outputSize.width = extractUint32(it + 28);\n+\t\tret.outputSize.height = extractUint32(it + 32);\n+\n+\t\tret.pixelRate = extractUint64(it + 36);\n+\n+\t\tret.lineLength = extractUint64(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\tappendUint32(data_vec, data.pixelFormat);\n+\n+\t\tappendUint32(data_vec, data.size.width);\n+\t\tappendUint32(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 = extractUint32(it1);\n+\n+\t\tret.size.width = extractUint32(it1 + 4);\n+\t\tret.size.height = extractUint32(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\tstd::cerr << \"Failed to serialize ControlList's ControlInfoMap\" << std::endl;\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\tstd::cerr << \"Failed to serialize ControlList\" << std::endl;\n+\t\t\treturn {{}, {}};\n+\t\t}\n+\n+\t\tstd::vector<uint8_t> data_vec;\n+\t\tappendUint32(data_vec, infoData.size());\n+\t\tappendUint32(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 = extractUint32(it1);\n+\t\tuint32_t listDataSize = extractUint32(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\tif (map.empty() || buffer.overflow()) {\n+\t\t\tstd::cerr << \"Failed to deserialize ControlLists's ControlInfoMap\" << std::endl;\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\tstd::cerr << \"Failed to deserialize ControlList: buffer overflow\" << std::endl;\n+\t\tif (list.empty())\n+\t\t\tstd::cerr << \"Failed to deserialize ControlList: empty list\" << std::endl;\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\tappendUint32(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 = extractUint32(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<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\tappendUint32(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 = extractUint32(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\tappendUint32(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 = extractUint32(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/include/libcamera/ipa/raspberrypi_serializer.h b/include/libcamera/ipa/raspberrypi_serializer.h\nnew file mode 100644\nindex 00000000..01d69986\n--- /dev/null\n+++ b/include/libcamera/ipa/raspberrypi_serializer.h\n@@ -0,0 +1,487 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * raspberrypi_serializer.h - Image Processing Algorithm data serializer for raspberry pi\n+ */\n+\n+// automatically generated by custom compiler\n+\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+\n+#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_H__\n+#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_H__\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(IPADataSerializer)\n+\n+template<>\n+class IPADataSerializer<RPiStaggeredWritePayload>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiStaggeredWritePayload data,\n+\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\n+\t\t// scalar gainDelay_\n+\t\tappendUint32(ret_data, data.gainDelay_);\n+\n+\t\t// scalar exposureDelay_\n+\t\tappendUint32(ret_data, data.exposureDelay_);\n+\n+\t\t// scalar sensorMetadata_\n+\t\tappendUint32(ret_data, data.sensorMetadata_);\n+\n+\t\treturn {ret_data, {}};\n+\t}\n+\n+\tstatic RPiStaggeredWritePayload deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tRPiStaggeredWritePayload ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\n+\t\t// scalar gainDelay_\n+\t\tret.gainDelay_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// scalar exposureDelay_\n+\t\tret.exposureDelay_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// scalar sensorMetadata_\n+\t\tret.sensorMetadata_ = extractUint32(m);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiStaggeredWritePayload deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t\t    std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t\t    ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data(it1, it2);\n+\t\treturn IPADataSerializer<RPiStaggeredWritePayload>::deserialize(data, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<RPiIspPreparePayload>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiIspPreparePayload data,\n+\t\t  [[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\n+\t\t// scalar embeddedbufferId_\n+\t\tappendUint32(ret_data, data.embeddedbufferId_);\n+\n+\t\t// scalar bayerbufferId_\n+\t\tappendUint32(ret_data, data.bayerbufferId_);\n+\n+\t\treturn {ret_data, {}};\n+\t}\n+\n+\tstatic RPiIspPreparePayload deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\t[[maybe_unused]] ControlSerializer *cs = nullptr)\n+\t{\n+\t\tRPiIspPreparePayload ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\n+\t\t// scalar embeddedbufferId_\n+\t\tret.embeddedbufferId_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// scalar bayerbufferId_\n+\t\tret.bayerbufferId_ = extractUint32(m);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiIspPreparePayload deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t\tstd::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t\tControlSerializer *cs = nullptr)\n+\t{\n+\t\tstd::vector<uint8_t> data(it1, it2);\n+\t\treturn IPADataSerializer<RPiIspPreparePayload>::deserialize(data, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<RPiStatsCompletePayload>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiStatsCompletePayload data,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\n+\t\t// scalar bufferId_\n+\t\tappendUint32(ret_data, data.bufferId_);\n+\n+\t\t// ControlList controls_\n+\t\tif (data.controls_.size() > 0) {\n+\t\t\tstd::vector<uint8_t> controls;\n+\t\t\tstd::tie(controls, std::ignore) =\n+\t\t\t\tIPADataSerializer<ControlList>::serialize(data.controls_,\n+\t\t\t\t\t\tdata.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,\n+\t\t\t\t\t\tcs);\n+\t\t\tappendUint32(ret_data, controls.size());\n+\t\t\tret_data.insert(ret_data.end(), controls.begin(), controls.end());\n+\t\t} else {\n+\t\t\tappendUint32(ret_data, 0);\n+\t\t}\n+\n+\n+\t\treturn {ret_data, {}};\n+\t}\n+\n+\tstatic RPiStatsCompletePayload deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t\t   ControlSerializer *cs)\n+\t{\n+\t\tRPiStatsCompletePayload ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\n+\t\t// scalar bufferId_\n+\t\tret.bufferId_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// ControlList controls_\n+\t\tsize_t controlsSize = extractUint32(m);\n+\t\tif (controlsSize > 0)\n+\t\t\tret.controls_ =\n+\t\t\t\tIPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiStatsCompletePayload deserialize(std::vector<uint8_t>::iterator it1,\n+\t\t\t\t\t\t   std::vector<uint8_t>::iterator it2,\n+\t\t\t\t\t\t   ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> data(it1, it2);\n+\t\treturn IPADataSerializer<RPiStatsCompletePayload>::deserialize(data, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<RPiConfigurePayload>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiConfigurePayload data,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\t\tstd::vector<int32_t> ret_fds;\n+\n+\t\t// scalar op_\n+\t\tappendUint32(ret_data, data.op_);\n+\n+\t\t// fd lsTableHandle_\n+\t\tstd::vector<uint8_t> lsTableHandle;\n+\t\tstd::vector<int32_t> lsTableHandleFds;\n+\t\tstd::tie(lsTableHandle, lsTableHandleFds) =\n+\t\t\tIPADataSerializer<FileDescriptor>::serialize(data.lsTableHandle_);\n+\t\tret_data.insert(ret_data.end(), lsTableHandle.begin(), lsTableHandle.end());\n+\t\tret_fds.insert(ret_fds.end(), lsTableHandleFds.begin(), lsTableHandleFds.end());\n+\n+\t\t// scalar lsTableHandleStatic_\n+\t\tappendUint32(ret_data, static_cast<uint32_t>(data.lsTableHandleStatic_));\n+\n+\t\t// struct staggeredWriteResult_\n+\t\tstd::vector<uint8_t> staggeredWriteResult;\n+\t\tstd::tie(staggeredWriteResult, std::ignore) =\n+\t\t\tIPADataSerializer<RPiStaggeredWritePayload>::serialize(data.staggeredWriteResult_, cs);\n+\t\tappendUint32(ret_data, staggeredWriteResult.size());\n+\t\tret_data.insert(ret_data.end(), staggeredWriteResult.begin(), staggeredWriteResult.end());\n+\n+\t\t// ControlList controls_\n+\t\tif (data.controls_.size() > 0) {\n+\t\t\tstd::vector<uint8_t> controls;\n+\t\t\tstd::tie(controls, std::ignore) =\n+\t\t\t\tIPADataSerializer<ControlList>::serialize(data.controls_,\n+\t\t\t\t\tdata.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,\n+\t\t\t\t\tcs);\n+\t\t\tappendUint32(ret_data, controls.size());\n+\t\t\tret_data.insert(ret_data.end(), controls.begin(), controls.end());\n+\t\t} else {\n+\t\t\tappendUint32(ret_data, 0);\n+\t\t}\n+\n+\t\t// scalar bufferFd_\n+\t\tappendUint32(ret_data, static_cast<uint32_t>(data.bufferFd_));\n+\n+\t\treturn {ret_data, ret_fds};\n+\t}\n+\n+\tstatic RPiConfigurePayload 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)\n+\t{\n+\t\tRPiConfigurePayload ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\t\tstd::vector<int32_t>::iterator n = fds.begin();\n+\n+\t\t// scalar op_\n+\t\tret.op_ = static_cast<RPiConfigParameters>(extractUint32(m));\n+\t\tm += 4;\n+\n+\t\t// fd lsTableHandle_\n+\t\tret.lsTableHandle_ = IPADataSerializer<FileDescriptor>::deserialize(m, m + 1, n, n + 1);\n+\t\tm += 1;\n+\t\tn += ret.lsTableHandle_.isValid() ? 1 : 0;\n+\n+\t\t// scalar lsTableHandleStatic_\n+\t\tret.lsTableHandleStatic_ = static_cast<int32_t>(extractUint32(m));\n+\t\tm += 4;\n+\n+\t\t// struct staggeredWriteResult_\n+\t\tsize_t staggeredWriteResultSize = extractUint32(m);\n+\t\tret.staggeredWriteResult_ =\n+\t\t\tIPADataSerializer<RPiStaggeredWritePayload>::deserialize(m + 4, m + 4 + staggeredWriteResultSize, cs);\n+\t\tm += 4 + staggeredWriteResultSize;\n+\n+\t\t// ControlList controls_\n+\t\tsize_t controlsSize = extractUint32(m);\n+\t\tif (controlsSize > 0)\n+\t\t\tret.controls_ =\n+\t\t\t\tIPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);\n+\t\tm += 4 + controlsSize;\n+\n+\t\t// scalar bufferFd_\n+\t\tret.bufferFd_ = static_cast<int32_t>(extractUint32(m));\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiConfigurePayload 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       ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> data(data_it1, data_it2);\n+\t\tstd::vector<int32_t> fds(fds_it1, fds_it2);\n+\t\treturn IPADataSerializer<RPiConfigurePayload>::deserialize(data, fds, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<RPiConfigureParams>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiConfigureParams data,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\t\tstd::vector<int32_t> ret_fds;\n+\n+\t\t// vector payload_\n+\t\t// only member, so don't need size\n+\t\tstd::tie(ret_data, ret_fds) =\n+\t\t\tIPADataSerializer<std::vector<RPiConfigurePayload>>::serialize(data.payload_, cs);\n+\n+\t\treturn {ret_data, ret_fds};\n+\t}\n+\n+\tstatic RPiConfigureParams 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)\n+\t{\n+\t\tRPiConfigureParams ret;\n+\n+\t\t// vector payload_\n+\t\tret.payload_ =\n+\t\t\tIPADataSerializer<std::vector<RPiConfigurePayload>>::deserialize(data, fds, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiConfigureParams 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      ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> data(data_it1, data_it2);\n+\t\tstd::vector<int32_t> fds(fds_it1, fds_it2);\n+\t\treturn IPADataSerializer<RPiConfigureParams>::deserialize(data, fds, cs);\n+\t}\n+};\n+\n+template<>\n+class IPADataSerializer<RPiEventParams>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiEventParams data,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\n+\t\t// scalar ev_\n+\t\tappendUint32(ret_data, data.ev_);\n+\n+\t\t// scalar bufferId_\n+\t\tappendUint32(ret_data, data.bufferId_);\n+\n+\t\t// struct ispPrepare_\n+\t\tstd::vector<uint8_t> ispPrepare;\n+\t\tstd::tie(ispPrepare, std::ignore) =\n+\t\t\tIPADataSerializer<RPiIspPreparePayload>::serialize(data.ispPrepare_, cs);\n+\t\tappendUint32(ret_data, ispPrepare.size());\n+\t\tret_data.insert(ret_data.end(), ispPrepare.begin(), ispPrepare.end());\n+\n+\t\t// ControlList controls_\n+\t\tif (data.controls_.size() > 0) {\n+\t\t\tstd::vector<uint8_t> controls;\n+\t\t\tstd::tie(controls, std::ignore) =\n+\t\t\t\tIPADataSerializer<ControlList>::serialize(data.controls_,\n+\t\t\t\t\t\tdata.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,\n+\t\t\t\t\t\tcs);\n+\t\t\tappendUint32(ret_data, controls.size());\n+\t\t\tret_data.insert(ret_data.end(), controls.begin(), controls.end());\n+\t\t} else {\n+\t\t\tappendUint32(ret_data, 0);\n+\t\t}\n+\n+\t\t// scalar bufferFd_\n+\t\tappendUint32(ret_data, static_cast<uint32_t>(data.bufferFd_));\n+\n+\t\treturn {ret_data, {}};\n+\t}\n+\n+\tstatic RPiEventParams deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t  ControlSerializer *cs)\n+\t{\n+\t\tRPiEventParams ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\n+\t\t// scalar ev_\n+\t\tret.ev_ = static_cast<RPiEvents>(extractUint32(m));\n+\t\tm += 4;\n+\n+\t\t// scalar bufferId_\n+\t\tret.bufferId_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// struct ispPrepare_\n+\t\tsize_t ispPrepareSize = extractUint32(m);\n+\t\tret.ispPrepare_ =\n+\t\t\tIPADataSerializer<RPiIspPreparePayload>::deserialize(m + 4, m + 4 + ispPrepareSize, cs);\n+\t\tm += 4 + ispPrepareSize;\n+\n+\t\t// ControlList controls_\n+\t\tsize_t controlsSize = extractUint32(m);\n+\t\tif (controlsSize > 0)\n+\t\t\tret.controls_ =\n+\t\t\t\tIPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);\n+\t\tm += 4 + controlsSize;\n+\n+\t\t// scalar bufferFd_\n+\t\tret.bufferFd_ = static_cast<int32_t>(extractUint32(m));\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiEventParams 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)\n+\t{\n+\t\tstd::vector<uint8_t> data(it1, it2);\n+\t\treturn IPADataSerializer<RPiEventParams>::deserialize(data, cs);\n+\t}\n+\n+};\n+\n+template<>\n+class IPADataSerializer<RPiActionParams>\n+{\n+public:\n+\tstatic std::tuple<std::vector<uint8_t>, std::vector<int32_t>>\n+\tserialize(const RPiActionParams data,\n+\t\t  ControlSerializer *cs)\n+\t{\n+\t\tstd::vector<uint8_t> ret_data;\n+\n+\t\t// scalar op_\n+\t\tappendUint32(ret_data, data.op_);\n+\n+\t\t// scalar bufferId_\n+\t\tappendUint32(ret_data, data.bufferId_);\n+\n+\t\t// struct statsComplete_\n+\t\tstd::vector<uint8_t> statsComplete;\n+\t\tstd::tie(statsComplete, std::ignore) =\n+\t\t\tIPADataSerializer<RPiStatsCompletePayload>::serialize(data.statsComplete_, cs);\n+\t\tappendUint32(ret_data, statsComplete.size());\n+\t\tret_data.insert(ret_data.end(), statsComplete.begin(), statsComplete.end());\n+\n+\t\t// ControlList controls_\n+\t\tif (data.controls_.size() > 0) {\n+\t\t\tstd::vector<uint8_t> controls;\n+\t\t\tstd::tie(controls, std::ignore) =\n+\t\t\t\tIPADataSerializer<ControlList>::serialize(data.controls_,\n+\t\t\t\t\t\tdata.controls_.infoMap() ? *data.controls_.infoMap() : RPiControls,\n+\t\t\t\t\t\tcs);\n+\t\t\tappendUint32(ret_data, controls.size());\n+\t\t\tret_data.insert(ret_data.end(), controls.begin(), controls.end());\n+\t\t} else {\n+\t\t\tappendUint32(ret_data, 0);\n+\t\t}\n+\n+\t\treturn {ret_data, {}};\n+\t}\n+\n+\tstatic RPiActionParams deserialize(std::vector<uint8_t> &data,\n+\t\t\t\t\t   ControlSerializer *cs)\n+\t{\n+\t\tRPiActionParams ret;\n+\t\tstd::vector<uint8_t>::iterator m = data.begin();\n+\n+\t\t// scalar op_\n+\t\tret.op_ = static_cast<RPiActions>(extractUint32(m));\n+\t\tm += 4;\n+\n+\t\t// scalar bufferId_\n+\t\tret.bufferId_ = extractUint32(m);\n+\t\tm += 4;\n+\n+\t\t// struct statsComplete_\n+\t\tsize_t statsCompleteSize = extractUint32(m);\n+\t\tret.statsComplete_ =\n+\t\t\tIPADataSerializer<RPiStatsCompletePayload>::deserialize(m + 4, m + 4 + statsCompleteSize, cs);\n+\t\tm += 4 + statsCompleteSize;\n+\n+\t\t// ControlList controls_\n+\t\tsize_t controlsSize = extractUint32(m);\n+\t\tif (controlsSize > 0)\n+\t\t\tret.controls_ =\n+\t\t\t\tIPADataSerializer<ControlList>::deserialize(m + 4, m + 4 + controlsSize, cs);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tstatic RPiActionParams 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)\n+\t{\n+\t\tstd::vector<uint8_t> data(it1, it2);\n+\t\treturn IPADataSerializer<RPiActionParams>::deserialize(data, cs);\n+\t}\n+\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_RASPBERRYPI_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 */\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "04/17"
    ]
}