{#-
 # 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, 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, 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) %}
	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, 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) %}
	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, 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) %}
	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) %}
	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, loop)}}
{%- endfor %}
		return ret;
	}
{%- endmacro %}

{#
 # \brief Deserialize a struct
 #
 # Generate code for IPADataSerializer specialization, for deserializing \a struct.
 #}
{%- macro deserializer(struct) %}
{%- if struct|has_fd %}
{{deserializer_fd(struct)}}
{%- else %}
{{deserializer_no_fd(struct)}}
{{deserializer_fd_simple(struct)}}
{%- endif %}
{%- endmacro %}
