summaryrefslogtreecommitdiff
path: root/utils/codegen/ipc/generators/libcamera_templates
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2024-08-08 18:13:00 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2024-08-15 23:59:08 +0300
commit50c92cc7e2924009ecab3e4004448b01d687707c (patch)
treec22b49816a3c79dae4727780962aa0928df42b52 /utils/codegen/ipc/generators/libcamera_templates
parentd3bf27180ef1d91b86b7b87a2378e559eaff5455 (diff)
meson: Move all code generation scripts to utils/codegen/
We have multiple code generation scripts in utils/, mixed with other miscellaneous utilities, as well as a larger code base based on mojom in utils/ipc/. To make code sharing easier between the generator scripts, without creating a mess in the utils/ directory, move all the code generation code to utils/codegen/. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Diffstat (limited to 'utils/codegen/ipc/generators/libcamera_templates')
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/core_ipa_interface.h.tmpl37
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl44
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/definition_functions.tmpl56
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/meson.build14
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl84
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl255
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl132
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl246
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl45
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/proxy_functions.tmpl202
-rw-r--r--utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl319
11 files changed, 1434 insertions, 0 deletions
diff --git a/utils/codegen/ipc/generators/libcamera_templates/core_ipa_interface.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/core_ipa_interface.h.tmpl
new file mode 100644
index 00000000..7f2d0810
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/core_ipa_interface.h.tmpl
@@ -0,0 +1,37 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "definition_functions.tmpl" as funcs -%}
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * libcamera core definitions for Image Processing Algorithms
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#pragma once
+
+{% if has_map %}#include <map>{% endif %}
+{% if has_array %}#include <vector>{% endif %}
+
+#include <libcamera/ipa/ipa_interface.h>
+
+namespace libcamera {
+
+{# \todo Use const char * instead of std::string for strings #}
+{% for const in consts %}
+static const {{const.kind|name}} {{const.mojom_name}} = {{const.value}};
+{% endfor %}
+
+{% for enum in enums_gen_header %}
+{{funcs.define_enum(enum)}}
+{% endfor %}
+
+{%- for struct in structs_gen_header %}
+{{funcs.define_struct(struct)}}
+{% endfor %}
+
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl
new file mode 100644
index 00000000..036518f6
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/core_ipa_serializer.h.tmpl
@@ -0,0 +1,44 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "serializer.tmpl" as serializer -%}
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Data serializer for core libcamera definitions for IPA
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <vector>
+
+#include <libcamera/ipa/core_ipa_interface.h>
+
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPADataSerializer)
+{% for struct in structs_gen_serializer %}
+template<>
+class IPADataSerializer<{{struct|name}}>
+{
+public:
+{{- serializer.serializer(struct, "")}}
+{%- if struct|has_fd %}
+{{serializer.deserializer_fd(struct, "")}}
+{%- else %}
+{{serializer.deserializer_no_fd(struct, "")}}
+{{serializer.deserializer_fd_simple(struct, "")}}
+{%- endif %}
+};
+{% endfor %}
+
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/definition_functions.tmpl b/utils/codegen/ipc/generators/libcamera_templates/definition_functions.tmpl
new file mode 100644
index 00000000..8b8509f3
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/definition_functions.tmpl
@@ -0,0 +1,56 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+
+{#
+ # \brief Generate enum definition
+ #
+ # \param enum Enum object whose definition is to be generated
+ #}
+{%- macro define_enum(enum) -%}
+enum{{" class" if enum|is_scoped}} {{enum.mojom_name}} {
+{%- for field in enum.fields %}
+ {{field.mojom_name}} = {{field.numeric_value}},
+{%- endfor %}
+};
+{%- endmacro -%}
+
+{#
+ # \brief Generate struct definition
+ #
+ # \param struct Struct object whose definition is to be generated
+ #}
+{%- macro define_struct(struct) -%}
+struct {{struct.mojom_name}}
+{
+public:
+#ifndef __DOXYGEN__
+ {{struct.mojom_name}}() {%- if struct|has_default_fields %}
+ :{% endif %}
+{%- for field in struct.fields|with_default_values -%}
+{{" " if loop.first}}{{field.mojom_name}}({{field|default_value}}){{", " if not loop.last}}
+{%- endfor %}
+ {
+ }
+
+ {{struct.mojom_name}}(
+{%- for field in struct.fields -%}
+{{"const " if not field|is_pod}}{{field|name}} {{"&" if not field|is_pod}}_{{field.mojom_name}}{{", " if not loop.last}}
+{%- endfor -%}
+)
+ :
+{%- for field in struct.fields -%}
+{{" " if loop.first}}{{field.mojom_name}}(_{{field.mojom_name}}){{", " if not loop.last}}
+{%- endfor %}
+ {
+ }
+#endif
+
+{% for field in struct.fields %}
+ {{field|name}} {{field.mojom_name}};
+{%- endfor %}
+};
+{%- endmacro -%}
+
+
diff --git a/utils/codegen/ipc/generators/libcamera_templates/meson.build b/utils/codegen/ipc/generators/libcamera_templates/meson.build
new file mode 100644
index 00000000..70664eab
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/meson.build
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: CC0-1.0
+
+mojom_template_files = files([
+ 'core_ipa_interface.h.tmpl',
+ 'core_ipa_serializer.h.tmpl',
+ 'definition_functions.tmpl',
+ 'module_ipa_interface.h.tmpl',
+ 'module_ipa_proxy.cpp.tmpl',
+ 'module_ipa_proxy.h.tmpl',
+ 'module_ipa_proxy_worker.cpp.tmpl',
+ 'module_ipa_serializer.h.tmpl',
+ 'proxy_functions.tmpl',
+ 'serializer.tmpl',
+])
diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl
new file mode 100644
index 00000000..4d88a3d7
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl
@@ -0,0 +1,84 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "definition_functions.tmpl" as funcs -%}
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Image Processing Algorithm interface for {{module_name}}
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#pragma once
+
+#include <libcamera/ipa/core_ipa_interface.h>
+#include <libcamera/ipa/ipa_interface.h>
+
+{% if has_map %}#include <map>{% endif %}
+{% if has_array %}#include <vector>{% endif %}
+
+namespace libcamera {
+{%- if has_namespace %}
+{% for ns in namespace %}
+namespace {{ns}} {
+{% endfor %}
+{%- endif %}
+
+{% for const in consts %}
+const {{const.kind|name}} {{const.mojom_name}} = {{const.value}};
+{% endfor %}
+
+enum class {{cmd_enum_name}} {
+ Exit = 0,
+{%- for method in interface_main.methods %}
+ {{method.mojom_name|cap}} = {{loop.index}},
+{%- endfor %}
+};
+
+enum class {{cmd_event_enum_name}} {
+{%- for method in interface_event.methods %}
+ {{method.mojom_name|cap}} = {{loop.index}},
+{%- endfor %}
+};
+
+{% for enum in enums %}
+{{funcs.define_enum(enum)}}
+{% endfor %}
+
+{%- for struct in structs_nonempty %}
+{{funcs.define_struct(struct)}}
+{% endfor %}
+
+{#-
+Any consts or #defines should be moved to the mojom file.
+#}
+class {{interface_name}} : public IPAInterface
+{
+public:
+{% for method in interface_main.methods %}
+ virtual {{method|method_return_value}} {{method.mojom_name}}(
+{%- for param in method|method_parameters %}
+ {{param}}{{- "," if not loop.last}}
+{%- endfor -%}
+) = 0;
+{% endfor %}
+
+{%- for method in interface_event.methods %}
+ Signal<
+{%- for param in method.parameters -%}
+ {{"const " if not param|is_pod}}{{param|name}}{{" &" if not param|is_pod and not param|is_enum}}
+ {{- ", " if not loop.last}}
+{%- endfor -%}
+> {{method.mojom_name}};
+{% endfor -%}
+};
+
+{%- if has_namespace %}
+{% for ns in namespace|reverse %}
+} /* namespace {{ns}} */
+{% endfor %}
+{%- endif %}
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
new file mode 100644
index 00000000..ce3cc5ab
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
@@ -0,0 +1,255 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "proxy_functions.tmpl" as proxy_funcs -%}
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Image Processing Algorithm proxy for {{module_name}}
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#include <libcamera/ipa/{{module_name}}_ipa_proxy.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libcamera/ipa/ipa_module_info.h>
+#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
+#include <libcamera/ipa/{{module_name}}_ipa_serializer.h>
+
+#include <libcamera/base/log.h>
+#include <libcamera/base/thread.h>
+
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/ipa_proxy.h"
+#include "libcamera/internal/ipc_pipe.h"
+#include "libcamera/internal/ipc_pipe_unixsocket.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+#include "libcamera/internal/process.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPAProxy)
+
+{%- if has_namespace %}
+{% for ns in namespace %}
+namespace {{ns}} {
+{% endfor %}
+{%- endif %}
+
+{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)
+ : IPAProxy(ipam), isolate_(isolate),
+ controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)
+{
+ LOG(IPAProxy, Debug)
+ << "initializing {{module_name}} proxy: loading IPA from "
+ << ipam->path();
+
+ if (isolate_) {
+ const std::string proxyWorkerPath = resolvePath("{{module_name}}_ipa_proxy");
+ if (proxyWorkerPath.empty()) {
+ LOG(IPAProxy, Error)
+ << "Failed to get proxy worker path";
+ return;
+ }
+
+ ipc_ = std::make_unique<IPCPipeUnixSocket>(ipam->path().c_str(),
+ proxyWorkerPath.c_str());
+ if (!ipc_->isConnected()) {
+ LOG(IPAProxy, Error) << "Failed to create IPCPipe";
+ return;
+ }
+
+ ipc_->recv.connect(this, &{{proxy_name}}::recvMessage);
+
+ valid_ = true;
+ return;
+ }
+
+ if (!ipam->load())
+ return;
+
+ IPAInterface *ipai = ipam->createInterface();
+ if (!ipai) {
+ LOG(IPAProxy, Error)
+ << "Failed to create IPA context for " << ipam->path();
+ return;
+ }
+
+ ipa_ = std::unique_ptr<{{interface_name}}>(static_cast<{{interface_name}} *>(ipai));
+ proxy_.setIPA(ipa_.get());
+
+{% for method in interface_event.methods %}
+ ipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread);
+{%- endfor %}
+
+ valid_ = true;
+}
+
+{{proxy_name}}::~{{proxy_name}}()
+{
+ if (isolate_) {
+ IPCMessage::Header header =
+ { static_cast<uint32_t>({{cmd_enum_name}}::Exit), seq_++ };
+ IPCMessage msg(header);
+ ipc_->sendAsync(msg);
+ }
+}
+
+{% if interface_event.methods|length > 0 %}
+void {{proxy_name}}::recvMessage(const IPCMessage &data)
+{
+ size_t dataSize = data.data().size();
+ {{cmd_event_enum_name}} _cmd = static_cast<{{cmd_event_enum_name}}>(data.header().cmd);
+
+ switch (_cmd) {
+{%- for method in interface_event.methods %}
+ case {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {
+ {{method.mojom_name}}IPC(data.data().cbegin(), dataSize, data.fds());
+ break;
+ }
+{%- endfor %}
+ default:
+ LOG(IPAProxy, Error) << "Unknown command " << static_cast<uint32_t>(_cmd);
+ }
+}
+{%- endif %}
+
+{% for method in interface_main.methods %}
+{{proxy_funcs.func_sig(proxy_name, method)}}
+{
+ if (isolate_)
+ {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}IPC(
+{%- for param in method|method_param_names -%}
+ {{param}}{{- ", " if not loop.last}}
+{%- endfor -%}
+);
+ else
+ {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}Thread(
+{%- for param in method|method_param_names -%}
+ {{param}}{{- ", " if not loop.last}}
+{%- endfor -%}
+);
+}
+
+{{proxy_funcs.func_sig(proxy_name, method, "Thread")}}
+{
+{%- if method.mojom_name == "stop" %}
+ {{proxy_funcs.stop_thread_body()}}
+{%- elif method.mojom_name == "init" %}
+ {{ method|method_return_value + " _ret = " if method|method_return_value != "void" -}}
+ ipa_->{{method.mojom_name}}(
+ {%- for param in method|method_param_names -%}
+ {{param}}{{- ", " if not loop.last}}
+ {%- endfor -%}
+);
+
+ proxy_.moveToThread(&thread_);
+
+ return {{ "_ret" if method|method_return_value != "void" }};
+{%- elif method.mojom_name == "start" %}
+ state_ = ProxyRunning;
+ thread_.start();
+
+ {{ "return " if method|method_return_value != "void" -}}
+ proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking
+ {{- ", " if method|method_param_names}}
+ {%- for param in method|method_param_names -%}
+ {{param}}{{- ", " if not loop.last}}
+ {%- endfor -%}
+);
+{%- elif not method|is_async %}
+ {{ "return " if method|method_return_value != "void" -}}
+ ipa_->{{method.mojom_name}}(
+ {%- for param in method|method_param_names -%}
+ {{param}}{{- ", " if not loop.last}}
+ {%- endfor -%}
+);
+{% elif method|is_async %}
+ ASSERT(state_ == ProxyRunning);
+ proxy_.invokeMethod(&ThreadProxy::{{method.mojom_name}}, ConnectionTypeQueued
+ {%- for param in method|method_param_names -%}
+ , {{param}}
+ {%- endfor -%}
+);
+{%- endif %}
+}
+
+{{proxy_funcs.func_sig(proxy_name, method, "IPC")}}
+{
+{%- if method.mojom_name == "configure" %}
+ controlSerializer_.reset();
+{%- endif %}
+{%- set has_output = true if method|method_param_outputs|length > 0 or method|method_return_value != "void" %}
+{%- set cmd = cmd_enum_name + "::" + method.mojom_name|cap %}
+ IPCMessage::Header _header = { static_cast<uint32_t>({{cmd}}), seq_++ };
+ IPCMessage _ipcInputBuf(_header);
+{%- if has_output %}
+ IPCMessage _ipcOutputBuf;
+{%- endif %}
+
+{{proxy_funcs.serialize_call(method|method_param_inputs, '_ipcInputBuf.data()', '_ipcInputBuf.fds()')}}
+
+{% if method|is_async %}
+ int _ret = ipc_->sendAsync(_ipcInputBuf);
+{%- else %}
+ int _ret = ipc_->sendSync(_ipcInputBuf
+{{- ", &_ipcOutputBuf" if has_output -}}
+);
+{%- endif %}
+ if (_ret < 0) {
+ LOG(IPAProxy, Error) << "Failed to call {{method.mojom_name}}";
+{%- if method|method_return_value != "void" %}
+ return static_cast<{{method|method_return_value}}>(_ret);
+{%- else %}
+ return;
+{%- endif %}
+ }
+{% if method|method_return_value != "void" %}
+ {{method|method_return_value}} _retValue = IPADataSerializer<{{method|method_return_value}}>::deserialize(_ipcOutputBuf.data(), 0);
+
+{{proxy_funcs.deserialize_call(method|method_param_outputs, '_ipcOutputBuf.data()', '_ipcOutputBuf.fds()', init_offset = method|method_return_value|byte_width|int)}}
+
+ return _retValue;
+
+{% elif method|method_param_outputs|length > 0 %}
+{{proxy_funcs.deserialize_call(method|method_param_outputs, '_ipcOutputBuf.data()', '_ipcOutputBuf.fds()')}}
+{% endif -%}
+}
+
+{% endfor %}
+
+{% for method in interface_event.methods %}
+{{proxy_funcs.func_sig(proxy_name, method, "Thread")}}
+{
+ ASSERT(state_ != ProxyStopped);
+ {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});
+}
+
+void {{proxy_name}}::{{method.mojom_name}}IPC(
+ [[maybe_unused]] std::vector<uint8_t>::const_iterator data,
+ [[maybe_unused]] size_t dataSize,
+ [[maybe_unused]] const std::vector<SharedFD> &fds)
+{
+{%- for param in method.parameters %}
+ {{param|name}} {{param.mojom_name}};
+{%- endfor %}
+{{proxy_funcs.deserialize_call(method.parameters, 'data', 'fds', false, false, true, 'dataSize')}}
+ {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});
+}
+{% endfor %}
+
+{%- if has_namespace %}
+{% for ns in namespace|reverse %}
+} /* namespace {{ns}} */
+{% endfor %}
+{%- endif %}
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
new file mode 100644
index 00000000..e213b18a
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
@@ -0,0 +1,132 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "proxy_functions.tmpl" as proxy_funcs -%}
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Image Processing Algorithm proxy for {{module_name}}
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#pragma once
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
+
+#include <libcamera/base/object.h>
+#include <libcamera/base/thread.h>
+
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_proxy.h"
+#include "libcamera/internal/ipc_pipe.h"
+#include "libcamera/internal/ipc_pipe_unixsocket.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+
+namespace libcamera {
+{%- if has_namespace %}
+{% for ns in namespace %}
+namespace {{ns}} {
+{% endfor %}
+{%- endif %}
+
+class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object
+{
+public:
+ {{proxy_name}}(IPAModule *ipam, bool isolate);
+ ~{{proxy_name}}();
+
+{% for method in interface_main.methods %}
+{{proxy_funcs.func_sig(proxy_name, method, "", false, true)|indent(8, true)}};
+{% endfor %}
+
+{%- for method in interface_event.methods %}
+ Signal<
+{%- for param in method.parameters -%}
+ {{"const " if not param|is_pod}}{{param|name}}{{" &" if not param|is_pod and not param|is_enum}}
+ {{- ", " if not loop.last}}
+{%- endfor -%}
+> {{method.mojom_name}};
+{% endfor %}
+
+private:
+ void recvMessage(const IPCMessage &data);
+
+{% for method in interface_main.methods %}
+{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}};
+{{proxy_funcs.func_sig(proxy_name, method, "IPC", false)|indent(8, true)}};
+{% endfor %}
+{% for method in interface_event.methods %}
+{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}};
+ void {{method.mojom_name}}IPC(
+ std::vector<uint8_t>::const_iterator data,
+ size_t dataSize,
+ const std::vector<SharedFD> &fds);
+{% endfor %}
+
+ /* Helper class to invoke async functions in another thread. */
+ class ThreadProxy : public Object
+ {
+ public:
+ ThreadProxy()
+ : ipa_(nullptr)
+ {
+ }
+
+ void setIPA({{interface_name}} *ipa)
+ {
+ ipa_ = ipa;
+ }
+
+ void stop()
+ {
+ ipa_->stop();
+ }
+{% for method in interface_main.methods %}
+{%- if method|is_async %}
+ {{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(16)}}
+ {
+ ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});
+ }
+{%- elif method.mojom_name == "start" %}
+ {{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(16)}}
+ {
+{%- if method|method_return_value != "void" %}
+ return ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});
+{%- else %}
+ ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}}
+ {{- ", " if method|method_param_outputs|params_comma_sep -}}
+ {{- method|method_param_outputs|params_comma_sep}});
+{%- endif %}
+ }
+{%- endif %}
+{%- endfor %}
+
+ private:
+ {{interface_name}} *ipa_;
+ };
+
+ Thread thread_;
+ ThreadProxy proxy_;
+ std::unique_ptr<{{interface_name}}> ipa_;
+
+ const bool isolate_;
+
+ std::unique_ptr<IPCPipeUnixSocket> ipc_;
+
+ ControlSerializer controlSerializer_;
+
+{# \todo Move this to IPCPipe #}
+ uint32_t seq_;
+};
+
+{%- if has_namespace %}
+{% for ns in namespace|reverse %}
+} /* namespace {{ns}} */
+{% endfor %}
+{%- endif %}
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl
new file mode 100644
index 00000000..1f990d3f
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl
@@ -0,0 +1,246 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "proxy_functions.tmpl" as proxy_funcs -%}
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Image Processing Algorithm proxy worker for {{module_name}}
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+{#- \todo Split proxy worker into IPC worker and proxy worker. #}
+
+#include <algorithm>
+#include <iostream>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+
+#include <libcamera/ipa/ipa_interface.h>
+#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
+#include <libcamera/ipa/{{module_name}}_ipa_serializer.h>
+#include <libcamera/logging.h>
+
+#include <libcamera/base/event_dispatcher.h>
+#include <libcamera/base/log.h>
+#include <libcamera/base/thread.h>
+#include <libcamera/base/unique_fd.h>
+
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/ipa_proxy.h"
+#include "libcamera/internal/ipc_pipe.h"
+#include "libcamera/internal/ipc_pipe_unixsocket.h"
+#include "libcamera/internal/ipc_unixsocket.h"
+
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY({{proxy_worker_name}})
+
+{%- if has_namespace %}
+{% for ns in namespace -%}
+using namespace {{ns}};
+{% endfor %}
+{%- endif %}
+
+class {{proxy_worker_name}}
+{
+public:
+ {{proxy_worker_name}}()
+ : ipa_(nullptr),
+ controlSerializer_(ControlSerializer::Role::Worker),
+ exit_(false) {}
+
+ ~{{proxy_worker_name}}() {}
+
+ void readyRead()
+ {
+ IPCUnixSocket::Payload _message;
+ int _retRecv = socket_.receive(&_message);
+ if (_retRecv) {
+ LOG({{proxy_worker_name}}, Error)
+ << "Receive message failed: " << _retRecv;
+ return;
+ }
+
+ IPCMessage _ipcMessage(_message);
+
+ {{cmd_enum_name}} _cmd = static_cast<{{cmd_enum_name}}>(_ipcMessage.header().cmd);
+
+ switch (_cmd) {
+ case {{cmd_enum_name}}::Exit: {
+ exit_ = true;
+ break;
+ }
+
+{% for method in interface_main.methods %}
+ case {{cmd_enum_name}}::{{method.mojom_name|cap}}: {
+{%- if method.mojom_name == "configure" %}
+ controlSerializer_.reset();
+{%- endif %}
+ {{proxy_funcs.deserialize_call(method|method_param_inputs, '_ipcMessage.data()', '_ipcMessage.fds()', false, true)|indent(16, true)}}
+{% for param in method|method_param_outputs %}
+ {{param|name}} {{param.mojom_name}};
+{% endfor %}
+{%- if method|method_return_value != "void" %}
+ {{method|method_return_value}} _callRet =
+{%- endif -%}
+ ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}}
+{{- ", " if method|method_param_outputs|params_comma_sep -}}
+{%- for param in method|method_param_outputs -%}
+&{{param.mojom_name}}{{", " if not loop.last}}
+{%- endfor -%}
+);
+{% if not method|is_async %}
+ IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
+ IPCMessage _response(header);
+{%- if method|method_return_value != "void" %}
+ std::vector<uint8_t> _callRetBuf;
+ std::tie(_callRetBuf, std::ignore) =
+ IPADataSerializer<{{method|method_return_value}}>::serialize(_callRet);
+ _response.data().insert(_response.data().end(), _callRetBuf.cbegin(), _callRetBuf.cend());
+{%- endif %}
+ {{proxy_funcs.serialize_call(method|method_param_outputs, "_response.data()", "_response.fds()")|indent(16, true)}}
+ int _ret = socket_.send(_response.payload());
+ if (_ret < 0) {
+ LOG({{proxy_worker_name}}, Error)
+ << "Reply to {{method.mojom_name}}() failed: " << _ret;
+ }
+ LOG({{proxy_worker_name}}, Debug) << "Done replying to {{method.mojom_name}}()";
+{%- endif %}
+ break;
+ }
+{% endfor %}
+ default:
+ LOG({{proxy_worker_name}}, Error) << "Unknown command " << _ipcMessage.header().cmd;
+ }
+ }
+
+ int init(std::unique_ptr<IPAModule> &ipam, UniqueFD socketfd)
+ {
+ if (socket_.bind(std::move(socketfd)) < 0) {
+ LOG({{proxy_worker_name}}, Error)
+ << "IPC socket binding failed";
+ return EXIT_FAILURE;
+ }
+ socket_.readyRead.connect(this, &{{proxy_worker_name}}::readyRead);
+
+ ipa_ = dynamic_cast<{{interface_name}} *>(ipam->createInterface());
+ if (!ipa_) {
+ LOG({{proxy_worker_name}}, Error)
+ << "Failed to create IPA interface instance";
+ return EXIT_FAILURE;
+ }
+{% for method in interface_event.methods %}
+ ipa_->{{method.mojom_name}}.connect(this, &{{proxy_worker_name}}::{{method.mojom_name}});
+{%- endfor %}
+ return 0;
+ }
+
+ void run()
+ {
+ EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
+ while (!exit_)
+ dispatcher->processEvents();
+ }
+
+ void cleanup()
+ {
+ delete ipa_;
+ socket_.close();
+ }
+
+private:
+
+{% for method in interface_event.methods %}
+{{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(8, true)}}
+ {
+ IPCMessage::Header header = {
+ static_cast<uint32_t>({{cmd_event_enum_name}}::{{method.mojom_name|cap}}),
+ 0
+ };
+ IPCMessage _message(header);
+
+ {{proxy_funcs.serialize_call(method|method_param_inputs, "_message.data()", "_message.fds()")}}
+
+ int _ret = socket_.send(_message.payload());
+ if (_ret < 0)
+ LOG({{proxy_worker_name}}, Error)
+ << "Sending event {{method.mojom_name}}() failed: " << _ret;
+
+ LOG({{proxy_worker_name}}, Debug) << "{{method.mojom_name}} done";
+ }
+{% endfor %}
+
+ {{interface_name}} *ipa_;
+ IPCUnixSocket socket_;
+
+ ControlSerializer controlSerializer_;
+
+ bool exit_;
+};
+
+int main(int argc, char **argv)
+{
+{#- \todo Handle enabling debugging more dynamically. #}
+ /* Uncomment this for debugging. */
+#if 0
+ std::string logPath = "/tmp/libcamera.worker." +
+ std::to_string(getpid()) + ".log";
+ logSetFile(logPath.c_str());
+#endif
+
+ if (argc < 3) {
+ LOG({{proxy_worker_name}}, Error)
+ << "Tried to start worker with no args: "
+ << "expected <path to IPA so> <fd to bind unix socket>";
+ return EXIT_FAILURE;
+ }
+
+ UniqueFD fd(std::stoi(argv[2]));
+ LOG({{proxy_worker_name}}, Info)
+ << "Starting worker for IPA module " << argv[1]
+ << " with IPC fd = " << fd.get();
+
+ std::unique_ptr<IPAModule> ipam = std::make_unique<IPAModule>(argv[1]);
+ if (!ipam->isValid() || !ipam->load()) {
+ LOG({{proxy_worker_name}}, Error)
+ << "IPAModule " << argv[1] << " isn't valid";
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * Shutdown of proxy worker can be pre-empted by events like
+ * SIGINT/SIGTERM, even before the pipeline handler can request
+ * shutdown. Hence, assign a new gid to prevent signals on the
+ * application being delivered to the proxy.
+ */
+ if (setpgid(0, 0) < 0) {
+ int err = errno;
+ LOG({{proxy_worker_name}}, Warning)
+ << "Failed to set new gid: " << strerror(err);
+ }
+
+ {{proxy_worker_name}} proxyWorker;
+ int ret = proxyWorker.init(ipam, std::move(fd));
+ if (ret < 0) {
+ LOG({{proxy_worker_name}}, Error)
+ << "Failed to initialize proxy worker";
+ return ret;
+ }
+
+ LOG({{proxy_worker_name}}, Debug) << "Proxy worker successfully initialized";
+
+ proxyWorker.run();
+
+ proxyWorker.cleanup();
+
+ return 0;
+}
diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
new file mode 100644
index 00000000..cd5a65a9
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
@@ -0,0 +1,45 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{%- import "serializer.tmpl" as serializer -%}
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * Image Processing Algorithm data serializer for {{module_name}}
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <vector>
+
+#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
+#include <libcamera/ipa/core_ipa_serializer.h>
+
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPADataSerializer)
+{% for struct in structs_nonempty %}
+template<>
+class IPADataSerializer<{{struct|name_full}}>
+{
+public:
+{{- serializer.serializer(struct, namespace_str)}}
+{%- if struct|has_fd %}
+{{serializer.deserializer_fd(struct, namespace_str)}}
+{%- else %}
+{{serializer.deserializer_no_fd(struct, namespace_str)}}
+{{serializer.deserializer_fd_simple(struct, namespace_str)}}
+{%- endif %}
+};
+{% endfor %}
+
+} /* namespace libcamera */
diff --git a/utils/codegen/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/codegen/ipc/generators/libcamera_templates/proxy_functions.tmpl
new file mode 100644
index 00000000..b5797b14
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/proxy_functions.tmpl
@@ -0,0 +1,202 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{#
+ # \brief Generate function prototype
+ #
+ # \param class Class name
+ # \param method mojom Method object
+ # \param suffix Suffix to append to \a method function name
+ # \param need_class_name If true, generate class name with function
+ # \param override If true, generate override tag after the function prototype
+ #}
+{%- macro func_sig(class, method, suffix = "", need_class_name = true, override = false) -%}
+{{method|method_return_value}} {{class + "::" if need_class_name}}{{method.mojom_name}}{{suffix}}(
+{%- for param in method|method_parameters %}
+ {{param}}{{- "," if not loop.last}}
+{%- endfor -%}
+){{" override" if override}}
+{%- endmacro -%}
+
+{#
+ # \brief Generate function body for IPA stop() function for thread
+ #}
+{%- macro stop_thread_body() -%}
+ ASSERT(state_ != ProxyStopping);
+ if (state_ != ProxyRunning)
+ return;
+
+ state_ = ProxyStopping;
+
+ proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking);
+
+ thread_.exit();
+ thread_.wait();
+
+ Thread::current()->dispatchMessages(Message::Type::InvokeMessage);
+
+ state_ = ProxyStopped;
+{%- endmacro -%}
+
+
+{#
+ # \brief Serialize multiple objects into data buffer and fd vector
+ #
+ # Generate code to serialize multiple objects, as specified in \a params
+ # (which are the parameters to some function), into \a buf data buffer and
+ # \a fds fd vector.
+ # This code is meant to be used by the proxy, for serializing prior to IPC calls.
+ #
+ # \todo Avoid intermediate vectors
+ #}
+{%- macro serialize_call(params, buf, fds) %}
+{%- for param in params %}
+{%- if param|is_enum %}
+ static_assert(sizeof({{param|name_full}}) <= 4);
+{%- endif %}
+ std::vector<uint8_t> {{param.mojom_name}}Buf;
+{%- if param|has_fd %}
+ std::vector<SharedFD> {{param.mojom_name}}Fds;
+ std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
+{%- else %}
+ std::tie({{param.mojom_name}}Buf, std::ignore) =
+{%- endif %}
+{%- if param|is_flags %}
+ IPADataSerializer<{{param|name_full}}>::serialize({{param.mojom_name}}
+{%- elif param|is_enum %}
+ IPADataSerializer<uint32_t>::serialize(static_cast<uint32_t>({{param.mojom_name}})
+{%- else %}
+ IPADataSerializer<{{param|name}}>::serialize({{param.mojom_name}}
+{% endif -%}
+{{- ", &controlSerializer_" if param|needs_control_serializer -}}
+);
+{%- endfor %}
+
+{%- if params|length > 1 %}
+{%- for param in params %}
+ appendPOD<uint32_t>({{buf}}, {{param.mojom_name}}Buf.size());
+{%- if param|has_fd %}
+ appendPOD<uint32_t>({{buf}}, {{param.mojom_name}}Fds.size());
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
+{%- for param in params %}
+ {{buf}}.insert({{buf}}.end(), {{param.mojom_name}}Buf.begin(), {{param.mojom_name}}Buf.end());
+{%- endfor %}
+
+{%- for param in params %}
+{%- if param|has_fd %}
+ {{fds}}.insert({{fds}}.end(), {{param.mojom_name}}Fds.begin(), {{param.mojom_name}}Fds.end());
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+
+{#
+ # \brief Deserialize a single object from data buffer and fd vector
+ #
+ # \param pointer If true, deserialize the object into a dereferenced pointer
+ # \param iter If true, treat \a buf as an iterator instead of a vector
+ # \param data_size Variable that holds the size of the vector referenced by \a buf
+ #
+ # Generate code to deserialize a single object, as specified in \a param,
+ # from \a buf data buffer and \a fds fd vector.
+ # This code is meant to be used by macro deserialize_call.
+ #}
+{%- macro deserialize_param(param, pointer, loop, buf, fds, iter, data_size) -%}
+{{"*" if pointer}}{{param.mojom_name}} =
+{%- if param|is_flags %}
+IPADataSerializer<{{param|name_full}}>::deserialize(
+{%- elif param|is_enum %}
+static_cast<{{param|name_full}}>(IPADataSerializer<uint32_t>::deserialize(
+{%- else %}
+IPADataSerializer<{{param|name}}>::deserialize(
+{%- endif %}
+ {{buf}}{{- ".cbegin()" if not iter}} + {{param.mojom_name}}Start,
+{%- if loop.last and not iter %}
+ {{buf}}.cend()
+{%- elif not iter %}
+ {{buf}}.cbegin() + {{param.mojom_name}}Start + {{param.mojom_name}}BufSize
+{%- elif iter and loop.length == 1 %}
+ {{buf}} + {{data_size}}
+{%- else %}
+ {{buf}} + {{param.mojom_name}}Start + {{param.mojom_name}}BufSize
+{%- endif -%}
+{{- "," if param|has_fd}}
+{%- if param|has_fd %}
+ {{fds}}.cbegin() + {{param.mojom_name}}FdStart,
+{%- if loop.last %}
+ {{fds}}.cend()
+{%- else %}
+ {{fds}}.cbegin() + {{param.mojom_name}}FdStart + {{param.mojom_name}}FdsSize
+{%- endif -%}
+{%- endif -%}
+{{- "," if param|needs_control_serializer}}
+{%- if param|needs_control_serializer %}
+ &controlSerializer_
+{%- endif -%}
+){{")" if param|is_enum and not param|is_flags}};
+{%- endmacro -%}
+
+
+{#
+ # \brief Deserialize multiple objects from data buffer and fd vector
+ #
+ # \param pointer If true, deserialize objects into pointers, and adds a null check.
+ # \param declare If true, declare the objects in addition to deserialization.
+ # \param iter if true, treat \a buf as an iterator instead of a vector
+ # \param data_size Variable that holds the size of the vector referenced by \a buf
+ #
+ # Generate code to deserialize multiple objects, as specified in \a params
+ # (which are the parameters to some function), from \a buf data buffer and
+ # \a fds fd vector.
+ # This code is meant to be used by the proxy, for deserializing after IPC calls.
+ #
+ # \todo Avoid intermediate vectors
+ #}
+{%- macro deserialize_call(params, buf, fds, pointer = true, declare = false, iter = false, data_size = '', init_offset = 0) -%}
+{% set ns = namespace(size_offset = init_offset) %}
+{%- if params|length > 1 %}
+{%- for param in params %}
+ [[maybe_unused]] const size_t {{param.mojom_name}}BufSize = readPOD<uint32_t>({{buf}}, {{ns.size_offset}}
+{%- if iter -%}
+, {{buf}} + {{data_size}}
+{%- endif -%}
+);
+ {%- set ns.size_offset = ns.size_offset + 4 %}
+{%- if param|has_fd %}
+ [[maybe_unused]] const size_t {{param.mojom_name}}FdsSize = readPOD<uint32_t>({{buf}}, {{ns.size_offset}}
+{%- if iter -%}
+, {{buf}} + {{data_size}}
+{%- endif -%}
+);
+ {%- set ns.size_offset = ns.size_offset + 4 %}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{% for param in params %}
+{%- if loop.first %}
+ const size_t {{param.mojom_name}}Start = {{ns.size_offset}};
+{%- else %}
+ const size_t {{param.mojom_name}}Start = {{loop.previtem.mojom_name}}Start + {{loop.previtem.mojom_name}}BufSize;
+{%- endif %}
+{%- endfor %}
+{% for param in params|with_fds %}
+{%- if loop.first %}
+ const size_t {{param.mojom_name}}FdStart = 0;
+{%- else %}
+ const size_t {{param.mojom_name}}FdStart = {{loop.previtem.mojom_name}}FdStart + {{loop.previtem.mojom_name}}FdsSize;
+{%- endif %}
+{%- endfor %}
+{% for param in params %}
+ {%- if pointer %}
+ if ({{param.mojom_name}}) {
+{{deserialize_param(param, pointer, loop, buf, fds, iter, data_size)|indent(16, True)}}
+ }
+ {%- else %}
+ {{param|name + " " if declare}}{{deserialize_param(param, pointer, loop, buf, fds, iter, data_size)|indent(8)}}
+ {%- endif %}
+{% endfor %}
+{%- endmacro -%}
diff --git a/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl b/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl
new file mode 100644
index 00000000..323e1293
--- /dev/null
+++ b/utils/codegen/ipc/generators/libcamera_templates/serializer.tmpl
@@ -0,0 +1,319 @@
+{#-
+ # SPDX-License-Identifier: LGPL-2.1-or-later
+ # Copyright (C) 2020, Google Inc.
+-#}
+{#
+ # \brief Verify that there is enough bytes to deserialize
+ #
+ # Generate code that verifies that \a size is not greater than \a dataSize.
+ # Otherwise log an error with \a name and \a typename.
+ #}
+{%- macro check_data_size(size, dataSize, name, typename) %}
+ if ({{dataSize}} < {{size}}) {
+ LOG(IPADataSerializer, Error)
+ << "Failed to deserialize " << "{{name}}"
+ << ": not enough {{typename}}, expected "
+ << ({{size}}) << ", got " << ({{dataSize}});
+ return ret;
+ }
+{%- endmacro %}
+
+
+{#
+ # \brief Serialize a field into return vector
+ #
+ # Generate code to serialize \a field into retData, including size of the
+ # field and fds (where appropriate).
+ # This code is meant to be used by the IPADataSerializer specialization.
+ #
+ # \todo Avoid intermediate vectors
+ #}
+{%- macro serializer_field(field, namespace, loop) %}
+{%- if field|is_pod or field|is_enum %}
+ std::vector<uint8_t> {{field.mojom_name}};
+ std::tie({{field.mojom_name}}, std::ignore) =
+ {%- if field|is_pod %}
+ IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
+ {%- elif field|is_flags %}
+ IPADataSerializer<{{field|name_full}}>::serialize(data.{{field.mojom_name}});
+ {%- elif field|is_enum_scoped %}
+ IPADataSerializer<uint{{field|bit_width}}_t>::serialize(static_cast<uint{{field|bit_width}}_t>(data.{{field.mojom_name}}));
+ {%- elif field|is_enum %}
+ IPADataSerializer<uint{{field|bit_width}}_t>::serialize(data.{{field.mojom_name}});
+ {%- endif %}
+ retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
+{%- elif field|is_fd %}
+ std::vector<uint8_t> {{field.mojom_name}};
+ std::vector<SharedFD> {{field.mojom_name}}Fds;
+ std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
+ IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
+ retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
+ retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
+{%- elif field|is_controls %}
+ if (data.{{field.mojom_name}}.size() > 0) {
+ std::vector<uint8_t> {{field.mojom_name}};
+ std::tie({{field.mojom_name}}, std::ignore) =
+ IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}, cs);
+ appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
+ retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
+ } else {
+ appendPOD<uint32_t>(retData, 0);
+ }
+{%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
+ std::vector<uint8_t> {{field.mojom_name}};
+ {%- if field|has_fd %}
+ std::vector<SharedFD> {{field.mojom_name}}Fds;
+ std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
+ {%- else %}
+ std::tie({{field.mojom_name}}, std::ignore) =
+ {%- endif %}
+ {%- if field|is_array or field|is_map %}
+ IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}, cs);
+ {%- elif field|is_str %}
+ IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});
+ {%- else %}
+ IPADataSerializer<{{field|name_full}}>::serialize(data.{{field.mojom_name}}, cs);
+ {%- endif %}
+ appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
+ {%- if field|has_fd %}
+ appendPOD<uint32_t>(retData, {{field.mojom_name}}Fds.size());
+ {%- endif %}
+ retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
+ {%- if field|has_fd %}
+ retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
+ {%- endif %}
+{%- else %}
+ /* Unknown serialization for {{field.mojom_name}}. */
+{%- endif %}
+{%- endmacro %}
+
+
+{#
+ # \brief Deserialize a field into return struct
+ #
+ # Generate code to deserialize \a field into object ret.
+ # This code is meant to be used by the IPADataSerializer specialization.
+ #}
+{%- macro deserializer_field(field, namespace, loop) %}
+{% if field|is_pod or field|is_enum %}
+ {%- set field_size = (field|bit_width|int / 8)|int %}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
+ {%- if field|is_pod %}
+ ret.{{field.mojom_name}} = IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field_size}});
+ {%- elif field|is_flags %}
+ ret.{{field.mojom_name}} = IPADataSerializer<{{field|name_full}}>::deserialize(m, m + {{field_size}});
+ {%- else %}
+ ret.{{field.mojom_name}} = static_cast<{{field|name_full}}>(IPADataSerializer<uint{{field|bit_width}}_t>::deserialize(m, m + {{field_size}}));
+ {%- endif %}
+ {%- if not loop.last %}
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {%- endif %}
+{% elif field|is_fd %}
+ {%- set field_size = 4 %}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
+ ret.{{field.mojom_name}} = IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field_size}}, n, n + 1, cs);
+ {%- if not loop.last %}
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ n += ret.{{field.mojom_name}}.isValid() ? 1 : 0;
+ fdsSize -= ret.{{field.mojom_name}}.isValid() ? 1 : 0;
+ {%- endif %}
+{% elif field|is_controls %}
+ {%- set field_size = 4 %}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
+ const size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, dataEnd);
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {%- set field_size = field.mojom_name + 'Size' -%}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
+ if ({{field.mojom_name}}Size > 0)
+ ret.{{field.mojom_name}} =
+ IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
+ {%- if not loop.last %}
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {%- endif %}
+{% elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
+ {%- set field_size = 4 %}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
+ const size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, dataEnd);
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {%- if field|has_fd %}
+ {%- set field_size = 4 %}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'FdsSize', 'data')}}
+ const size_t {{field.mojom_name}}FdsSize = readPOD<uint32_t>(m, 0, dataEnd);
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {{- check_data_size(field.mojom_name + 'FdsSize', 'fdsSize', field.mojom_name, 'fds')}}
+ {%- endif %}
+ {%- set field_size = field.mojom_name + 'Size' -%}
+ {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
+ ret.{{field.mojom_name}} =
+ {%- if field|is_str %}
+ IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size);
+ {%- elif field|has_fd and (field|is_array or field|is_map) %}
+ IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
+ {%- elif field|has_fd and (not (field|is_array or field|is_map)) %}
+ IPADataSerializer<{{field|name_full}}>::deserialize(m, m + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
+ {%- elif (not field|has_fd) and (field|is_array or field|is_map) %}
+ IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
+ {%- else %}
+ IPADataSerializer<{{field|name_full}}>::deserialize(m, m + {{field.mojom_name}}Size, cs);
+ {%- endif %}
+ {%- if not loop.last %}
+ m += {{field_size}};
+ dataSize -= {{field_size}};
+ {%- if field|has_fd %}
+ n += {{field.mojom_name}}FdsSize;
+ fdsSize -= {{field.mojom_name}}FdsSize;
+ {%- endif %}
+ {%- endif %}
+{% else %}
+ /* Unknown deserialization for {{field.mojom_name}}. */
+{%- endif %}
+{%- endmacro %}
+
+
+{#
+ # \brief Serialize a struct
+ #
+ # Generate code for IPADataSerializer specialization, for serializing
+ # \a struct.
+ #}
+{%- macro serializer(struct, namespace) %}
+ static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
+ serialize(const {{struct|name_full}} &data,
+{%- if struct|needs_control_serializer %}
+ ControlSerializer *cs)
+{%- else %}
+ [[maybe_unused]] ControlSerializer *cs = nullptr)
+{%- endif %}
+ {
+ std::vector<uint8_t> retData;
+{%- if struct|has_fd %}
+ std::vector<SharedFD> retFds;
+{%- endif %}
+{%- for field in struct.fields %}
+{{serializer_field(field, namespace, loop)}}
+{%- endfor %}
+{% if struct|has_fd %}
+ return {retData, retFds};
+{%- else %}
+ return {retData, {}};
+{%- endif %}
+ }
+{%- endmacro %}
+
+
+{#
+ # \brief Deserialize a struct that has fds
+ #
+ # Generate code for IPADataSerializer specialization, for deserializing
+ # \a struct, in the case that \a struct has file descriptors.
+ #}
+{%- macro deserializer_fd(struct, namespace) %}
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t> &data,
+ std::vector<SharedFD> &fds,
+{%- if struct|needs_control_serializer %}
+ ControlSerializer *cs)
+{%- else %}
+ ControlSerializer *cs = nullptr)
+{%- endif %}
+ {
+ return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), fds.cbegin(), fds.cend(), cs);
+ }
+
+{# \todo Don't inline this function #}
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t>::const_iterator dataBegin,
+ std::vector<uint8_t>::const_iterator dataEnd,
+ std::vector<SharedFD>::const_iterator fdsBegin,
+ std::vector<SharedFD>::const_iterator fdsEnd,
+{%- if struct|needs_control_serializer %}
+ ControlSerializer *cs)
+{%- else %}
+ [[maybe_unused]] ControlSerializer *cs = nullptr)
+{%- endif %}
+ {
+ {{struct|name_full}} ret;
+ std::vector<uint8_t>::const_iterator m = dataBegin;
+ std::vector<SharedFD>::const_iterator n = fdsBegin;
+
+ size_t dataSize = std::distance(dataBegin, dataEnd);
+ [[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);
+{%- for field in struct.fields -%}
+{{deserializer_field(field, namespace, loop)}}
+{%- endfor %}
+ return ret;
+ }
+{%- endmacro %}
+
+{#
+ # \brief Deserialize a struct that has fds, using non-fd
+ #
+ # Generate code for IPADataSerializer specialization, for deserializing
+ # \a struct, in the case that \a struct has no file descriptors but requires
+ # deserializers with file descriptors.
+ #}
+{%- macro deserializer_fd_simple(struct, namespace) %}
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t> &data,
+ [[maybe_unused]] std::vector<SharedFD> &fds,
+ ControlSerializer *cs = nullptr)
+ {
+ return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
+ }
+
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t>::const_iterator dataBegin,
+ std::vector<uint8_t>::const_iterator dataEnd,
+ [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
+ [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
+ ControlSerializer *cs = nullptr)
+ {
+ return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);
+ }
+{%- endmacro %}
+
+
+{#
+ # \brief Deserialize a struct that has no fds
+ #
+ # Generate code for IPADataSerializer specialization, for deserializing
+ # \a struct, in the case that \a struct does not have file descriptors.
+ #}
+{%- macro deserializer_no_fd(struct, namespace) %}
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t> &data,
+{%- if struct|needs_control_serializer %}
+ ControlSerializer *cs)
+{%- else %}
+ ControlSerializer *cs = nullptr)
+{%- endif %}
+ {
+ return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);
+ }
+
+{# \todo Don't inline this function #}
+ static {{struct|name_full}}
+ deserialize(std::vector<uint8_t>::const_iterator dataBegin,
+ std::vector<uint8_t>::const_iterator dataEnd,
+{%- if struct|needs_control_serializer %}
+ ControlSerializer *cs)
+{%- else %}
+ [[maybe_unused]] ControlSerializer *cs = nullptr)
+{%- endif %}
+ {
+ {{struct|name_full}} ret;
+ std::vector<uint8_t>::const_iterator m = dataBegin;
+
+ size_t dataSize = std::distance(dataBegin, dataEnd);
+{%- for field in struct.fields -%}
+{{deserializer_field(field, namespace, loop)}}
+{%- endfor %}
+ return ret;
+ }
+{%- endmacro %}