[libcamera-devel,13/38] libcamera: Add IPADataSerializer

Message ID 20200922133537.258098-14-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • IPA isolation implementation
Related show

Commit Message

Paul Elder Sept. 22, 2020, 1:35 p.m. UTC
Add an IPADataSerializer which implments de/serialization of built-in
and libcamera data structures. This is intended to be used by the proxy
and the proxy worker in the IPC layer.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v2:
- added serializers for all integer types, bool, and string
- use string serializer for IPASettings serializer
- add documentation
- add serializer for const ControlList
---
 .../libcamera/internal/ipa_data_serializer.h  | 1220 +++++++++++++++++
 src/libcamera/ipa_data_serializer.cpp         |  154 +++
 src/libcamera/meson.build                     |    1 +
 3 files changed, 1375 insertions(+)
 create mode 100644 include/libcamera/internal/ipa_data_serializer.h
 create mode 100644 src/libcamera/ipa_data_serializer.cpp

Comments

Jacopo Mondi Sept. 23, 2020, 3:32 p.m. UTC | #1
Hi Paul,

On Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:
> Add an IPADataSerializer which implments de/serialization of built-in

With built-in do you mean STL defined data types ?

> and libcamera data structures. This is intended to be used by the proxy
> and the proxy worker in the IPC layer.
>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
>
> ---
> Changes in v2:
> - added serializers for all integer types, bool, and string
> - use string serializer for IPASettings serializer
> - add documentation
> - add serializer for const ControlList
> ---
>  .../libcamera/internal/ipa_data_serializer.h  | 1220 +++++++++++++++++
>  src/libcamera/ipa_data_serializer.cpp         |  154 +++
>  src/libcamera/meson.build                     |    1 +
>  3 files changed, 1375 insertions(+)
>  create mode 100644 include/libcamera/internal/ipa_data_serializer.h
>  create mode 100644 src/libcamera/ipa_data_serializer.cpp
>
> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> new file mode 100644
> index 00000000..accd4dce
> --- /dev/null
> +++ b/include/libcamera/internal/ipa_data_serializer.h
> @@ -0,0 +1,1220 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * ipa_data_serializer.h - Image Processing Algorithm data serializer
> + */
> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> +
> +#include <deque>
> +#include <iostream>
> +#include <tuple>
> +#include <vector>
> +
> +#include <libcamera/buffer.h>
> +#include <libcamera/control_ids.h>
> +#include <libcamera/geometry.h>
> +#include <libcamera/ipa/ipa_interface.h>
> +
> +#include "libcamera/internal/byte_stream_buffer.h"
> +#include "libcamera/internal/camera_sensor.h"
> +#include "libcamera/internal/control_serializer.h"
> +#include "libcamera/internal/log.h"
> +
> +namespace libcamera {
> +
> +LOG_DECLARE_CATEGORY(IPADataSerializer)
> +
> +template<typename T>
> +void appendUInt(std::vector<uint8_t> &vec, T val)
> +{
> +	size_t byteWidth = sizeof(val);
> +	for (size_t i = 0; i < byteWidth; i++)
> +		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));

I see in several places missing spaces around the '*' operator
I think you can also drop the & 0xff as it's implied in the cast to
uint8_t?

> +}
> +
> +template<typename T>
> +T readUInt(std::vector<uint8_t> &vec, size_t pos)
> +{
> +	T ret = 0;
> +	size_t byteWidth = sizeof(ret);
> +	if (pos + byteWidth > vec.size())
> +		return ret;
> +
> +	for (size_t i = 0; i < byteWidth; i++)
> +		ret |= vec[pos + i] << 8*i;

You could iterate the vector and call the below defined overload. Not
a big deal, it would be probably more confusing.

> +	return ret;
> +}
> +
> +template<typename T>
> +T readUInt(std::vector<uint8_t>::iterator it)
> +{
> +	T ret = 0;
> +	size_t byteWidth = sizeof(ret);
> +	for (size_t i = 0; i < byteWidth; i++)
> +		ret |= *(it + i) << 8*i;
> +	return ret;
> +}
> +
> +template<typename T>
> +class IPADataSerializer
> +{
> +#ifdef __DOXYGEN__
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(T data, ControlSerializer *cs);
> +
> +	static T deserialize(std::vector<uint8_t> &data,
> +			     ControlSerializer *cs);

That *cs parameter is unused in all specializations but ControlList
one. We already had to face a similar issue when we implemented
ControlValue. You could define two versions of these functions one for
the ControlList case which has *cs in its signature, the other for all
the other types, without cs. The construct to do so is not nice at a
first look but effective:
https://en.cppreference.com/w/cpp/types/enable_if

I think it could be applied here, maybe on top ?

> +	static T deserialize(std::vector<uint8_t>::iterator it1,
> +			     std::vector<uint8_t>::iterator it2,
> +			     ControlSerializer *cs);
> +
> +	static T deserialize(std::vector<uint8_t> &data,
> +			     std::vector<int32_t> &fds,
> +			     ControlSerializer *cs);
> +	static T deserialize(std::vector<uint8_t>::iterator data_it1,
> +			     std::vector<uint8_t>::iterator data_it2,
> +			     std::vector<int32_t>::iterator fds_it1,
> +			     std::vector<int32_t>::iterator fds_it2,
> +			     ControlSerializer *cs);

Where can I see this used ? As I wonder why this always take one
> +#endif /* __DOXYGEN__ */
> +};
> +
> +#ifndef __DOXYGEN__
> +
> +template<typename V>
> +class IPADataSerializer<std::vector<V>>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +		std::vector<int32_t> fds_vec;
> +
> +		// serialize the length

// ?

> +		uint32_t vec_len = data.size();
> +		appendUInt<uint32_t>(data_vec, vec_len);
> +
> +		// serialize the members
> +		for (auto it = data.begin(); it != data.end(); ++it) {
> +			std::vector<uint8_t> dvec;
> +			std::vector<int32_t> fvec;
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<V>::serialize(*it, cs);
> +
> +			appendUInt<uint32_t>(data_vec, dvec.size());
> +			appendUInt<uint32_t>(data_vec, fvec.size());
> +
> +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> +		}
> +
> +		return {data_vec, fds_vec};
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,
> +					  std::vector<uint8_t>::iterator it2,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<int32_t> fds;
> +		return IPADataSerializer<std::vector<V>>::deserialize(it1, it2,
> +								      fds.begin(), fds.end(),
> +								      cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),
> +								      fds.begin(), fds.end(),
> +								      cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,
> +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> +					  std::vector<int32_t>::iterator fds_it1,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		uint32_t vec_len = readUInt<uint32_t>(data_it1);
> +		std::vector<V> ret(vec_len);
> +
> +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> +		std::vector<int32_t>::iterator fd_it = fds_it1;
> +		for (uint32_t i = 0; i < vec_len; i++) {
> +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> +
> +			ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
> +								   data_it + 8 + sizeof_data,
> +								   fd_it,
> +								   fd_it + sizeof_fds,
> +								   cs);
> +
> +			data_it += 8 + sizeof_data;
> +			fd_it += sizeof_fds;
> +		}
> +
> +		return ret;
> +	}
> +};
> +
> +template<typename K, typename V>
> +class IPADataSerializer<std::map<K, V>>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +		std::vector<int32_t> fds_vec;
> +
> +		// serialize the length
> +		uint32_t map_len = data.size();
> +		appendUInt<uint32_t>(data_vec, map_len);
> +
> +		// serialize the members
> +		for (auto it = data.begin(); it != data.end(); ++it) {
> +			std::vector<uint8_t> dvec;
> +			std::vector<int32_t> fvec;
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<K>::serialize(it->first, cs);
> +
> +			appendUInt<uint32_t>(data_vec, dvec.size());
> +			appendUInt<uint32_t>(data_vec, fvec.size());
> +
> +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<V>::serialize(it->second, cs);
> +
> +			appendUInt<uint32_t>(data_vec, dvec.size());
> +			appendUInt<uint32_t>(data_vec, fvec.size());
> +
> +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> +		}
> +
> +		return {data_vec, fds_vec};
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,
> +					  std::vector<uint8_t>::iterator it2,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<int32_t> fds;
> +		return IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,
> +								      fds.begin(), fds.end(),
> +								      cs);
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),
> +								      fds.begin(), fds.end(),
> +								      cs);
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,
> +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> +					  std::vector<int32_t>::iterator fds_it1,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::map<K, V> ret;
> +
> +		uint32_t map_len = readUInt<uint32_t>(data_it1);
> +
> +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> +		std::vector<int32_t>::iterator fd_it = fds_it1;
> +		for (uint32_t i = 0; i < map_len; i++) {
> +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> +
> +			K key = IPADataSerializer<K>::deserialize(data_it + 8,
> +								  data_it + 8 + sizeof_data,
> +								  fd_it,
> +								  fd_it + sizeof_fds,
> +								  cs);
> +
> +			data_it += 8 + sizeof_data;
> +			fd_it += sizeof_fds;
> +			sizeof_data = readUInt<uint32_t>(data_it);
> +			sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> +
> +			const V value = IPADataSerializer<V>::deserialize(data_it + 8,
> +									  data_it + 8 + sizeof_data,
> +									  fd_it,
> +									  fd_it + sizeof_fds,
> +									  cs);
> +			ret.insert({key, value});
> +
> +			data_it += 8 + sizeof_data;
> +			fd_it += sizeof_fds;
> +		}
> +
> +		return ret;
> +	}
> +};
> +
> +#define DECLARE_INTEGRAL_SERIALIZER(type)				\
> +template<>								\
> +class IPADataSerializer<type>						\
> +{									\
> +public:									\
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>	\
> +	serialize(const type data,					\
> +		  [[maybe_unused]] ControlSerializer *cs = nullptr)	\
> +	{								\
> +		std::vector<uint8_t> data_vec;				\
> +		appendUInt<type>(data_vec, data);			\
> +									\
> +		return {data_vec, {}};					\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t> &data,		\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return IPADataSerializer<type>::deserialize(data.begin(),\
> +							    data.end());\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t>::iterator it1,	\
> +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return readUInt<type>(it1);				\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t> &data,		\
> +				[[maybe_unused]] std::vector<int32_t> &fds,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return IPADataSerializer<type>::deserialize(data.begin(),\
> +							    data.end());\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t>::iterator data_it1,\
> +				std::vector<uint8_t>::iterator data_it2,\
> +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\
> +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return IPADataSerializer<type>::deserialize(data_it1,	\
> +							    data_it2);	\
> +	}								\
> +};
> +
> +DECLARE_INTEGRAL_SERIALIZER(uint8_t)
> +DECLARE_INTEGRAL_SERIALIZER(uint16_t)
> +DECLARE_INTEGRAL_SERIALIZER(uint32_t)
> +DECLARE_INTEGRAL_SERIALIZER(uint64_t)
> +DECLARE_INTEGRAL_SERIALIZER(int8_t)
> +DECLARE_INTEGRAL_SERIALIZER(int16_t)
> +DECLARE_INTEGRAL_SERIALIZER(int32_t)
> +
> +template<>
> +class IPADataSerializer<int64_t>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const int64_t data,
> +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +		appendUInt<uint64_t>(data_vec, data);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static int64_t deserialize(std::vector<uint8_t> &data,
> +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> +							       data.end());
> +	}
> +
> +	static int64_t deserialize(std::vector<uint8_t>::iterator it1,
> +				   [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		if (std::distance(it1, it2) < 8) {
> +			LOG(IPADataSerializer, Info)
> +				<< "Not enough bytes to deserialize int64_t, returning 0";
> +			return 0;
> +		}
> +
> +		uint32_t lower = readUInt<uint32_t>(it1);
> +		uint32_t upper = readUInt<uint32_t>(it1 + 4);
> +
> +		return lower + (uint64_t(upper) << 32);
> +	}
> +
> +	static int64_t deserialize(std::vector<uint8_t> &data,
> +				   [[maybe_unused]] std::vector<int32_t> &fds,
> +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> +							       data.end());
> +	}
> +
> +	static int64_t deserialize(std::vector<uint8_t>::iterator data_it1,
> +				   std::vector<uint8_t>::iterator data_it2,
> +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);
> +	}
> +};

In what is the int64_t specialization different that the other ones
defined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you
don't have a  readUInt<int64_t> ?

> +
> +template<>
> +class IPADataSerializer<bool>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const bool data,
> +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +		appendUInt<uint8_t>(data_vec, data);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static bool deserialize(std::vector<uint8_t> &data,
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<bool>::deserialize(data.begin(),
> +							    data.end());
> +	}
> +
> +	static bool deserialize(std::vector<uint8_t>::iterator it1,
> +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return readUInt<uint8_t>(it1);

The sizeof(bool) seems to be a bit an issue actually... Can you solve
it by declarting a

template<> bool readUInt(std::vector<uint8_t>::iterator it)
?

Actually it seems to me the current implementation might work with
bool too...


> +	}
> +
> +	static bool deserialize(std::vector<uint8_t> &data,
> +				[[maybe_unused]] std::vector<int32_t> &fds,
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<bool>::deserialize(data.begin(),
> +							    data.end());
> +	}
> +
> +	static bool deserialize(std::vector<uint8_t>::iterator data_it1,
> +				std::vector<uint8_t>::iterator data_it2,
> +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<bool>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<float>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		uint8_t arr[4];
> +		memcpy(arr, &data, sizeof(arr));

I wonder if we should no be using memcpy in the readUInt() function
too. Would this help you with floats ?

> +
> +		std::vector<uint8_t> data_vec(arr, arr + 4);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static float deserialize(std::vector<uint8_t> &data,
> +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static float deserialize(std::vector<uint8_t>::iterator it1,
> +				 [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		if (std::distance(it1, it2) < 4) {
> +			LOG(IPADataSerializer, Info)
> +				<< "Not enough bytes to deserialize float, returning 0";
> +			return 0;
> +		}
> +
> +		std::vector data(it1, it2);
> +		float ret;
> +		memcpy(&ret, data.data(), sizeof(ret));
> +
> +		return ret;
> +	}
> +
> +	static float deserialize(std::vector<uint8_t> &data,
> +				 [[maybe_unused]] std::vector<int32_t> &fds,
> +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static float deserialize(std::vector<uint8_t>::iterator data_it1,
> +				 std::vector<uint8_t>::iterator data_it2,
> +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<float>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<double>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		uint8_t arr[8];
> +		memcpy(arr, &data, sizeof(arr));
> +
> +		std::vector<uint8_t> data_vec(arr, arr + 8);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static double deserialize(std::vector<uint8_t> &data,
> +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static double deserialize(std::vector<uint8_t>::iterator it1,
> +				  [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		if (std::distance(it1, it2) < 8) {
> +			LOG(IPADataSerializer, Info)
> +				<< "Not enough bytes to deserialize double, returning 0";
> +			return 0;
> +		}
> +
> +		std::vector data(it1, it2);
> +		double ret;
> +		memcpy(&ret, data.data(), sizeof(ret));
> +
> +		return ret;
> +	}
> +
> +	static double deserialize(std::vector<uint8_t> &data,
> +				  [[maybe_unused]] std::vector<int32_t> &fds,
> +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static double deserialize(std::vector<uint8_t>::iterator data_it1,
> +				  std::vector<uint8_t>::iterator data_it2,
> +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<double>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<std::string>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec(data.begin(), data.end());
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t>::iterator it1,
> +				       std::vector<uint8_t>::iterator it2,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::string str(it1, it2);
> +
> +		return str;
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] std::vector<int32_t> &fds,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t>::iterator data_it1,
> +				       std::vector<uint8_t>::iterator data_it2,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::string>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<FileDescriptor>

Ah, that's why you have the fds parameter in deserializer and the
std::vector<int32_t> in the serializer return parameter.

I think this would be cleaner if we could use enable_if and define an
ad-hoc function signature for FileDescriptor and ControlList. It would
remove the fds and cs parameters from all other types (the ones I've
see so far at least).

I think I'll stop here, there's enough to discuss on
Thanks
   j

> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec = { data.isValid() };
> +		std::vector<int32_t> fd_vec;
> +		if (data.isValid())
> +			fd_vec.push_back(data.fd());
> +
> +		return {data_vec, fd_vec};
> +	}
> +
> +	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),
> +								      fds.begin(), fds.end());
> +	}
> +
> +	static FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,
> +					  std::vector<uint8_t>::iterator data_it2,
> +					  std::vector<int32_t>::iterator fds_it1,
> +					  std::vector<int32_t>::iterator fds_it2,
> +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		if (std::distance(data_it1, data_it2) < 1)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "Invalid data to deserialize FileDescriptor";
> +
> +		bool valid = *data_it1;
> +
> +		if (valid && std::distance(fds_it1, fds_it2) < 1)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "Invalid fds to deserialize FileDescriptor";
> +
> +		return valid ? FileDescriptor(*fds_it1) : FileDescriptor();
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<IPASettings>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<std::string>::serialize(data.configurationFile);
> +	}
> +
> +	static IPASettings deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPASettings deserialize(std::vector<uint8_t>::iterator it1,
> +				       std::vector<uint8_t>::iterator it2,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		IPASettings ret;
> +		ret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);
> +
> +		return ret;
> +	}
> +
> +	static IPASettings deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] std::vector<int32_t> &fds,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,
> +				       std::vector<uint8_t>::iterator data_it2,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<CameraSensorInfo>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +
> +		uint32_t str_len = data.model.size();
> +		appendUInt<uint32_t>(data_vec, str_len);
> +
> +		data_vec.insert(data_vec.end(), data.model.begin(), data.model.end());
> +
> +		appendUInt<uint32_t>(data_vec, data.bitsPerPixel);
> +
> +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.width);
> +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.height);
> +
> +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));
> +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));
> +		appendUInt<uint32_t>(data_vec, data.analogCrop.width);
> +		appendUInt<uint32_t>(data_vec, data.analogCrop.height);
> +
> +		appendUInt<uint32_t>(data_vec, data.outputSize.width);
> +		appendUInt<uint32_t>(data_vec, data.outputSize.height);
> +
> +		appendUInt<uint64_t>(data_vec, data.pixelRate);
> +
> +		appendUInt<uint32_t>(data_vec, data.lineLength);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,
> +					    [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		CameraSensorInfo ret;
> +
> +		uint32_t str_len = readUInt<uint32_t>(it1);
> +		std::string str(it1 + 4, it1 + 4 + str_len);
> +		ret.model = str;
> +
> +		std::vector<uint8_t>::iterator it = it1 + 4 + str_len;
> +
> +		ret.bitsPerPixel = readUInt<uint32_t>(it);
> +
> +		ret.activeAreaSize.width = readUInt<uint32_t>(it + 4);
> +		ret.activeAreaSize.height = readUInt<uint32_t>(it + 8);
> +
> +		ret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));
> +		ret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));
> +		ret.analogCrop.width = readUInt<uint32_t>(it + 20);
> +		ret.analogCrop.height = readUInt<uint32_t>(it + 24);
> +
> +		ret.outputSize.width = readUInt<uint32_t>(it + 28);
> +		ret.outputSize.height = readUInt<uint32_t>(it + 32);
> +
> +		ret.pixelRate = readUInt<uint64_t>(it + 36);
> +
> +		ret.lineLength = readUInt<uint64_t>(it + 44);
> +
> +		return ret;
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> +					    [[maybe_unused]] std::vector<int32_t> &fds,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,
> +					    std::vector<uint8_t>::iterator data_it2,
> +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<IPAStream>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +
> +		appendUInt<uint32_t>(data_vec, data.pixelFormat);
> +
> +		appendUInt<uint32_t>(data_vec, data.size.width);
> +		appendUInt<uint32_t>(data_vec, data.size.height);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t> &data,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t>::iterator it1,
> +				     [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		IPAStream ret;
> +
> +		ret.pixelFormat = readUInt<uint32_t>(it1);
> +
> +		ret.size.width = readUInt<uint32_t>(it1 + 4);
> +		ret.size.height = readUInt<uint32_t>(it1 + 8);
> +
> +		return ret;
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t> &data,
> +				     [[maybe_unused]] std::vector<int32_t> &fds,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,
> +				     std::vector<uint8_t>::iterator data_it2,
> +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<const ControlList>
> +{
> +public:
> +	// map arg will be generated, since it's per-pipeline anyway
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const ControlList &data, const ControlInfoMap &map,
> +		  ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for serialization of ControlList";
> +
> +		size_t size = cs->binarySize(map);
> +		std::vector<uint8_t> infoData(size);
> +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> +		int ret = cs->serialize(map, buffer);
> +
> +		if (ret < 0 || buffer.overflow()) {
> +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap";
> +			return {{}, {}};
> +		}
> +
> +		size = cs->binarySize(data);
> +		std::vector<uint8_t> listData(size);
> +		buffer = ByteStreamBuffer(listData.data(), listData.size());
> +		ret = cs->serialize(data, buffer);
> +
> +		if (ret < 0 || buffer.overflow()) {
> +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList";
> +			return {{}, {}};
> +		}
> +
> +		std::vector<uint8_t> data_vec;
> +		appendUInt<uint32_t>(data_vec, infoData.size());
> +		appendUInt<uint32_t>(data_vec, listData.size());
> +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> +		data_vec.insert(data_vec.end(), listData.begin(), listData.end());
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t>::iterator it1,
> +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				       ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for deserialization of ControlList";
> +
> +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> +		uint32_t listDataSize = readUInt<uint32_t>(it1 + 4);
> +
> +		std::vector<uint8_t>::iterator it = it1 + 8;
> +
> +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> +		std::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);
> +
> +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> +		ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> +		/* It's fine if map is empty. */
> +		if (buffer.overflow()) {
> +			LOG(IPADataSerializer, Error)
> +				<< "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow";
> +			return ControlList();
> +		}
> +
> +		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());
> +		ControlList list = cs->deserialize<ControlList>(buffer);
> +		if (buffer.overflow())
> +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow";
> +		if (list.empty())
> +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: empty list";
> +
> +		return list;
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] std::vector<int32_t> &fds,
> +				       ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> +				       std::vector<uint8_t>::iterator data_it2,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				       ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<ControlList>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const ControlList &data, const ControlInfoMap &map,
> +		  ControlSerializer *cs)
> +	{
> +		const ControlList &list_const = const_cast<const ControlList &>(data);
> +
> +		std::vector<uint8_t> data_vec;
> +		std::tie(data_vec, std::ignore) =
> +			IPADataSerializer<const ControlList>::serialize(list_const, map, cs);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static ControlList deserialize(std::vector<uint8_t> &data,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(data, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlList deserialize(std::vector<uint8_t>::iterator it1,
> +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(it1, it2, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlList deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] std::vector<int32_t> &fds,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(data, fds, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> +				       std::vector<uint8_t>::iterator data_it2,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,
> +									  fds_it1, fds_it2, cs);
> +
> +		return ret;
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<const ControlInfoMap>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const ControlInfoMap &map, ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for serialization of ControlInfoMap";
> +
> +		size_t size = cs->binarySize(map);
> +		std::vector<uint8_t> infoData(size);
> +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> +		int ret = cs->serialize(map, buffer);
> +
> +		if (ret < 0 || buffer.overflow())
> +			return {{}, {}};
> +
> +		std::vector<uint8_t> data_vec;
> +		appendUInt<uint32_t>(data_vec, infoData.size());
> +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> +						ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +						ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
> +
> +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> +
> +		std::vector<uint8_t>::iterator it = it1 + 4;
> +
> +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> +
> +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> +		const ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> +
> +		return map;
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> +						[[maybe_unused]] std::vector<int32_t> &fds,
> +						ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> +					  std::vector<uint8_t>::iterator data_it2,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					  ControlSerializer *cs)
> +	{
> +		return IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<ControlInfoMap>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs)
> +	{
> +		const ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);
> +
> +		std::vector<uint8_t> data_vec;
> +		std::tie(data_vec, std::ignore) =
> +			IPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);
> +
> +		return {data_vec, {}};
> +	}
> +
> +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> +					  ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(data, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> +						ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> +						[[maybe_unused]] std::vector<int32_t> &fds,
> +						ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);
> +
> +		return ret;
> +	}
> +
> +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> +					  std::vector<uint8_t>::iterator data_it2,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					  ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,
> +									     fds_it1, fds_it2, cs);
> +
> +		return ret;
> +	}
> +};
> +
> +template<>
> +class IPADataSerializer<FrameBuffer::Plane>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +		std::vector<int32_t> fds_vec;
> +
> +		// fd
> +		std::vector<uint8_t> fdBuf;
> +		std::vector<int32_t> fdFds;
> +		std::tie(fdBuf, fdFds) =
> +			IPADataSerializer<FileDescriptor>::serialize(data.fd);
> +		data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());
> +		fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());
> +
> +		// length
> +		appendUInt<uint32_t>(data_vec, data.length);
> +
> +		return {data_vec, fds_vec};
> +	}
> +
> +	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
> +					      std::vector<int32_t> &fds,
> +					      ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),
> +									  fds.begin(), fds.end(),
> +									  cs);
> +	}
> +
> +	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,
> +					      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> +					      std::vector<int32_t>::iterator fds_it1,
> +					      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> +					      [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		FrameBuffer::Plane ret;
> +
> +		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,
> +									fds_it1, fds_it1 + 1);
> +		ret.length = readUInt<uint32_t>(data_it1 + 1);
> +
> +		return ret;
> +	}
> +};
> +
> +
> +template<>
> +class IPADataSerializer<IPABuffer>
> +{
> +public:
> +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> +	serialize(const IPABuffer &data, ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<uint8_t> data_vec;
> +
> +		appendUInt<uint32_t>(data_vec, data.id);
> +
> +		std::vector<uint8_t> planes_data_vec;
> +		std::vector<int32_t> planes_fds_vec;
> +		std::tie(planes_data_vec, planes_fds_vec) =
> +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
> +
> +		data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());
> +
> +		return {data_vec, planes_fds_vec};
> +	}
> +
> +	static IPABuffer deserialize(std::vector<uint8_t> &data,
> +				     std::vector<int32_t> &fds,
> +				     ControlSerializer *cs = nullptr)
> +	{
> +		return IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),
> +								 fds.begin(), fds.end(), cs);
> +	}
> +
> +	static IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,
> +				     std::vector<uint8_t>::iterator data_it2,
> +				     std::vector<int32_t>::iterator fds_it1,
> +				     std::vector<int32_t>::iterator fds_it2,
> +				     ControlSerializer *cs = nullptr)
> +	{
> +		IPABuffer ret;
> +
> +		ret.id = readUInt<uint32_t>(data_it1);
> +
> +		ret.planes =
> +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
> +				data_it1 + 4, data_it2, fds_it1, fds_it2, cs);
> +
> +		return ret;
> +	}
> +};
> +
> +#endif /* __DOXYGEN__ */
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */
> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> new file mode 100644
> index 00000000..5029cdf6
> --- /dev/null
> +++ b/src/libcamera/ipa_data_serializer.cpp
> @@ -0,0 +1,154 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2020, Google Inc.
> + *
> + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
> + */
> +
> +#include "libcamera/internal/ipa_data_serializer.h"
> +
> +#include "libcamera/internal/log.h"
> +
> +/**
> + * \file ipa_data_serializer.h
> + * \brief IPA Data Serializer
> + */
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(IPADataSerializer)
> +
> +/**
> + * \class IPADataSerializer
> + * \brief IPA Data Serializer
> + *
> + * Static template class that provides functions for serializing and
> + * deserializing IPA data.
> + */
> +
> +/**
> + * \fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)
> + * \brief Append uint to end of byte vector, in little-endian order
> + * \tparam T Type of uint to append
> + * \param[in] vec Byte vector to append to
> + * \param[in] val Value of uint to append
> + */
> +
> +/**
> + * \fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)
> + * \brief Read uint from byte vector, in little-endian order
> + * \tparam T Type of uint to read
> + * \param[in] vec Byte vector to read from
> + * \param[in] pos Index in vec to start reading from
> + *
> + * \return The uint read from \a vec, or 0 if reading goes past end of \a vec
> + */
> +
> +/**
> + * \fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)
> + * \brief Read uint from byte vector, in little-endian order
> + * \tparam T Type of uint to read
> + * \param[in] it Iterator of byte vector to read from
> + *
> + * \return The uint read from \a vec
> + */
> +
> +/**
> + * \fn template<typename T> IPADataSerializer<T>::serialize(
> + * 	T data,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Serialize an object into byte vector and fd vector
> + * \tparam T Type of object to serialize
> + * \param[in] data Object to serialize
> + * \param[in] cs ControlSerializer
> + *
> + * \a cs is only necessary if the object type \a T or its members contain
> + * ControlList or ControlInfoMap.
> + *
> + * \return Tuple of byte vector and fd vector, that is the serialized form
> + * of \a data
> + */
> +
> +/**
> + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> + * 	std::vector<uint8_t> &data,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] data Byte vector to deserialize from
> + * \param[in] cs ControlSerializer
> + *
> + * This version of deserialize() can be used if the object type \a T and its
> + * members don't have any FileDescriptor.
> + *
> + * \a cs is only necessary if the object type \a T or its members contain
> + * ControlList or ControlInfoMap.
> + *
> + * \return The deserialized object
> + */
> +
> +/**
> + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> + * 	std::vector<uint8_t>::iterator it1,
> + * 	std::vector<uint8_t>::iterator it2,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] it1 Begin iterator of byte vector to deserialize from
> + * \param[in] it2 End iterator of byte vector to deserialize from
> + * \param[in] cs ControlSerializer
> + *
> + * This version of deserialize() can be used if the object type \a T and its
> + * members don't have any FileDescriptor.
> + *
> + * \a cs is only necessary if the object type \a T or its members contain
> + * ControlList or ControlInfoMap.
> + *
> + * \return The deserialized object
> + */
> +
> +/**
> + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> + * 	std::vector<uint8_t> &data,
> + * 	std::vector<int32_t> &fds,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector and fd vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] data Byte vector to deserialize from
> + * \param[in] fds Fd vector to deserialize from
> + * \param[in] cs ControlSerializer
> + *
> + * This version of deserialize() (or the iterator version) must be used if
> + * the object type \a T or its members contain FileDescriptor.
> + *
> + * \a cs is only necessary if the object type \a T or its members contain
> + * ControlList or ControlInfoMap.
> + *
> + * \return The deserialized object
> + */
> +
> +/**
> + * \fn template<typename T> IPADataSerializer::deserialize(
> + * 	std::vector<uint8_t>::iterator data_it1,
> + * 	std::vector<uint8_t>::iterator data_it2,
> + * 	std::vector<int32_t>::iterator fds_it1,
> + * 	std::vector<int32_t>::iterator fds_it2,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector and fd vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] data_it1 Begin iterator of byte vector to deserialize from
> + * \param[in] data_it2 End iterator of byte vector to deserialize from
> + * \param[in] fds_it1 Begin iterator of fd vector to deserialize from
> + * \param[in] fds_it2 End iterator of fd vector to deserialize from
> + * \param[in] cs ControlSerializer
> + *
> + * This version of deserialize() (or the vector version) must be used if
> + * the object type \a T or its members contain FileDescriptor.
> + *
> + * \a cs is only necessary if the object type \a T or its members contain
> + * ControlList or ControlInfoMap.
> + *
> + * \return The deserialized object
> + */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 9d7442fa..61aad08e 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -23,6 +23,7 @@ libcamera_sources = files([
>      'geometry.cpp',
>      'ipa_context_wrapper.cpp',
>      'ipa_controls.cpp',
> +    'ipa_data_serializer.cpp',
>      'ipa_interface.cpp',
>      'ipa_manager.cpp',
>      'ipa_module.cpp',
> --
> 2.27.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Paul Elder Sept. 24, 2020, 7:17 a.m. UTC | #2
Hi Jacopo,

On Wed, Sep 23, 2020 at 05:32:18PM +0200, Jacopo Mondi wrote:
> Hi Paul,
> 
> On Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:
> > Add an IPADataSerializer which implments de/serialization of built-in
> 
> With built-in do you mean STL defined data types ?

Not quite. Just PODs, vector, map, and string. I'll specify them.

> > and libcamera data structures. This is intended to be used by the proxy
> > and the proxy worker in the IPC layer.
> >
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> >
> > ---
> > Changes in v2:
> > - added serializers for all integer types, bool, and string
> > - use string serializer for IPASettings serializer
> > - add documentation
> > - add serializer for const ControlList
> > ---
> >  .../libcamera/internal/ipa_data_serializer.h  | 1220 +++++++++++++++++
> >  src/libcamera/ipa_data_serializer.cpp         |  154 +++
> >  src/libcamera/meson.build                     |    1 +
> >  3 files changed, 1375 insertions(+)
> >  create mode 100644 include/libcamera/internal/ipa_data_serializer.h
> >  create mode 100644 src/libcamera/ipa_data_serializer.cpp
> >
> > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> > new file mode 100644
> > index 00000000..accd4dce
> > --- /dev/null
> > +++ b/include/libcamera/internal/ipa_data_serializer.h
> > @@ -0,0 +1,1220 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2020, Google Inc.
> > + *
> > + * ipa_data_serializer.h - Image Processing Algorithm data serializer
> > + */
> > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> > +
> > +#include <deque>
> > +#include <iostream>
> > +#include <tuple>
> > +#include <vector>
> > +
> > +#include <libcamera/buffer.h>
> > +#include <libcamera/control_ids.h>
> > +#include <libcamera/geometry.h>
> > +#include <libcamera/ipa/ipa_interface.h>
> > +
> > +#include "libcamera/internal/byte_stream_buffer.h"
> > +#include "libcamera/internal/camera_sensor.h"
> > +#include "libcamera/internal/control_serializer.h"
> > +#include "libcamera/internal/log.h"
> > +
> > +namespace libcamera {
> > +
> > +LOG_DECLARE_CATEGORY(IPADataSerializer)
> > +
> > +template<typename T>
> > +void appendUInt(std::vector<uint8_t> &vec, T val)
> > +{
> > +	size_t byteWidth = sizeof(val);
> > +	for (size_t i = 0; i < byteWidth; i++)
> > +		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));
> 
> I see in several places missing spaces around the '*' operator
>
> I think you can also drop the & 0xff as it's implied in the cast to
> uint8_t?

I ended up replacing this (and readUInt) with memcpys, as suggested with
the floats. That allows us to have DECLARE_POD_SERIALIZER for bool,
float, double, as well as all of the ints and uints :)

> > +}
> > +
> > +template<typename T>
> > +T readUInt(std::vector<uint8_t> &vec, size_t pos)
> > +{
> > +	T ret = 0;
> > +	size_t byteWidth = sizeof(ret);
> > +	if (pos + byteWidth > vec.size())
> > +		return ret;
> > +
> > +	for (size_t i = 0; i < byteWidth; i++)
> > +		ret |= vec[pos + i] << 8*i;
> 
> You could iterate the vector and call the below defined overload. Not
> a big deal, it would be probably more confusing.
> 
> > +	return ret;
> > +}
> > +
> > +template<typename T>
> > +T readUInt(std::vector<uint8_t>::iterator it)
> > +{
> > +	T ret = 0;
> > +	size_t byteWidth = sizeof(ret);
> > +	for (size_t i = 0; i < byteWidth; i++)
> > +		ret |= *(it + i) << 8*i;
> > +	return ret;
> > +}
> > +
> > +template<typename T>
> > +class IPADataSerializer
> > +{
> > +#ifdef __DOXYGEN__
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(T data, ControlSerializer *cs);
> > +
> > +	static T deserialize(std::vector<uint8_t> &data,
> > +			     ControlSerializer *cs);
> 
> That *cs parameter is unused in all specializations but ControlList
> one. We already had to face a similar issue when we implemented
> ControlValue. You could define two versions of these functions one for
> the ControlList case which has *cs in its signature, the other for all
> the other types, without cs. The construct to do so is not nice at a
> first look but effective:
> https://en.cppreference.com/w/cpp/types/enable_if
> 
> I think it could be applied here, maybe on top ?

I don't think we can quite do so. ControlSerializer and the fd functions
are necessary if any descendent of the struct needs it. This means a
simple enable_if won't we sufficient, and it would have to check the
encapsulation tree.

On the other hand, at code-generation time (for generating the
serializer), it is known which structs need ControlSerializer/fd, so in
theory we could prune the parameters and functions at that point. The
users of the serializers are also known at code-generation time, so the
calls can be matched as well.

The problem is in the de/serializer for vector and map. For vector
serialization:

	IPADataSerializer<V>::serialize(*it, cs);

And for deserialization:

	ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
						   data_it + 8 + sizeof_data,
						   fd_it,
						   fd_it + sizeof_fds,
						   cs);

The vector and map de/serializers don't know if the struct that they
contain needs the ControlSerializer/fd, that's why we have to pass it in
regardless, and that's why all IPADataSerializers must accept them as
parameters. enable_if might be useful here, but again that only works if
the struct that the vector/map contains is the one that needs the
ControlSerializer/fd; if the struct doesn't need it but its children do,
then we'd have to do nested enable_if.
	
> > +	static T deserialize(std::vector<uint8_t>::iterator it1,
> > +			     std::vector<uint8_t>::iterator it2,
> > +			     ControlSerializer *cs);
> > +
> > +	static T deserialize(std::vector<uint8_t> &data,
> > +			     std::vector<int32_t> &fds,
> > +			     ControlSerializer *cs);
> > +	static T deserialize(std::vector<uint8_t>::iterator data_it1,
> > +			     std::vector<uint8_t>::iterator data_it2,
> > +			     std::vector<int32_t>::iterator fds_it1,
> > +			     std::vector<int32_t>::iterator fds_it2,
> > +			     ControlSerializer *cs);
> 
> Where can I see this used ? As I wonder why this always take one

All of these functions are meant to be used by the proxy and the proxy
worker, so they can be seen in the template code :)

This last one can be seen in the generated code for the raspberrypi
proxy worker:

       	RPiConfigInput ipaConfig = IPADataSerializer<RPiConfigInput>::deserialize(
               	_message.data.begin() + ipaConfigStart,
               	_message.data.end(),
               	_message.fds.begin() + ipaConfigFdStart,
               	_message.fds.end());

Where _message is a IPCUnixSocket::Payload, which carries a data vector
and an fd vector.

> > +#endif /* __DOXYGEN__ */
> > +};
> > +
> > +#ifndef __DOXYGEN__
> > +
> > +template<typename V>
> > +class IPADataSerializer<std::vector<V>>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +		std::vector<int32_t> fds_vec;
> > +
> > +		// serialize the length
> 
> // ?

ack

> > +		uint32_t vec_len = data.size();
> > +		appendUInt<uint32_t>(data_vec, vec_len);
> > +
> > +		// serialize the members
> > +		for (auto it = data.begin(); it != data.end(); ++it) {
> > +			std::vector<uint8_t> dvec;
> > +			std::vector<int32_t> fvec;
> > +
> > +			std::tie(dvec, fvec) =
> > +				IPADataSerializer<V>::serialize(*it, cs);
> > +
> > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > +
> > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > +		}
> > +
> > +		return {data_vec, fds_vec};
> > +	}
> > +
> > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,
> > +					  std::vector<uint8_t>::iterator it2,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<int32_t> fds;
> > +		return IPADataSerializer<std::vector<V>>::deserialize(it1, it2,
> > +								      fds.begin(), fds.end(),
> > +								      cs);
> > +	}
> > +
> > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),
> > +								      fds.begin(), fds.end(),
> > +								      cs);
> > +	}
> > +
> > +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > +					  std::vector<int32_t>::iterator fds_it1,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		uint32_t vec_len = readUInt<uint32_t>(data_it1);
> > +		std::vector<V> ret(vec_len);
> > +
> > +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> > +		std::vector<int32_t>::iterator fd_it = fds_it1;
> > +		for (uint32_t i = 0; i < vec_len; i++) {
> > +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> > +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > +
> > +			ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
> > +								   data_it + 8 + sizeof_data,
> > +								   fd_it,
> > +								   fd_it + sizeof_fds,
> > +								   cs);
> > +
> > +			data_it += 8 + sizeof_data;
> > +			fd_it += sizeof_fds;
> > +		}
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +template<typename K, typename V>
> > +class IPADataSerializer<std::map<K, V>>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +		std::vector<int32_t> fds_vec;
> > +
> > +		// serialize the length
> > +		uint32_t map_len = data.size();
> > +		appendUInt<uint32_t>(data_vec, map_len);
> > +
> > +		// serialize the members
> > +		for (auto it = data.begin(); it != data.end(); ++it) {
> > +			std::vector<uint8_t> dvec;
> > +			std::vector<int32_t> fvec;
> > +
> > +			std::tie(dvec, fvec) =
> > +				IPADataSerializer<K>::serialize(it->first, cs);
> > +
> > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > +
> > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > +
> > +			std::tie(dvec, fvec) =
> > +				IPADataSerializer<V>::serialize(it->second, cs);
> > +
> > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > +
> > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > +		}
> > +
> > +		return {data_vec, fds_vec};
> > +	}
> > +
> > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,
> > +					  std::vector<uint8_t>::iterator it2,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<int32_t> fds;
> > +		return IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,
> > +								      fds.begin(), fds.end(),
> > +								      cs);
> > +	}
> > +
> > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),
> > +								      fds.begin(), fds.end(),
> > +								      cs);
> > +	}
> > +
> > +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > +					  std::vector<int32_t>::iterator fds_it1,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					  ControlSerializer *cs = nullptr)
> > +	{
> > +		std::map<K, V> ret;
> > +
> > +		uint32_t map_len = readUInt<uint32_t>(data_it1);
> > +
> > +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> > +		std::vector<int32_t>::iterator fd_it = fds_it1;
> > +		for (uint32_t i = 0; i < map_len; i++) {
> > +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> > +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > +
> > +			K key = IPADataSerializer<K>::deserialize(data_it + 8,
> > +								  data_it + 8 + sizeof_data,
> > +								  fd_it,
> > +								  fd_it + sizeof_fds,
> > +								  cs);
> > +
> > +			data_it += 8 + sizeof_data;
> > +			fd_it += sizeof_fds;
> > +			sizeof_data = readUInt<uint32_t>(data_it);
> > +			sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > +
> > +			const V value = IPADataSerializer<V>::deserialize(data_it + 8,
> > +									  data_it + 8 + sizeof_data,
> > +									  fd_it,
> > +									  fd_it + sizeof_fds,
> > +									  cs);
> > +			ret.insert({key, value});
> > +
> > +			data_it += 8 + sizeof_data;
> > +			fd_it += sizeof_fds;
> > +		}
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +#define DECLARE_INTEGRAL_SERIALIZER(type)				\
> > +template<>								\
> > +class IPADataSerializer<type>						\
> > +{									\
> > +public:									\
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>	\
> > +	serialize(const type data,					\
> > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)	\
> > +	{								\
> > +		std::vector<uint8_t> data_vec;				\
> > +		appendUInt<type>(data_vec, data);			\
> > +									\
> > +		return {data_vec, {}};					\
> > +	}								\
> > +									\
> > +	static type deserialize(std::vector<uint8_t> &data,		\
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > +	{								\
> > +		return IPADataSerializer<type>::deserialize(data.begin(),\
> > +							    data.end());\
> > +	}								\
> > +									\
> > +	static type deserialize(std::vector<uint8_t>::iterator it1,	\
> > +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,\
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > +	{								\
> > +		return readUInt<type>(it1);				\
> > +	}								\
> > +									\
> > +	static type deserialize(std::vector<uint8_t> &data,		\
> > +				[[maybe_unused]] std::vector<int32_t> &fds,\
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > +	{								\
> > +		return IPADataSerializer<type>::deserialize(data.begin(),\
> > +							    data.end());\
> > +	}								\
> > +									\
> > +	static type deserialize(std::vector<uint8_t>::iterator data_it1,\
> > +				std::vector<uint8_t>::iterator data_it2,\
> > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\
> > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > +	{								\
> > +		return IPADataSerializer<type>::deserialize(data_it1,	\
> > +							    data_it2);	\
> > +	}								\
> > +};
> > +
> > +DECLARE_INTEGRAL_SERIALIZER(uint8_t)
> > +DECLARE_INTEGRAL_SERIALIZER(uint16_t)
> > +DECLARE_INTEGRAL_SERIALIZER(uint32_t)
> > +DECLARE_INTEGRAL_SERIALIZER(uint64_t)
> > +DECLARE_INTEGRAL_SERIALIZER(int8_t)
> > +DECLARE_INTEGRAL_SERIALIZER(int16_t)
> > +DECLARE_INTEGRAL_SERIALIZER(int32_t)
> > +
> > +template<>
> > +class IPADataSerializer<int64_t>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const int64_t data,
> > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +		appendUInt<uint64_t>(data_vec, data);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static int64_t deserialize(std::vector<uint8_t> &data,
> > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> > +							       data.end());
> > +	}
> > +
> > +	static int64_t deserialize(std::vector<uint8_t>::iterator it1,
> > +				   [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		if (std::distance(it1, it2) < 8) {
> > +			LOG(IPADataSerializer, Info)
> > +				<< "Not enough bytes to deserialize int64_t, returning 0";
> > +			return 0;
> > +		}
> > +
> > +		uint32_t lower = readUInt<uint32_t>(it1);
> > +		uint32_t upper = readUInt<uint32_t>(it1 + 4);
> > +
> > +		return lower + (uint64_t(upper) << 32);
> > +	}
> > +
> > +	static int64_t deserialize(std::vector<uint8_t> &data,
> > +				   [[maybe_unused]] std::vector<int32_t> &fds,
> > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> > +							       data.end());
> > +	}
> > +
> > +	static int64_t deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				   std::vector<uint8_t>::iterator data_it2,
> > +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> 
> In what is the int64_t specialization different that the other ones
> defined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you
> don't have a  readUInt<int64_t> ?

Yeah... I was having bit shifting problems with readUInt<int64_t>.
I reimplemented it with memcpy though, and now the macro can generate
code for serializing int64_t, bool, float, and double as well :)

> > +
> > +template<>
> > +class IPADataSerializer<bool>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const bool data,
> > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +		appendUInt<uint8_t>(data_vec, data);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static bool deserialize(std::vector<uint8_t> &data,
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<bool>::deserialize(data.begin(),
> > +							    data.end());
> > +	}
> > +
> > +	static bool deserialize(std::vector<uint8_t>::iterator it1,
> > +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return readUInt<uint8_t>(it1);
> 
> The sizeof(bool) seems to be a bit an issue actually... Can you solve
> it by declarting a
>
> template<> bool readUInt(std::vector<uint8_t>::iterator it)
> ?
>
> Actually it seems to me the current implementation might work with
> bool too...
>
> > +	}
> > +
> > +	static bool deserialize(std::vector<uint8_t> &data,
> > +				[[maybe_unused]] std::vector<int32_t> &fds,
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<bool>::deserialize(data.begin(),
> > +							    data.end());
> > +	}
> > +
> > +	static bool deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				std::vector<uint8_t>::iterator data_it2,
> > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<bool>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<float>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		uint8_t arr[4];
> > +		memcpy(arr, &data, sizeof(arr));
> 
> I wonder if we should no be using memcpy in the readUInt() function
> too. Would this help you with floats ?

Apparently yes.


Thanks,

Paul
> > +
> > +		std::vector<uint8_t> data_vec(arr, arr + 4);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static float deserialize(std::vector<uint8_t> &data,
> > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static float deserialize(std::vector<uint8_t>::iterator it1,
> > +				 [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		if (std::distance(it1, it2) < 4) {
> > +			LOG(IPADataSerializer, Info)
> > +				<< "Not enough bytes to deserialize float, returning 0";
> > +			return 0;
> > +		}
> > +
> > +		std::vector data(it1, it2);
> > +		float ret;
> > +		memcpy(&ret, data.data(), sizeof(ret));
> > +
> > +		return ret;
> > +	}
> > +
> > +	static float deserialize(std::vector<uint8_t> &data,
> > +				 [[maybe_unused]] std::vector<int32_t> &fds,
> > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static float deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				 std::vector<uint8_t>::iterator data_it2,
> > +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<float>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<double>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		uint8_t arr[8];
> > +		memcpy(arr, &data, sizeof(arr));
> > +
> > +		std::vector<uint8_t> data_vec(arr, arr + 8);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static double deserialize(std::vector<uint8_t> &data,
> > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static double deserialize(std::vector<uint8_t>::iterator it1,
> > +				  [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		if (std::distance(it1, it2) < 8) {
> > +			LOG(IPADataSerializer, Info)
> > +				<< "Not enough bytes to deserialize double, returning 0";
> > +			return 0;
> > +		}
> > +
> > +		std::vector data(it1, it2);
> > +		double ret;
> > +		memcpy(&ret, data.data(), sizeof(ret));
> > +
> > +		return ret;
> > +	}
> > +
> > +	static double deserialize(std::vector<uint8_t> &data,
> > +				  [[maybe_unused]] std::vector<int32_t> &fds,
> > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static double deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				  std::vector<uint8_t>::iterator data_it2,
> > +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<double>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<std::string>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec(data.begin(), data.end());
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static std::string deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static std::string deserialize(std::vector<uint8_t>::iterator it1,
> > +				       std::vector<uint8_t>::iterator it2,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::string str(it1, it2);
> > +
> > +		return str;
> > +	}
> > +
> > +	static std::string deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static std::string deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				       std::vector<uint8_t>::iterator data_it2,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::string>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<FileDescriptor>
> 
> Ah, that's why you have the fds parameter in deserializer and the
> std::vector<int32_t> in the serializer return parameter.
> 
> I think this would be cleaner if we could use enable_if and define an
> ad-hoc function signature for FileDescriptor and ControlList. It would
> remove the fds and cs parameters from all other types (the ones I've
> see so far at least).
> 
> I think I'll stop here, there's enough to discuss on
> Thanks
>    j
> 
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec = { data.isValid() };
> > +		std::vector<int32_t> fd_vec;
> > +		if (data.isValid())
> > +			fd_vec.push_back(data.fd());
> > +
> > +		return {data_vec, fd_vec};
> > +	}
> > +
> > +	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),
> > +								      fds.begin(), fds.end());
> > +	}
> > +
> > +	static FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					  std::vector<uint8_t>::iterator data_it2,
> > +					  std::vector<int32_t>::iterator fds_it1,
> > +					  std::vector<int32_t>::iterator fds_it2,
> > +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		if (std::distance(data_it1, data_it2) < 1)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "Invalid data to deserialize FileDescriptor";
> > +
> > +		bool valid = *data_it1;
> > +
> > +		if (valid && std::distance(fds_it1, fds_it2) < 1)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "Invalid fds to deserialize FileDescriptor";
> > +
> > +		return valid ? FileDescriptor(*fds_it1) : FileDescriptor();
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<IPASettings>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<std::string>::serialize(data.configurationFile);
> > +	}
> > +
> > +	static IPASettings deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static IPASettings deserialize(std::vector<uint8_t>::iterator it1,
> > +				       std::vector<uint8_t>::iterator it2,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		IPASettings ret;
> > +		ret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static IPASettings deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				       std::vector<uint8_t>::iterator data_it2,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<CameraSensorInfo>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +
> > +		uint32_t str_len = data.model.size();
> > +		appendUInt<uint32_t>(data_vec, str_len);
> > +
> > +		data_vec.insert(data_vec.end(), data.model.begin(), data.model.end());
> > +
> > +		appendUInt<uint32_t>(data_vec, data.bitsPerPixel);
> > +
> > +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.width);
> > +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.height);
> > +
> > +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));
> > +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));
> > +		appendUInt<uint32_t>(data_vec, data.analogCrop.width);
> > +		appendUInt<uint32_t>(data_vec, data.analogCrop.height);
> > +
> > +		appendUInt<uint32_t>(data_vec, data.outputSize.width);
> > +		appendUInt<uint32_t>(data_vec, data.outputSize.height);
> > +
> > +		appendUInt<uint64_t>(data_vec, data.pixelRate);
> > +
> > +		appendUInt<uint32_t>(data_vec, data.lineLength);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,
> > +					    [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		CameraSensorInfo ret;
> > +
> > +		uint32_t str_len = readUInt<uint32_t>(it1);
> > +		std::string str(it1 + 4, it1 + 4 + str_len);
> > +		ret.model = str;
> > +
> > +		std::vector<uint8_t>::iterator it = it1 + 4 + str_len;
> > +
> > +		ret.bitsPerPixel = readUInt<uint32_t>(it);
> > +
> > +		ret.activeAreaSize.width = readUInt<uint32_t>(it + 4);
> > +		ret.activeAreaSize.height = readUInt<uint32_t>(it + 8);
> > +
> > +		ret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));
> > +		ret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));
> > +		ret.analogCrop.width = readUInt<uint32_t>(it + 20);
> > +		ret.analogCrop.height = readUInt<uint32_t>(it + 24);
> > +
> > +		ret.outputSize.width = readUInt<uint32_t>(it + 28);
> > +		ret.outputSize.height = readUInt<uint32_t>(it + 32);
> > +
> > +		ret.pixelRate = readUInt<uint64_t>(it + 36);
> > +
> > +		ret.lineLength = readUInt<uint64_t>(it + 44);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> > +					    [[maybe_unused]] std::vector<int32_t> &fds,
> > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					    std::vector<uint8_t>::iterator data_it2,
> > +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<IPAStream>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +
> > +		appendUInt<uint32_t>(data_vec, data.pixelFormat);
> > +
> > +		appendUInt<uint32_t>(data_vec, data.size.width);
> > +		appendUInt<uint32_t>(data_vec, data.size.height);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static IPAStream deserialize(std::vector<uint8_t> &data,
> > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static IPAStream deserialize(std::vector<uint8_t>::iterator it1,
> > +				     [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		IPAStream ret;
> > +
> > +		ret.pixelFormat = readUInt<uint32_t>(it1);
> > +
> > +		ret.size.width = readUInt<uint32_t>(it1 + 4);
> > +		ret.size.height = readUInt<uint32_t>(it1 + 8);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static IPAStream deserialize(std::vector<uint8_t> &data,
> > +				     [[maybe_unused]] std::vector<int32_t> &fds,
> > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> > +	}
> > +
> > +	static IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				     std::vector<uint8_t>::iterator data_it2,
> > +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<const ControlList>
> > +{
> > +public:
> > +	// map arg will be generated, since it's per-pipeline anyway
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const ControlList &data, const ControlInfoMap &map,
> > +		  ControlSerializer *cs)
> > +	{
> > +		if (!cs)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "ControlSerializer not provided for serialization of ControlList";
> > +
> > +		size_t size = cs->binarySize(map);
> > +		std::vector<uint8_t> infoData(size);
> > +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> > +		int ret = cs->serialize(map, buffer);
> > +
> > +		if (ret < 0 || buffer.overflow()) {
> > +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap";
> > +			return {{}, {}};
> > +		}
> > +
> > +		size = cs->binarySize(data);
> > +		std::vector<uint8_t> listData(size);
> > +		buffer = ByteStreamBuffer(listData.data(), listData.size());
> > +		ret = cs->serialize(data, buffer);
> > +
> > +		if (ret < 0 || buffer.overflow()) {
> > +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList";
> > +			return {{}, {}};
> > +		}
> > +
> > +		std::vector<uint8_t> data_vec;
> > +		appendUInt<uint32_t>(data_vec, infoData.size());
> > +		appendUInt<uint32_t>(data_vec, listData.size());
> > +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> > +		data_vec.insert(data_vec.end(), listData.begin(), listData.end());
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static const ControlList deserialize(std::vector<uint8_t>::iterator it1,
> > +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				       ControlSerializer *cs)
> > +	{
> > +		if (!cs)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "ControlSerializer not provided for deserialization of ControlList";
> > +
> > +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> > +		uint32_t listDataSize = readUInt<uint32_t>(it1 + 4);
> > +
> > +		std::vector<uint8_t>::iterator it = it1 + 8;
> > +
> > +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> > +		std::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);
> > +
> > +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> > +		ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> > +		/* It's fine if map is empty. */
> > +		if (buffer.overflow()) {
> > +			LOG(IPADataSerializer, Error)
> > +				<< "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow";
> > +			return ControlList();
> > +		}
> > +
> > +		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());
> > +		ControlList list = cs->deserialize<ControlList>(buffer);
> > +		if (buffer.overflow())
> > +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow";
> > +		if (list.empty())
> > +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: empty list";
> > +
> > +		return list;
> > +	}
> > +
> > +	static const ControlList deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > +				       ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				       std::vector<uint8_t>::iterator data_it2,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				       ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<ControlList>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const ControlList &data, const ControlInfoMap &map,
> > +		  ControlSerializer *cs)
> > +	{
> > +		const ControlList &list_const = const_cast<const ControlList &>(data);
> > +
> > +		std::vector<uint8_t> data_vec;
> > +		std::tie(data_vec, std::ignore) =
> > +			IPADataSerializer<const ControlList>::serialize(list_const, map, cs);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static ControlList deserialize(std::vector<uint8_t> &data,
> > +				       ControlSerializer *cs)
> > +	{
> > +		ControlList ret;
> > +		const ControlList &list = ret;
> > +		const_cast<ControlList &>(list) =
> > +			IPADataSerializer<const ControlList>::deserialize(data, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlList deserialize(std::vector<uint8_t>::iterator it1,
> > +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +				       ControlSerializer *cs)
> > +	{
> > +		ControlList ret;
> > +		const ControlList &list = ret;
> > +		const_cast<ControlList &>(list) =
> > +			IPADataSerializer<const ControlList>::deserialize(it1, it2, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlList deserialize(std::vector<uint8_t> &data,
> > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > +				       ControlSerializer *cs)
> > +	{
> > +		ControlList ret;
> > +		const ControlList &list = ret;
> > +		const_cast<ControlList &>(list) =
> > +			IPADataSerializer<const ControlList>::deserialize(data, fds, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				       std::vector<uint8_t>::iterator data_it2,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +				       ControlSerializer *cs)
> > +	{
> > +		ControlList ret;
> > +		const ControlList &list = ret;
> > +		const_cast<ControlList &>(list) =
> > +			IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,
> > +									  fds_it1, fds_it2, cs);
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<const ControlInfoMap>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const ControlInfoMap &map, ControlSerializer *cs)
> > +	{
> > +		if (!cs)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "ControlSerializer not provided for serialization of ControlInfoMap";
> > +
> > +		size_t size = cs->binarySize(map);
> > +		std::vector<uint8_t> infoData(size);
> > +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> > +		int ret = cs->serialize(map, buffer);
> > +
> > +		if (ret < 0 || buffer.overflow())
> > +			return {{}, {}};
> > +
> > +		std::vector<uint8_t> data_vec;
> > +		appendUInt<uint32_t>(data_vec, infoData.size());
> > +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > +						ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> > +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +						ControlSerializer *cs)
> > +	{
> > +		if (!cs)
> > +			LOG(IPADataSerializer, Fatal)
> > +				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
> > +
> > +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> > +
> > +		std::vector<uint8_t>::iterator it = it1 + 4;
> > +
> > +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> > +
> > +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> > +		const ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> > +
> > +		return map;
> > +	}
> > +
> > +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > +						[[maybe_unused]] std::vector<int32_t> &fds,
> > +						ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> > +	}
> > +
> > +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					  std::vector<uint8_t>::iterator data_it2,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					  ControlSerializer *cs)
> > +	{
> > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<ControlInfoMap>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs)
> > +	{
> > +		const ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);
> > +
> > +		std::vector<uint8_t> data_vec;
> > +		std::tie(data_vec, std::ignore) =
> > +			IPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);
> > +
> > +		return {data_vec, {}};
> > +	}
> > +
> > +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > +					  ControlSerializer *cs)
> > +	{
> > +		ControlInfoMap ret;
> > +		const ControlInfoMap &map = ret;
> > +		const_cast<ControlInfoMap &>(map) =
> > +			IPADataSerializer<const ControlInfoMap>::deserialize(data, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> > +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > +						ControlSerializer *cs)
> > +	{
> > +		ControlInfoMap ret;
> > +		const ControlInfoMap &map = ret;
> > +		const_cast<ControlInfoMap &>(map) =
> > +			IPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > +						[[maybe_unused]] std::vector<int32_t> &fds,
> > +						ControlSerializer *cs)
> > +	{
> > +		ControlInfoMap ret;
> > +		const ControlInfoMap &map = ret;
> > +		const_cast<ControlInfoMap &>(map) =
> > +			IPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);
> > +
> > +		return ret;
> > +	}
> > +
> > +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					  std::vector<uint8_t>::iterator data_it2,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					  ControlSerializer *cs)
> > +	{
> > +		ControlInfoMap ret;
> > +		const ControlInfoMap &map = ret;
> > +		const_cast<ControlInfoMap &>(map) =
> > +			IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,
> > +									     fds_it1, fds_it2, cs);
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +template<>
> > +class IPADataSerializer<FrameBuffer::Plane>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +		std::vector<int32_t> fds_vec;
> > +
> > +		// fd
> > +		std::vector<uint8_t> fdBuf;
> > +		std::vector<int32_t> fdFds;
> > +		std::tie(fdBuf, fdFds) =
> > +			IPADataSerializer<FileDescriptor>::serialize(data.fd);
> > +		data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());
> > +		fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());
> > +
> > +		// length
> > +		appendUInt<uint32_t>(data_vec, data.length);
> > +
> > +		return {data_vec, fds_vec};
> > +	}
> > +
> > +	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
> > +					      std::vector<int32_t> &fds,
> > +					      ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),
> > +									  fds.begin(), fds.end(),
> > +									  cs);
> > +	}
> > +
> > +	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,
> > +					      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > +					      std::vector<int32_t>::iterator fds_it1,
> > +					      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > +					      [[maybe_unused]] ControlSerializer *cs = nullptr)
> > +	{
> > +		FrameBuffer::Plane ret;
> > +
> > +		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,
> > +									fds_it1, fds_it1 + 1);
> > +		ret.length = readUInt<uint32_t>(data_it1 + 1);
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +
> > +template<>
> > +class IPADataSerializer<IPABuffer>
> > +{
> > +public:
> > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > +	serialize(const IPABuffer &data, ControlSerializer *cs = nullptr)
> > +	{
> > +		std::vector<uint8_t> data_vec;
> > +
> > +		appendUInt<uint32_t>(data_vec, data.id);
> > +
> > +		std::vector<uint8_t> planes_data_vec;
> > +		std::vector<int32_t> planes_fds_vec;
> > +		std::tie(planes_data_vec, planes_fds_vec) =
> > +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
> > +
> > +		data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());
> > +
> > +		return {data_vec, planes_fds_vec};
> > +	}
> > +
> > +	static IPABuffer deserialize(std::vector<uint8_t> &data,
> > +				     std::vector<int32_t> &fds,
> > +				     ControlSerializer *cs = nullptr)
> > +	{
> > +		return IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),
> > +								 fds.begin(), fds.end(), cs);
> > +	}
> > +
> > +	static IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,
> > +				     std::vector<uint8_t>::iterator data_it2,
> > +				     std::vector<int32_t>::iterator fds_it1,
> > +				     std::vector<int32_t>::iterator fds_it2,
> > +				     ControlSerializer *cs = nullptr)
> > +	{
> > +		IPABuffer ret;
> > +
> > +		ret.id = readUInt<uint32_t>(data_it1);
> > +
> > +		ret.planes =
> > +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
> > +				data_it1 + 4, data_it2, fds_it1, fds_it2, cs);
> > +
> > +		return ret;
> > +	}
> > +};
> > +
> > +#endif /* __DOXYGEN__ */
> > +
> > +} /* namespace libcamera */
> > +
> > +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */
> > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> > new file mode 100644
> > index 00000000..5029cdf6
> > --- /dev/null
> > +++ b/src/libcamera/ipa_data_serializer.cpp
> > @@ -0,0 +1,154 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2020, Google Inc.
> > + *
> > + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
> > + */
> > +
> > +#include "libcamera/internal/ipa_data_serializer.h"
> > +
> > +#include "libcamera/internal/log.h"
> > +
> > +/**
> > + * \file ipa_data_serializer.h
> > + * \brief IPA Data Serializer
> > + */
> > +
> > +namespace libcamera {
> > +
> > +LOG_DEFINE_CATEGORY(IPADataSerializer)
> > +
> > +/**
> > + * \class IPADataSerializer
> > + * \brief IPA Data Serializer
> > + *
> > + * Static template class that provides functions for serializing and
> > + * deserializing IPA data.
> > + */
> > +
> > +/**
> > + * \fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)
> > + * \brief Append uint to end of byte vector, in little-endian order
> > + * \tparam T Type of uint to append
> > + * \param[in] vec Byte vector to append to
> > + * \param[in] val Value of uint to append
> > + */
> > +
> > +/**
> > + * \fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)
> > + * \brief Read uint from byte vector, in little-endian order
> > + * \tparam T Type of uint to read
> > + * \param[in] vec Byte vector to read from
> > + * \param[in] pos Index in vec to start reading from
> > + *
> > + * \return The uint read from \a vec, or 0 if reading goes past end of \a vec
> > + */
> > +
> > +/**
> > + * \fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)
> > + * \brief Read uint from byte vector, in little-endian order
> > + * \tparam T Type of uint to read
> > + * \param[in] it Iterator of byte vector to read from
> > + *
> > + * \return The uint read from \a vec
> > + */
> > +
> > +/**
> > + * \fn template<typename T> IPADataSerializer<T>::serialize(
> > + * 	T data,
> > + * 	ControlSerializer *cs = nullptr)
> > + * \brief Serialize an object into byte vector and fd vector
> > + * \tparam T Type of object to serialize
> > + * \param[in] data Object to serialize
> > + * \param[in] cs ControlSerializer
> > + *
> > + * \a cs is only necessary if the object type \a T or its members contain
> > + * ControlList or ControlInfoMap.
> > + *
> > + * \return Tuple of byte vector and fd vector, that is the serialized form
> > + * of \a data
> > + */
> > +
> > +/**
> > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > + * 	std::vector<uint8_t> &data,
> > + * 	ControlSerializer *cs = nullptr)
> > + * \brief Deserialize byte vector into an object
> > + * \tparam T Type of object to deserialize to
> > + * \param[in] data Byte vector to deserialize from
> > + * \param[in] cs ControlSerializer
> > + *
> > + * This version of deserialize() can be used if the object type \a T and its
> > + * members don't have any FileDescriptor.
> > + *
> > + * \a cs is only necessary if the object type \a T or its members contain
> > + * ControlList or ControlInfoMap.
> > + *
> > + * \return The deserialized object
> > + */
> > +
> > +/**
> > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > + * 	std::vector<uint8_t>::iterator it1,
> > + * 	std::vector<uint8_t>::iterator it2,
> > + * 	ControlSerializer *cs = nullptr)
> > + * \brief Deserialize byte vector into an object
> > + * \tparam T Type of object to deserialize to
> > + * \param[in] it1 Begin iterator of byte vector to deserialize from
> > + * \param[in] it2 End iterator of byte vector to deserialize from
> > + * \param[in] cs ControlSerializer
> > + *
> > + * This version of deserialize() can be used if the object type \a T and its
> > + * members don't have any FileDescriptor.
> > + *
> > + * \a cs is only necessary if the object type \a T or its members contain
> > + * ControlList or ControlInfoMap.
> > + *
> > + * \return The deserialized object
> > + */
> > +
> > +/**
> > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > + * 	std::vector<uint8_t> &data,
> > + * 	std::vector<int32_t> &fds,
> > + * 	ControlSerializer *cs = nullptr)
> > + * \brief Deserialize byte vector and fd vector into an object
> > + * \tparam T Type of object to deserialize to
> > + * \param[in] data Byte vector to deserialize from
> > + * \param[in] fds Fd vector to deserialize from
> > + * \param[in] cs ControlSerializer
> > + *
> > + * This version of deserialize() (or the iterator version) must be used if
> > + * the object type \a T or its members contain FileDescriptor.
> > + *
> > + * \a cs is only necessary if the object type \a T or its members contain
> > + * ControlList or ControlInfoMap.
> > + *
> > + * \return The deserialized object
> > + */
> > +
> > +/**
> > + * \fn template<typename T> IPADataSerializer::deserialize(
> > + * 	std::vector<uint8_t>::iterator data_it1,
> > + * 	std::vector<uint8_t>::iterator data_it2,
> > + * 	std::vector<int32_t>::iterator fds_it1,
> > + * 	std::vector<int32_t>::iterator fds_it2,
> > + * 	ControlSerializer *cs = nullptr)
> > + * \brief Deserialize byte vector and fd vector into an object
> > + * \tparam T Type of object to deserialize to
> > + * \param[in] data_it1 Begin iterator of byte vector to deserialize from
> > + * \param[in] data_it2 End iterator of byte vector to deserialize from
> > + * \param[in] fds_it1 Begin iterator of fd vector to deserialize from
> > + * \param[in] fds_it2 End iterator of fd vector to deserialize from
> > + * \param[in] cs ControlSerializer
> > + *
> > + * This version of deserialize() (or the vector version) must be used if
> > + * the object type \a T or its members contain FileDescriptor.
> > + *
> > + * \a cs is only necessary if the object type \a T or its members contain
> > + * ControlList or ControlInfoMap.
> > + *
> > + * \return The deserialized object
> > + */
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > index 9d7442fa..61aad08e 100644
> > --- a/src/libcamera/meson.build
> > +++ b/src/libcamera/meson.build
> > @@ -23,6 +23,7 @@ libcamera_sources = files([
> >      'geometry.cpp',
> >      'ipa_context_wrapper.cpp',
> >      'ipa_controls.cpp',
> > +    'ipa_data_serializer.cpp',
> >      'ipa_interface.cpp',
> >      'ipa_manager.cpp',
> >      'ipa_module.cpp',
> > --
> > 2.27.0
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
Jacopo Mondi Sept. 24, 2020, 9:52 a.m. UTC | #3
Hi Paul,

On Thu, Sep 24, 2020 at 04:17:00PM +0900, paul.elder@ideasonboard.com wrote:
> Hi Jacopo,
>
> On Wed, Sep 23, 2020 at 05:32:18PM +0200, Jacopo Mondi wrote:
> > Hi Paul,
> >
> > On Tue, Sep 22, 2020 at 10:35:12PM +0900, Paul Elder wrote:
> > > Add an IPADataSerializer which implments de/serialization of built-in
> >
> > With built-in do you mean STL defined data types ?
>
> Not quite. Just PODs, vector, map, and string. I'll specify them.
>

Thanks, that makes it clear which types we support

> > > and libcamera data structures. This is intended to be used by the proxy
> > > and the proxy worker in the IPC layer.
> > >
> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> > >
> > > ---
> > > Changes in v2:
> > > - added serializers for all integer types, bool, and string
> > > - use string serializer for IPASettings serializer
> > > - add documentation
> > > - add serializer for const ControlList
> > > ---
> > >  .../libcamera/internal/ipa_data_serializer.h  | 1220 +++++++++++++++++
> > >  src/libcamera/ipa_data_serializer.cpp         |  154 +++
> > >  src/libcamera/meson.build                     |    1 +
> > >  3 files changed, 1375 insertions(+)
> > >  create mode 100644 include/libcamera/internal/ipa_data_serializer.h
> > >  create mode 100644 src/libcamera/ipa_data_serializer.cpp
> > >
> > > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
> > > new file mode 100644
> > > index 00000000..accd4dce
> > > --- /dev/null
> > > +++ b/include/libcamera/internal/ipa_data_serializer.h
> > > @@ -0,0 +1,1220 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2020, Google Inc.
> > > + *
> > > + * ipa_data_serializer.h - Image Processing Algorithm data serializer
> > > + */
> > > +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> > > +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
> > > +
> > > +#include <deque>
> > > +#include <iostream>
> > > +#include <tuple>
> > > +#include <vector>
> > > +
> > > +#include <libcamera/buffer.h>
> > > +#include <libcamera/control_ids.h>
> > > +#include <libcamera/geometry.h>
> > > +#include <libcamera/ipa/ipa_interface.h>
> > > +
> > > +#include "libcamera/internal/byte_stream_buffer.h"
> > > +#include "libcamera/internal/camera_sensor.h"
> > > +#include "libcamera/internal/control_serializer.h"
> > > +#include "libcamera/internal/log.h"
> > > +
> > > +namespace libcamera {
> > > +
> > > +LOG_DECLARE_CATEGORY(IPADataSerializer)
> > > +
> > > +template<typename T>
> > > +void appendUInt(std::vector<uint8_t> &vec, T val)
> > > +{
> > > +	size_t byteWidth = sizeof(val);
> > > +	for (size_t i = 0; i < byteWidth; i++)
> > > +		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));
> >
> > I see in several places missing spaces around the '*' operator
> >
> > I think you can also drop the & 0xff as it's implied in the cast to
> > uint8_t?
>
> I ended up replacing this (and readUInt) with memcpys, as suggested with
> the floats. That allows us to have DECLARE_POD_SERIALIZER for bool,
> float, double, as well as all of the ints and uints :)
>

Great!

> > > +}
> > > +
> > > +template<typename T>
> > > +T readUInt(std::vector<uint8_t> &vec, size_t pos)
> > > +{
> > > +	T ret = 0;
> > > +	size_t byteWidth = sizeof(ret);
> > > +	if (pos + byteWidth > vec.size())
> > > +		return ret;
> > > +
> > > +	for (size_t i = 0; i < byteWidth; i++)
> > > +		ret |= vec[pos + i] << 8*i;
> >
> > You could iterate the vector and call the below defined overload. Not
> > a big deal, it would be probably more confusing.
> >
> > > +	return ret;
> > > +}
> > > +
> > > +template<typename T>
> > > +T readUInt(std::vector<uint8_t>::iterator it)
> > > +{
> > > +	T ret = 0;
> > > +	size_t byteWidth = sizeof(ret);
> > > +	for (size_t i = 0; i < byteWidth; i++)
> > > +		ret |= *(it + i) << 8*i;
> > > +	return ret;
> > > +}
> > > +
> > > +template<typename T>
> > > +class IPADataSerializer
> > > +{
> > > +#ifdef __DOXYGEN__
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(T data, ControlSerializer *cs);
> > > +
> > > +	static T deserialize(std::vector<uint8_t> &data,
> > > +			     ControlSerializer *cs);
> >
> > That *cs parameter is unused in all specializations but ControlList
> > one. We already had to face a similar issue when we implemented
> > ControlValue. You could define two versions of these functions one for
> > the ControlList case which has *cs in its signature, the other for all
> > the other types, without cs. The construct to do so is not nice at a
> > first look but effective:
> > https://en.cppreference.com/w/cpp/types/enable_if
> >
> > I think it could be applied here, maybe on top ?
>
> I don't think we can quite do so. ControlSerializer and the fd functions
> are necessary if any descendent of the struct needs it. This means a
> simple enable_if won't we sufficient, and it would have to check the
> encapsulation tree.
>
> On the other hand, at code-generation time (for generating the
> serializer), it is known which structs need ControlSerializer/fd, so in
> theory we could prune the parameters and functions at that point. The
> users of the serializers are also known at code-generation time, so the
> calls can be matched as well.
>
> The problem is in the de/serializer for vector and map. For vector
> serialization:
>
> 	IPADataSerializer<V>::serialize(*it, cs);
>
> And for deserialization:
>
> 	ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
> 						   data_it + 8 + sizeof_data,
> 						   fd_it,
> 						   fd_it + sizeof_fds,
> 						   cs);
>
> The vector and map de/serializers don't know if the struct that they
> contain needs the ControlSerializer/fd, that's why we have to pass it in
> regardless, and that's why all IPADataSerializers must accept them as
> parameters. enable_if might be useful here, but again that only works if
> the struct that the vector/map contains is the one that needs the
> ControlSerializer/fd; if the struct doesn't need it but its children do,
> then we'd have to do nested enable_if.
>

As we have clarified, enable_if<> would only help you in overloading
on the return type of the 'serialize()' function.
For 'deserialize()' you could probably get away with overloads.

As you point out, the real issue is in the map/vector deserializer,
where you would need to define a specialization for FileDescriptor
(whose serialize() return type has to be a tuple, while for others
types it might just be data) and for ControlList/ControlInfoMap (whose
deserialze() has an additional cs parameter, while the other types is
not required).

Looking at the generated code for the IPADataSerializer<>
specialization for the custom IPA types you already have the notion of
when the fds member of the tuple returned by serialize is not needed
(as you place std::ignore there) and where the cs parameter to
deserialize() is not used (as it is marked with [[maybe_unused]]).

That said, I am not sure it is worth making a specialization of the
map/vector IPADataSerializer<> specialization, as this is generated
code and the API towards pipelines and IPAs is not impacted by the
additional parameter requirements.

All in all, I think what you have here is fine :)

> > > +	static T deserialize(std::vector<uint8_t>::iterator it1,
> > > +			     std::vector<uint8_t>::iterator it2,
> > > +			     ControlSerializer *cs);
> > > +
> > > +	static T deserialize(std::vector<uint8_t> &data,
> > > +			     std::vector<int32_t> &fds,
> > > +			     ControlSerializer *cs);
> > > +	static T deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +			     std::vector<uint8_t>::iterator data_it2,
> > > +			     std::vector<int32_t>::iterator fds_it1,
> > > +			     std::vector<int32_t>::iterator fds_it2,
> > > +			     ControlSerializer *cs);
> >
> > Where can I see this used ? As I wonder why this always take one

Seems like I left the question half written...
I think what I wanted to ask was if all of these methods needs to be
made public...

>
> All of these functions are meant to be used by the proxy and the proxy
> worker, so they can be seen in the template code :)
>
> This last one can be seen in the generated code for the raspberrypi
> proxy worker:
>
>        	RPiConfigInput ipaConfig = IPADataSerializer<RPiConfigInput>::deserialize(
>                	_message.data.begin() + ipaConfigStart,
>                	_message.data.end(),
>                	_message.fds.begin() + ipaConfigFdStart,
>                	_message.fds.end());

But looking at this example, it seems the answer is 'yes'

>
> Where _message is a IPCUnixSocket::Payload, which carries a data vector
> and an fd vector.
>
> > > +#endif /* __DOXYGEN__ */
> > > +};
> > > +
> > > +#ifndef __DOXYGEN__
> > > +
> > > +template<typename V>
> > > +class IPADataSerializer<std::vector<V>>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +		std::vector<int32_t> fds_vec;
> > > +
> > > +		// serialize the length
> >
> > // ?
>
> ack
>
> > > +		uint32_t vec_len = data.size();
> > > +		appendUInt<uint32_t>(data_vec, vec_len);
> > > +
> > > +		// serialize the members
> > > +		for (auto it = data.begin(); it != data.end(); ++it) {
> > > +			std::vector<uint8_t> dvec;
> > > +			std::vector<int32_t> fvec;
> > > +
> > > +			std::tie(dvec, fvec) =
> > > +				IPADataSerializer<V>::serialize(*it, cs);
> > > +
> > > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > > +
> > > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > > +		}
> > > +
> > > +		return {data_vec, fds_vec};
> > > +	}
> > > +
> > > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,
> > > +					  std::vector<uint8_t>::iterator it2,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<int32_t> fds;
> > > +		return IPADataSerializer<std::vector<V>>::deserialize(it1, it2,
> > > +								      fds.begin(), fds.end(),
> > > +								      cs);
> > > +	}
> > > +
> > > +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),
> > > +								      fds.begin(), fds.end(),
> > > +								      cs);
> > > +	}
> > > +
> > > +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > > +					  std::vector<int32_t>::iterator fds_it1,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		uint32_t vec_len = readUInt<uint32_t>(data_it1);
> > > +		std::vector<V> ret(vec_len);
> > > +
> > > +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> > > +		std::vector<int32_t>::iterator fd_it = fds_it1;
> > > +		for (uint32_t i = 0; i < vec_len; i++) {
> > > +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> > > +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > > +
> > > +			ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
> > > +								   data_it + 8 + sizeof_data,
> > > +								   fd_it,
> > > +								   fd_it + sizeof_fds,
> > > +								   cs);
> > > +
> > > +			data_it += 8 + sizeof_data;
> > > +			fd_it += sizeof_fds;
> > > +		}
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +template<typename K, typename V>
> > > +class IPADataSerializer<std::map<K, V>>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +		std::vector<int32_t> fds_vec;
> > > +
> > > +		// serialize the length
> > > +		uint32_t map_len = data.size();
> > > +		appendUInt<uint32_t>(data_vec, map_len);
> > > +
> > > +		// serialize the members
> > > +		for (auto it = data.begin(); it != data.end(); ++it) {
> > > +			std::vector<uint8_t> dvec;
> > > +			std::vector<int32_t> fvec;
> > > +
> > > +			std::tie(dvec, fvec) =
> > > +				IPADataSerializer<K>::serialize(it->first, cs);
> > > +
> > > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > > +
> > > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > > +
> > > +			std::tie(dvec, fvec) =
> > > +				IPADataSerializer<V>::serialize(it->second, cs);
> > > +
> > > +			appendUInt<uint32_t>(data_vec, dvec.size());
> > > +			appendUInt<uint32_t>(data_vec, fvec.size());
> > > +
> > > +			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
> > > +			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
> > > +		}
> > > +
> > > +		return {data_vec, fds_vec};
> > > +	}
> > > +
> > > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,
> > > +					  std::vector<uint8_t>::iterator it2,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<int32_t> fds;
> > > +		return IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,
> > > +								      fds.begin(), fds.end(),
> > > +								      cs);
> > > +	}
> > > +
> > > +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),
> > > +								      fds.begin(), fds.end(),
> > > +								      cs);
> > > +	}
> > > +
> > > +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > > +					  std::vector<int32_t>::iterator fds_it1,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					  ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::map<K, V> ret;
> > > +
> > > +		uint32_t map_len = readUInt<uint32_t>(data_it1);
> > > +
> > > +		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
> > > +		std::vector<int32_t>::iterator fd_it = fds_it1;
> > > +		for (uint32_t i = 0; i < map_len; i++) {
> > > +			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
> > > +			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > > +
> > > +			K key = IPADataSerializer<K>::deserialize(data_it + 8,
> > > +								  data_it + 8 + sizeof_data,
> > > +								  fd_it,
> > > +								  fd_it + sizeof_fds,
> > > +								  cs);
> > > +
> > > +			data_it += 8 + sizeof_data;
> > > +			fd_it += sizeof_fds;
> > > +			sizeof_data = readUInt<uint32_t>(data_it);
> > > +			sizeof_fds  = readUInt<uint32_t>(data_it + 4);
> > > +
> > > +			const V value = IPADataSerializer<V>::deserialize(data_it + 8,
> > > +									  data_it + 8 + sizeof_data,
> > > +									  fd_it,
> > > +									  fd_it + sizeof_fds,
> > > +									  cs);
> > > +			ret.insert({key, value});
> > > +
> > > +			data_it += 8 + sizeof_data;
> > > +			fd_it += sizeof_fds;
> > > +		}
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +#define DECLARE_INTEGRAL_SERIALIZER(type)				\
> > > +template<>								\
> > > +class IPADataSerializer<type>						\
> > > +{									\
> > > +public:									\
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>	\
> > > +	serialize(const type data,					\
> > > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)	\
> > > +	{								\
> > > +		std::vector<uint8_t> data_vec;				\
> > > +		appendUInt<type>(data_vec, data);			\
> > > +									\
> > > +		return {data_vec, {}};					\
> > > +	}								\
> > > +									\
> > > +	static type deserialize(std::vector<uint8_t> &data,		\
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > > +	{								\
> > > +		return IPADataSerializer<type>::deserialize(data.begin(),\
> > > +							    data.end());\
> > > +	}								\
> > > +									\
> > > +	static type deserialize(std::vector<uint8_t>::iterator it1,	\
> > > +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,\
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > > +	{								\
> > > +		return readUInt<type>(it1);				\
> > > +	}								\
> > > +									\
> > > +	static type deserialize(std::vector<uint8_t> &data,		\
> > > +				[[maybe_unused]] std::vector<int32_t> &fds,\
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > > +	{								\
> > > +		return IPADataSerializer<type>::deserialize(data.begin(),\
> > > +							    data.end());\
> > > +	}								\
> > > +									\
> > > +	static type deserialize(std::vector<uint8_t>::iterator data_it1,\
> > > +				std::vector<uint8_t>::iterator data_it2,\
> > > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\
> > > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> > > +	{								\
> > > +		return IPADataSerializer<type>::deserialize(data_it1,	\
> > > +							    data_it2);	\
> > > +	}								\
> > > +};
> > > +
> > > +DECLARE_INTEGRAL_SERIALIZER(uint8_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(uint16_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(uint32_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(uint64_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(int8_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(int16_t)
> > > +DECLARE_INTEGRAL_SERIALIZER(int32_t)
> > > +
> > > +template<>
> > > +class IPADataSerializer<int64_t>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const int64_t data,
> > > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +		appendUInt<uint64_t>(data_vec, data);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static int64_t deserialize(std::vector<uint8_t> &data,
> > > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> > > +							       data.end());
> > > +	}
> > > +
> > > +	static int64_t deserialize(std::vector<uint8_t>::iterator it1,
> > > +				   [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		if (std::distance(it1, it2) < 8) {
> > > +			LOG(IPADataSerializer, Info)
> > > +				<< "Not enough bytes to deserialize int64_t, returning 0";
> > > +			return 0;
> > > +		}
> > > +
> > > +		uint32_t lower = readUInt<uint32_t>(it1);
> > > +		uint32_t upper = readUInt<uint32_t>(it1 + 4);
> > > +
> > > +		return lower + (uint64_t(upper) << 32);
> > > +	}
> > > +
> > > +	static int64_t deserialize(std::vector<uint8_t> &data,
> > > +				   [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<int64_t>::deserialize(data.begin(),
> > > +							       data.end());
> > > +	}
> > > +
> > > +	static int64_t deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				   std::vector<uint8_t>::iterator data_it2,
> > > +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				   [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> >
> > In what is the int64_t specialization different that the other ones
> > defined through DECLARE_INTEGRAL_SERIALIZER ? It's only because you
> > don't have a  readUInt<int64_t> ?
>
> Yeah... I was having bit shifting problems with readUInt<int64_t>.
> I reimplemented it with memcpy though, and now the macro can generate
> code for serializing int64_t, bool, float, and double as well :)
>

Great!

Very nice overall!!


> > > +
> > > +template<>
> > > +class IPADataSerializer<bool>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const bool data,
> > > +		  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +		appendUInt<uint8_t>(data_vec, data);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static bool deserialize(std::vector<uint8_t> &data,
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<bool>::deserialize(data.begin(),
> > > +							    data.end());
> > > +	}
> > > +
> > > +	static bool deserialize(std::vector<uint8_t>::iterator it1,
> > > +				[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return readUInt<uint8_t>(it1);
> >
> > The sizeof(bool) seems to be a bit an issue actually... Can you solve
> > it by declarting a
> >
> > template<> bool readUInt(std::vector<uint8_t>::iterator it)
> > ?
> >
> > Actually it seems to me the current implementation might work with
> > bool too...
> >
> > > +	}
> > > +
> > > +	static bool deserialize(std::vector<uint8_t> &data,
> > > +				[[maybe_unused]] std::vector<int32_t> &fds,
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<bool>::deserialize(data.begin(),
> > > +							    data.end());
> > > +	}
> > > +
> > > +	static bool deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				std::vector<uint8_t>::iterator data_it2,
> > > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				[[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<bool>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<float>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		uint8_t arr[4];
> > > +		memcpy(arr, &data, sizeof(arr));
> >
> > I wonder if we should no be using memcpy in the readUInt() function
> > too. Would this help you with floats ?
>
> Apparently yes.
>
>
> Thanks,
>
> Paul
> > > +
> > > +		std::vector<uint8_t> data_vec(arr, arr + 4);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static float deserialize(std::vector<uint8_t> &data,
> > > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static float deserialize(std::vector<uint8_t>::iterator it1,
> > > +				 [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		if (std::distance(it1, it2) < 4) {
> > > +			LOG(IPADataSerializer, Info)
> > > +				<< "Not enough bytes to deserialize float, returning 0";
> > > +			return 0;
> > > +		}
> > > +
> > > +		std::vector data(it1, it2);
> > > +		float ret;
> > > +		memcpy(&ret, data.data(), sizeof(ret));
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static float deserialize(std::vector<uint8_t> &data,
> > > +				 [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static float deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				 std::vector<uint8_t>::iterator data_it2,
> > > +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				 [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<float>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<double>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		uint8_t arr[8];
> > > +		memcpy(arr, &data, sizeof(arr));
> > > +
> > > +		std::vector<uint8_t> data_vec(arr, arr + 8);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static double deserialize(std::vector<uint8_t> &data,
> > > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static double deserialize(std::vector<uint8_t>::iterator it1,
> > > +				  [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		if (std::distance(it1, it2) < 8) {
> > > +			LOG(IPADataSerializer, Info)
> > > +				<< "Not enough bytes to deserialize double, returning 0";
> > > +			return 0;
> > > +		}
> > > +
> > > +		std::vector data(it1, it2);
> > > +		double ret;
> > > +		memcpy(&ret, data.data(), sizeof(ret));
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static double deserialize(std::vector<uint8_t> &data,
> > > +				  [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static double deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				  std::vector<uint8_t>::iterator data_it2,
> > > +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<double>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<std::string>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec(data.begin(), data.end());
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static std::string deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static std::string deserialize(std::vector<uint8_t>::iterator it1,
> > > +				       std::vector<uint8_t>::iterator it2,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::string str(it1, it2);
> > > +
> > > +		return str;
> > > +	}
> > > +
> > > +	static std::string deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static std::string deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				       std::vector<uint8_t>::iterator data_it2,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::string>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<FileDescriptor>
> >
> > Ah, that's why you have the fds parameter in deserializer and the
> > std::vector<int32_t> in the serializer return parameter.
> >
> > I think this would be cleaner if we could use enable_if and define an
> > ad-hoc function signature for FileDescriptor and ControlList. It would
> > remove the fds and cs parameters from all other types (the ones I've
> > see so far at least).
> >
> > I think I'll stop here, there's enough to discuss on
> > Thanks
> >    j
> >
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec = { data.isValid() };
> > > +		std::vector<int32_t> fd_vec;
> > > +		if (data.isValid())
> > > +			fd_vec.push_back(data.fd());
> > > +
> > > +		return {data_vec, fd_vec};
> > > +	}
> > > +
> > > +	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> > > +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),
> > > +								      fds.begin(), fds.end());
> > > +	}
> > > +
> > > +	static FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					  std::vector<uint8_t>::iterator data_it2,
> > > +					  std::vector<int32_t>::iterator fds_it1,
> > > +					  std::vector<int32_t>::iterator fds_it2,
> > > +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		if (std::distance(data_it1, data_it2) < 1)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "Invalid data to deserialize FileDescriptor";
> > > +
> > > +		bool valid = *data_it1;
> > > +
> > > +		if (valid && std::distance(fds_it1, fds_it2) < 1)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "Invalid fds to deserialize FileDescriptor";
> > > +
> > > +		return valid ? FileDescriptor(*fds_it1) : FileDescriptor();
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<IPASettings>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<std::string>::serialize(data.configurationFile);
> > > +	}
> > > +
> > > +	static IPASettings deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static IPASettings deserialize(std::vector<uint8_t>::iterator it1,
> > > +				       std::vector<uint8_t>::iterator it2,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		IPASettings ret;
> > > +		ret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static IPASettings deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				       std::vector<uint8_t>::iterator data_it2,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<CameraSensorInfo>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +
> > > +		uint32_t str_len = data.model.size();
> > > +		appendUInt<uint32_t>(data_vec, str_len);
> > > +
> > > +		data_vec.insert(data_vec.end(), data.model.begin(), data.model.end());
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.bitsPerPixel);
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.width);
> > > +		appendUInt<uint32_t>(data_vec, data.activeAreaSize.height);
> > > +
> > > +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));
> > > +		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));
> > > +		appendUInt<uint32_t>(data_vec, data.analogCrop.width);
> > > +		appendUInt<uint32_t>(data_vec, data.analogCrop.height);
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.outputSize.width);
> > > +		appendUInt<uint32_t>(data_vec, data.outputSize.height);
> > > +
> > > +		appendUInt<uint64_t>(data_vec, data.pixelRate);
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.lineLength);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> > > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,
> > > +					    [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		CameraSensorInfo ret;
> > > +
> > > +		uint32_t str_len = readUInt<uint32_t>(it1);
> > > +		std::string str(it1 + 4, it1 + 4 + str_len);
> > > +		ret.model = str;
> > > +
> > > +		std::vector<uint8_t>::iterator it = it1 + 4 + str_len;
> > > +
> > > +		ret.bitsPerPixel = readUInt<uint32_t>(it);
> > > +
> > > +		ret.activeAreaSize.width = readUInt<uint32_t>(it + 4);
> > > +		ret.activeAreaSize.height = readUInt<uint32_t>(it + 8);
> > > +
> > > +		ret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));
> > > +		ret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));
> > > +		ret.analogCrop.width = readUInt<uint32_t>(it + 20);
> > > +		ret.analogCrop.height = readUInt<uint32_t>(it + 24);
> > > +
> > > +		ret.outputSize.width = readUInt<uint32_t>(it + 28);
> > > +		ret.outputSize.height = readUInt<uint32_t>(it + 32);
> > > +
> > > +		ret.pixelRate = readUInt<uint64_t>(it + 36);
> > > +
> > > +		ret.lineLength = readUInt<uint64_t>(it + 44);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> > > +					    [[maybe_unused]] std::vector<int32_t> &fds,
> > > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					    std::vector<uint8_t>::iterator data_it2,
> > > +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<IPAStream>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.pixelFormat);
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.size.width);
> > > +		appendUInt<uint32_t>(data_vec, data.size.height);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static IPAStream deserialize(std::vector<uint8_t> &data,
> > > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static IPAStream deserialize(std::vector<uint8_t>::iterator it1,
> > > +				     [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		IPAStream ret;
> > > +
> > > +		ret.pixelFormat = readUInt<uint32_t>(it1);
> > > +
> > > +		ret.size.width = readUInt<uint32_t>(it1 + 4);
> > > +		ret.size.height = readUInt<uint32_t>(it1 + 8);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static IPAStream deserialize(std::vector<uint8_t> &data,
> > > +				     [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
> > > +	}
> > > +
> > > +	static IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				     std::vector<uint8_t>::iterator data_it2,
> > > +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<const ControlList>
> > > +{
> > > +public:
> > > +	// map arg will be generated, since it's per-pipeline anyway
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const ControlList &data, const ControlInfoMap &map,
> > > +		  ControlSerializer *cs)
> > > +	{
> > > +		if (!cs)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "ControlSerializer not provided for serialization of ControlList";
> > > +
> > > +		size_t size = cs->binarySize(map);
> > > +		std::vector<uint8_t> infoData(size);
> > > +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> > > +		int ret = cs->serialize(map, buffer);
> > > +
> > > +		if (ret < 0 || buffer.overflow()) {
> > > +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap";
> > > +			return {{}, {}};
> > > +		}
> > > +
> > > +		size = cs->binarySize(data);
> > > +		std::vector<uint8_t> listData(size);
> > > +		buffer = ByteStreamBuffer(listData.data(), listData.size());
> > > +		ret = cs->serialize(data, buffer);
> > > +
> > > +		if (ret < 0 || buffer.overflow()) {
> > > +			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList";
> > > +			return {{}, {}};
> > > +		}
> > > +
> > > +		std::vector<uint8_t> data_vec;
> > > +		appendUInt<uint32_t>(data_vec, infoData.size());
> > > +		appendUInt<uint32_t>(data_vec, listData.size());
> > > +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> > > +		data_vec.insert(data_vec.end(), listData.begin(), listData.end());
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static const ControlList deserialize(std::vector<uint8_t>::iterator it1,
> > > +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		if (!cs)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "ControlSerializer not provided for deserialization of ControlList";
> > > +
> > > +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> > > +		uint32_t listDataSize = readUInt<uint32_t>(it1 + 4);
> > > +
> > > +		std::vector<uint8_t>::iterator it = it1 + 8;
> > > +
> > > +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> > > +		std::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);
> > > +
> > > +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> > > +		ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> > > +		/* It's fine if map is empty. */
> > > +		if (buffer.overflow()) {
> > > +			LOG(IPADataSerializer, Error)
> > > +				<< "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow";
> > > +			return ControlList();
> > > +		}
> > > +
> > > +		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());
> > > +		ControlList list = cs->deserialize<ControlList>(buffer);
> > > +		if (buffer.overflow())
> > > +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow";
> > > +		if (list.empty())
> > > +			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: empty list";
> > > +
> > > +		return list;
> > > +	}
> > > +
> > > +	static const ControlList deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				       std::vector<uint8_t>::iterator data_it2,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<ControlList>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const ControlList &data, const ControlInfoMap &map,
> > > +		  ControlSerializer *cs)
> > > +	{
> > > +		const ControlList &list_const = const_cast<const ControlList &>(data);
> > > +
> > > +		std::vector<uint8_t> data_vec;
> > > +		std::tie(data_vec, std::ignore) =
> > > +			IPADataSerializer<const ControlList>::serialize(list_const, map, cs);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static ControlList deserialize(std::vector<uint8_t> &data,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		ControlList ret;
> > > +		const ControlList &list = ret;
> > > +		const_cast<ControlList &>(list) =
> > > +			IPADataSerializer<const ControlList>::deserialize(data, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlList deserialize(std::vector<uint8_t>::iterator it1,
> > > +				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		ControlList ret;
> > > +		const ControlList &list = ret;
> > > +		const_cast<ControlList &>(list) =
> > > +			IPADataSerializer<const ControlList>::deserialize(it1, it2, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlList deserialize(std::vector<uint8_t> &data,
> > > +				       [[maybe_unused]] std::vector<int32_t> &fds,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		ControlList ret;
> > > +		const ControlList &list = ret;
> > > +		const_cast<ControlList &>(list) =
> > > +			IPADataSerializer<const ControlList>::deserialize(data, fds, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				       std::vector<uint8_t>::iterator data_it2,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +				       ControlSerializer *cs)
> > > +	{
> > > +		ControlList ret;
> > > +		const ControlList &list = ret;
> > > +		const_cast<ControlList &>(list) =
> > > +			IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,
> > > +									  fds_it1, fds_it2, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<const ControlInfoMap>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const ControlInfoMap &map, ControlSerializer *cs)
> > > +	{
> > > +		if (!cs)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "ControlSerializer not provided for serialization of ControlInfoMap";
> > > +
> > > +		size_t size = cs->binarySize(map);
> > > +		std::vector<uint8_t> infoData(size);
> > > +		ByteStreamBuffer buffer(infoData.data(), infoData.size());
> > > +		int ret = cs->serialize(map, buffer);
> > > +
> > > +		if (ret < 0 || buffer.overflow())
> > > +			return {{}, {}};
> > > +
> > > +		std::vector<uint8_t> data_vec;
> > > +		appendUInt<uint32_t>(data_vec, infoData.size());
> > > +		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > > +						ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> > > +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +						ControlSerializer *cs)
> > > +	{
> > > +		if (!cs)
> > > +			LOG(IPADataSerializer, Fatal)
> > > +				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
> > > +
> > > +		uint32_t infoDataSize = readUInt<uint32_t>(it1);
> > > +
> > > +		std::vector<uint8_t>::iterator it = it1 + 4;
> > > +
> > > +		std::vector<uint8_t> infoData(it, it + infoDataSize);
> > > +
> > > +		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
> > > +		const ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
> > > +
> > > +		return map;
> > > +	}
> > > +
> > > +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > > +						[[maybe_unused]] std::vector<int32_t> &fds,
> > > +						ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
> > > +	}
> > > +
> > > +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					  std::vector<uint8_t>::iterator data_it2,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					  ControlSerializer *cs)
> > > +	{
> > > +		return IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<ControlInfoMap>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs)
> > > +	{
> > > +		const ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);
> > > +
> > > +		std::vector<uint8_t> data_vec;
> > > +		std::tie(data_vec, std::ignore) =
> > > +			IPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);
> > > +
> > > +		return {data_vec, {}};
> > > +	}
> > > +
> > > +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > > +					  ControlSerializer *cs)
> > > +	{
> > > +		ControlInfoMap ret;
> > > +		const ControlInfoMap &map = ret;
> > > +		const_cast<ControlInfoMap &>(map) =
> > > +			IPADataSerializer<const ControlInfoMap>::deserialize(data, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
> > > +						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
> > > +						ControlSerializer *cs)
> > > +	{
> > > +		ControlInfoMap ret;
> > > +		const ControlInfoMap &map = ret;
> > > +		const_cast<ControlInfoMap &>(map) =
> > > +			IPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
> > > +						[[maybe_unused]] std::vector<int32_t> &fds,
> > > +						ControlSerializer *cs)
> > > +	{
> > > +		ControlInfoMap ret;
> > > +		const ControlInfoMap &map = ret;
> > > +		const_cast<ControlInfoMap &>(map) =
> > > +			IPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +
> > > +	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					  std::vector<uint8_t>::iterator data_it2,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
> > > +					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					  ControlSerializer *cs)
> > > +	{
> > > +		ControlInfoMap ret;
> > > +		const ControlInfoMap &map = ret;
> > > +		const_cast<ControlInfoMap &>(map) =
> > > +			IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,
> > > +									     fds_it1, fds_it2, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +template<>
> > > +class IPADataSerializer<FrameBuffer::Plane>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +		std::vector<int32_t> fds_vec;
> > > +
> > > +		// fd
> > > +		std::vector<uint8_t> fdBuf;
> > > +		std::vector<int32_t> fdFds;
> > > +		std::tie(fdBuf, fdFds) =
> > > +			IPADataSerializer<FileDescriptor>::serialize(data.fd);
> > > +		data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());
> > > +		fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());
> > > +
> > > +		// length
> > > +		appendUInt<uint32_t>(data_vec, data.length);
> > > +
> > > +		return {data_vec, fds_vec};
> > > +	}
> > > +
> > > +	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
> > > +					      std::vector<int32_t> &fds,
> > > +					      ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),
> > > +									  fds.begin(), fds.end(),
> > > +									  cs);
> > > +	}
> > > +
> > > +	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +					      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
> > > +					      std::vector<int32_t>::iterator fds_it1,
> > > +					      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
> > > +					      [[maybe_unused]] ControlSerializer *cs = nullptr)
> > > +	{
> > > +		FrameBuffer::Plane ret;
> > > +
> > > +		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,
> > > +									fds_it1, fds_it1 + 1);
> > > +		ret.length = readUInt<uint32_t>(data_it1 + 1);
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +
> > > +template<>
> > > +class IPADataSerializer<IPABuffer>
> > > +{
> > > +public:
> > > +	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> > > +	serialize(const IPABuffer &data, ControlSerializer *cs = nullptr)
> > > +	{
> > > +		std::vector<uint8_t> data_vec;
> > > +
> > > +		appendUInt<uint32_t>(data_vec, data.id);
> > > +
> > > +		std::vector<uint8_t> planes_data_vec;
> > > +		std::vector<int32_t> planes_fds_vec;
> > > +		std::tie(planes_data_vec, planes_fds_vec) =
> > > +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
> > > +
> > > +		data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());
> > > +
> > > +		return {data_vec, planes_fds_vec};
> > > +	}
> > > +
> > > +	static IPABuffer deserialize(std::vector<uint8_t> &data,
> > > +				     std::vector<int32_t> &fds,
> > > +				     ControlSerializer *cs = nullptr)
> > > +	{
> > > +		return IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),
> > > +								 fds.begin(), fds.end(), cs);
> > > +	}
> > > +
> > > +	static IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,
> > > +				     std::vector<uint8_t>::iterator data_it2,
> > > +				     std::vector<int32_t>::iterator fds_it1,
> > > +				     std::vector<int32_t>::iterator fds_it2,
> > > +				     ControlSerializer *cs = nullptr)
> > > +	{
> > > +		IPABuffer ret;
> > > +
> > > +		ret.id = readUInt<uint32_t>(data_it1);
> > > +
> > > +		ret.planes =
> > > +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
> > > +				data_it1 + 4, data_it2, fds_it1, fds_it2, cs);
> > > +
> > > +		return ret;
> > > +	}
> > > +};
> > > +
> > > +#endif /* __DOXYGEN__ */
> > > +
> > > +} /* namespace libcamera */
> > > +
> > > +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */
> > > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
> > > new file mode 100644
> > > index 00000000..5029cdf6
> > > --- /dev/null
> > > +++ b/src/libcamera/ipa_data_serializer.cpp
> > > @@ -0,0 +1,154 @@
> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > > +/*
> > > + * Copyright (C) 2020, Google Inc.
> > > + *
> > > + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
> > > + */
> > > +
> > > +#include "libcamera/internal/ipa_data_serializer.h"
> > > +
> > > +#include "libcamera/internal/log.h"
> > > +
> > > +/**
> > > + * \file ipa_data_serializer.h
> > > + * \brief IPA Data Serializer
> > > + */
> > > +
> > > +namespace libcamera {
> > > +
> > > +LOG_DEFINE_CATEGORY(IPADataSerializer)
> > > +
> > > +/**
> > > + * \class IPADataSerializer
> > > + * \brief IPA Data Serializer
> > > + *
> > > + * Static template class that provides functions for serializing and
> > > + * deserializing IPA data.
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)
> > > + * \brief Append uint to end of byte vector, in little-endian order
> > > + * \tparam T Type of uint to append
> > > + * \param[in] vec Byte vector to append to
> > > + * \param[in] val Value of uint to append
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)
> > > + * \brief Read uint from byte vector, in little-endian order
> > > + * \tparam T Type of uint to read
> > > + * \param[in] vec Byte vector to read from
> > > + * \param[in] pos Index in vec to start reading from
> > > + *
> > > + * \return The uint read from \a vec, or 0 if reading goes past end of \a vec
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)
> > > + * \brief Read uint from byte vector, in little-endian order
> > > + * \tparam T Type of uint to read
> > > + * \param[in] it Iterator of byte vector to read from
> > > + *
> > > + * \return The uint read from \a vec
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> IPADataSerializer<T>::serialize(
> > > + * 	T data,
> > > + * 	ControlSerializer *cs = nullptr)
> > > + * \brief Serialize an object into byte vector and fd vector
> > > + * \tparam T Type of object to serialize
> > > + * \param[in] data Object to serialize
> > > + * \param[in] cs ControlSerializer
> > > + *
> > > + * \a cs is only necessary if the object type \a T or its members contain
> > > + * ControlList or ControlInfoMap.
> > > + *
> > > + * \return Tuple of byte vector and fd vector, that is the serialized form
> > > + * of \a data
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > > + * 	std::vector<uint8_t> &data,
> > > + * 	ControlSerializer *cs = nullptr)
> > > + * \brief Deserialize byte vector into an object
> > > + * \tparam T Type of object to deserialize to
> > > + * \param[in] data Byte vector to deserialize from
> > > + * \param[in] cs ControlSerializer
> > > + *
> > > + * This version of deserialize() can be used if the object type \a T and its
> > > + * members don't have any FileDescriptor.
> > > + *
> > > + * \a cs is only necessary if the object type \a T or its members contain
> > > + * ControlList or ControlInfoMap.
> > > + *
> > > + * \return The deserialized object
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > > + * 	std::vector<uint8_t>::iterator it1,
> > > + * 	std::vector<uint8_t>::iterator it2,
> > > + * 	ControlSerializer *cs = nullptr)
> > > + * \brief Deserialize byte vector into an object
> > > + * \tparam T Type of object to deserialize to
> > > + * \param[in] it1 Begin iterator of byte vector to deserialize from
> > > + * \param[in] it2 End iterator of byte vector to deserialize from
> > > + * \param[in] cs ControlSerializer
> > > + *
> > > + * This version of deserialize() can be used if the object type \a T and its
> > > + * members don't have any FileDescriptor.
> > > + *
> > > + * \a cs is only necessary if the object type \a T or its members contain
> > > + * ControlList or ControlInfoMap.
> > > + *
> > > + * \return The deserialized object
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> IPADataSerializer<T>::deserialize(
> > > + * 	std::vector<uint8_t> &data,
> > > + * 	std::vector<int32_t> &fds,
> > > + * 	ControlSerializer *cs = nullptr)
> > > + * \brief Deserialize byte vector and fd vector into an object
> > > + * \tparam T Type of object to deserialize to
> > > + * \param[in] data Byte vector to deserialize from
> > > + * \param[in] fds Fd vector to deserialize from
> > > + * \param[in] cs ControlSerializer
> > > + *
> > > + * This version of deserialize() (or the iterator version) must be used if
> > > + * the object type \a T or its members contain FileDescriptor.
> > > + *
> > > + * \a cs is only necessary if the object type \a T or its members contain
> > > + * ControlList or ControlInfoMap.
> > > + *
> > > + * \return The deserialized object
> > > + */
> > > +
> > > +/**
> > > + * \fn template<typename T> IPADataSerializer::deserialize(
> > > + * 	std::vector<uint8_t>::iterator data_it1,
> > > + * 	std::vector<uint8_t>::iterator data_it2,
> > > + * 	std::vector<int32_t>::iterator fds_it1,
> > > + * 	std::vector<int32_t>::iterator fds_it2,
> > > + * 	ControlSerializer *cs = nullptr)
> > > + * \brief Deserialize byte vector and fd vector into an object
> > > + * \tparam T Type of object to deserialize to
> > > + * \param[in] data_it1 Begin iterator of byte vector to deserialize from
> > > + * \param[in] data_it2 End iterator of byte vector to deserialize from
> > > + * \param[in] fds_it1 Begin iterator of fd vector to deserialize from
> > > + * \param[in] fds_it2 End iterator of fd vector to deserialize from
> > > + * \param[in] cs ControlSerializer
> > > + *
> > > + * This version of deserialize() (or the vector version) must be used if
> > > + * the object type \a T or its members contain FileDescriptor.
> > > + *
> > > + * \a cs is only necessary if the object type \a T or its members contain
> > > + * ControlList or ControlInfoMap.
> > > + *
> > > + * \return The deserialized object
> > > + */
> > > +
> > > +} /* namespace libcamera */
> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > > index 9d7442fa..61aad08e 100644
> > > --- a/src/libcamera/meson.build
> > > +++ b/src/libcamera/meson.build
> > > @@ -23,6 +23,7 @@ libcamera_sources = files([
> > >      'geometry.cpp',
> > >      'ipa_context_wrapper.cpp',
> > >      'ipa_controls.cpp',
> > > +    'ipa_data_serializer.cpp',
> > >      'ipa_interface.cpp',
> > >      'ipa_manager.cpp',
> > >      'ipa_module.cpp',
> > > --
> > > 2.27.0
> > >
> > > _______________________________________________
> > > libcamera-devel mailing list
> > > libcamera-devel@lists.libcamera.org
> > > https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h
new file mode 100644
index 00000000..accd4dce
--- /dev/null
+++ b/include/libcamera/internal/ipa_data_serializer.h
@@ -0,0 +1,1220 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_data_serializer.h - Image Processing Algorithm data serializer
+ */
+#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
+#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__
+
+#include <deque>
+#include <iostream>
+#include <tuple>
+#include <vector>
+
+#include <libcamera/buffer.h>
+#include <libcamera/control_ids.h>
+#include <libcamera/geometry.h>
+#include <libcamera/ipa/ipa_interface.h>
+
+#include "libcamera/internal/byte_stream_buffer.h"
+#include "libcamera/internal/camera_sensor.h"
+#include "libcamera/internal/control_serializer.h"
+#include "libcamera/internal/log.h"
+
+namespace libcamera {
+
+LOG_DECLARE_CATEGORY(IPADataSerializer)
+
+template<typename T>
+void appendUInt(std::vector<uint8_t> &vec, T val)
+{
+	size_t byteWidth = sizeof(val);
+	for (size_t i = 0; i < byteWidth; i++)
+		vec.push_back(static_cast<uint8_t>((val >> 8*i) & 0xff));
+}
+
+template<typename T>
+T readUInt(std::vector<uint8_t> &vec, size_t pos)
+{
+	T ret = 0;
+	size_t byteWidth = sizeof(ret);
+	if (pos + byteWidth > vec.size())
+		return ret;
+
+	for (size_t i = 0; i < byteWidth; i++)
+		ret |= vec[pos + i] << 8*i;
+	return ret;
+}
+
+template<typename T>
+T readUInt(std::vector<uint8_t>::iterator it)
+{
+	T ret = 0;
+	size_t byteWidth = sizeof(ret);
+	for (size_t i = 0; i < byteWidth; i++)
+		ret |= *(it + i) << 8*i;
+	return ret;
+}
+
+template<typename T>
+class IPADataSerializer
+{
+#ifdef __DOXYGEN__
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(T data, ControlSerializer *cs);
+
+	static T deserialize(std::vector<uint8_t> &data,
+			     ControlSerializer *cs);
+	static T deserialize(std::vector<uint8_t>::iterator it1,
+			     std::vector<uint8_t>::iterator it2,
+			     ControlSerializer *cs);
+
+	static T deserialize(std::vector<uint8_t> &data,
+			     std::vector<int32_t> &fds,
+			     ControlSerializer *cs);
+	static T deserialize(std::vector<uint8_t>::iterator data_it1,
+			     std::vector<uint8_t>::iterator data_it2,
+			     std::vector<int32_t>::iterator fds_it1,
+			     std::vector<int32_t>::iterator fds_it2,
+			     ControlSerializer *cs);
+#endif /* __DOXYGEN__ */
+};
+
+#ifndef __DOXYGEN__
+
+template<typename V>
+class IPADataSerializer<std::vector<V>>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// serialize the length
+		uint32_t vec_len = data.size();
+		appendUInt<uint32_t>(data_vec, vec_len);
+
+		// serialize the members
+		for (auto it = data.begin(); it != data.end(); ++it) {
+			std::vector<uint8_t> dvec;
+			std::vector<int32_t> fvec;
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<V>::serialize(*it, cs);
+
+			appendUInt<uint32_t>(data_vec, dvec.size());
+			appendUInt<uint32_t>(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+		}
+
+		return {data_vec, fds_vec};
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t>::iterator it1,
+					  std::vector<uint8_t>::iterator it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::vector<int32_t> fds;
+		return IPADataSerializer<std::vector<V>>::deserialize(it1, it2,
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::vector<V>>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::vector<V> deserialize(std::vector<uint8_t>::iterator data_it1,
+					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		uint32_t vec_len = readUInt<uint32_t>(data_it1);
+		std::vector<V> ret(vec_len);
+
+		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
+		std::vector<int32_t>::iterator fd_it = fds_it1;
+		for (uint32_t i = 0; i < vec_len; i++) {
+			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
+			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
+
+			ret[i] = IPADataSerializer<V>::deserialize(data_it + 8,
+								   data_it + 8 + sizeof_data,
+								   fd_it,
+								   fd_it + sizeof_fds,
+								   cs);
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+		}
+
+		return ret;
+	}
+};
+
+template<typename K, typename V>
+class IPADataSerializer<std::map<K, V>>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// serialize the length
+		uint32_t map_len = data.size();
+		appendUInt<uint32_t>(data_vec, map_len);
+
+		// serialize the members
+		for (auto it = data.begin(); it != data.end(); ++it) {
+			std::vector<uint8_t> dvec;
+			std::vector<int32_t> fvec;
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<K>::serialize(it->first, cs);
+
+			appendUInt<uint32_t>(data_vec, dvec.size());
+			appendUInt<uint32_t>(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+
+			std::tie(dvec, fvec) =
+				IPADataSerializer<V>::serialize(it->second, cs);
+
+			appendUInt<uint32_t>(data_vec, dvec.size());
+			appendUInt<uint32_t>(data_vec, fvec.size());
+
+			data_vec.insert(data_vec.end(), dvec.begin(), dvec.end());
+			fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end());
+		}
+
+		return {data_vec, fds_vec};
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator it1,
+					  std::vector<uint8_t>::iterator it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::vector<int32_t> fds;
+		return IPADataSerializer<std::map<K, V>>::deserialize(it1, it2,
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::map<K, V>>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end(),
+								      cs);
+	}
+
+	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator data_it1,
+					  [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs = nullptr)
+	{
+		std::map<K, V> ret;
+
+		uint32_t map_len = readUInt<uint32_t>(data_it1);
+
+		std::vector<uint8_t>::iterator data_it = data_it1 + 4;
+		std::vector<int32_t>::iterator fd_it = fds_it1;
+		for (uint32_t i = 0; i < map_len; i++) {
+			uint32_t sizeof_data = readUInt<uint32_t>(data_it);
+			uint32_t sizeof_fds  = readUInt<uint32_t>(data_it + 4);
+
+			K key = IPADataSerializer<K>::deserialize(data_it + 8,
+								  data_it + 8 + sizeof_data,
+								  fd_it,
+								  fd_it + sizeof_fds,
+								  cs);
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+			sizeof_data = readUInt<uint32_t>(data_it);
+			sizeof_fds  = readUInt<uint32_t>(data_it + 4);
+
+			const V value = IPADataSerializer<V>::deserialize(data_it + 8,
+									  data_it + 8 + sizeof_data,
+									  fd_it,
+									  fd_it + sizeof_fds,
+									  cs);
+			ret.insert({key, value});
+
+			data_it += 8 + sizeof_data;
+			fd_it += sizeof_fds;
+		}
+
+		return ret;
+	}
+};
+
+#define DECLARE_INTEGRAL_SERIALIZER(type)				\
+template<>								\
+class IPADataSerializer<type>						\
+{									\
+public:									\
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>	\
+	serialize(const type data,					\
+		  [[maybe_unused]] ControlSerializer *cs = nullptr)	\
+	{								\
+		std::vector<uint8_t> data_vec;				\
+		appendUInt<type>(data_vec, data);			\
+									\
+		return {data_vec, {}};					\
+	}								\
+									\
+	static type deserialize(std::vector<uint8_t> &data,		\
+				[[maybe_unused]] ControlSerializer *cs = nullptr)\
+	{								\
+		return IPADataSerializer<type>::deserialize(data.begin(),\
+							    data.end());\
+	}								\
+									\
+	static type deserialize(std::vector<uint8_t>::iterator it1,	\
+				[[maybe_unused]] std::vector<uint8_t>::iterator it2,\
+				[[maybe_unused]] ControlSerializer *cs = nullptr)\
+	{								\
+		return readUInt<type>(it1);				\
+	}								\
+									\
+	static type deserialize(std::vector<uint8_t> &data,		\
+				[[maybe_unused]] std::vector<int32_t> &fds,\
+				[[maybe_unused]] ControlSerializer *cs = nullptr)\
+	{								\
+		return IPADataSerializer<type>::deserialize(data.begin(),\
+							    data.end());\
+	}								\
+									\
+	static type deserialize(std::vector<uint8_t>::iterator data_it1,\
+				std::vector<uint8_t>::iterator data_it2,\
+				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,\
+				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,\
+				[[maybe_unused]] ControlSerializer *cs = nullptr)\
+	{								\
+		return IPADataSerializer<type>::deserialize(data_it1,	\
+							    data_it2);	\
+	}								\
+};
+
+DECLARE_INTEGRAL_SERIALIZER(uint8_t)
+DECLARE_INTEGRAL_SERIALIZER(uint16_t)
+DECLARE_INTEGRAL_SERIALIZER(uint32_t)
+DECLARE_INTEGRAL_SERIALIZER(uint64_t)
+DECLARE_INTEGRAL_SERIALIZER(int8_t)
+DECLARE_INTEGRAL_SERIALIZER(int16_t)
+DECLARE_INTEGRAL_SERIALIZER(int32_t)
+
+template<>
+class IPADataSerializer<int64_t>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const int64_t data,
+		  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		appendUInt<uint64_t>(data_vec, data);
+
+		return {data_vec, {}};
+	}
+
+	static int64_t deserialize(std::vector<uint8_t> &data,
+				   [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<int64_t>::deserialize(data.begin(),
+							       data.end());
+	}
+
+	static int64_t deserialize(std::vector<uint8_t>::iterator it1,
+				   [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				   [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		if (std::distance(it1, it2) < 8) {
+			LOG(IPADataSerializer, Info)
+				<< "Not enough bytes to deserialize int64_t, returning 0";
+			return 0;
+		}
+
+		uint32_t lower = readUInt<uint32_t>(it1);
+		uint32_t upper = readUInt<uint32_t>(it1 + 4);
+
+		return lower + (uint64_t(upper) << 32);
+	}
+
+	static int64_t deserialize(std::vector<uint8_t> &data,
+				   [[maybe_unused]] std::vector<int32_t> &fds,
+				   [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<int64_t>::deserialize(data.begin(),
+							       data.end());
+	}
+
+	static int64_t deserialize(std::vector<uint8_t>::iterator data_it1,
+				   std::vector<uint8_t>::iterator data_it2,
+				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				   [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				   [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<int64_t>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<bool>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const bool data,
+		  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		appendUInt<uint8_t>(data_vec, data);
+
+		return {data_vec, {}};
+	}
+
+	static bool deserialize(std::vector<uint8_t> &data,
+				[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<bool>::deserialize(data.begin(),
+							    data.end());
+	}
+
+	static bool deserialize(std::vector<uint8_t>::iterator it1,
+				[[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return readUInt<uint8_t>(it1);
+	}
+
+	static bool deserialize(std::vector<uint8_t> &data,
+				[[maybe_unused]] std::vector<int32_t> &fds,
+				[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<bool>::deserialize(data.begin(),
+							    data.end());
+	}
+
+	static bool deserialize(std::vector<uint8_t>::iterator data_it1,
+				std::vector<uint8_t>::iterator data_it2,
+				[[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				[[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				[[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<bool>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<float>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const float data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		uint8_t arr[4];
+		memcpy(arr, &data, sizeof(arr));
+
+		std::vector<uint8_t> data_vec(arr, arr + 4);
+
+		return {data_vec, {}};
+	}
+
+	static float deserialize(std::vector<uint8_t> &data,
+				 [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
+	}
+
+	static float deserialize(std::vector<uint8_t>::iterator it1,
+				 [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				 [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		if (std::distance(it1, it2) < 4) {
+			LOG(IPADataSerializer, Info)
+				<< "Not enough bytes to deserialize float, returning 0";
+			return 0;
+		}
+
+		std::vector data(it1, it2);
+		float ret;
+		memcpy(&ret, data.data(), sizeof(ret));
+
+		return ret;
+	}
+
+	static float deserialize(std::vector<uint8_t> &data,
+				 [[maybe_unused]] std::vector<int32_t> &fds,
+				 [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<float>::deserialize(data.begin(), data.end());
+	}
+
+	static float deserialize(std::vector<uint8_t>::iterator data_it1,
+				 std::vector<uint8_t>::iterator data_it2,
+				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				 [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				 [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<float>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<double>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const double data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		uint8_t arr[8];
+		memcpy(arr, &data, sizeof(arr));
+
+		std::vector<uint8_t> data_vec(arr, arr + 8);
+
+		return {data_vec, {}};
+	}
+
+	static double deserialize(std::vector<uint8_t> &data,
+				  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
+	}
+
+	static double deserialize(std::vector<uint8_t>::iterator it1,
+				  [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		if (std::distance(it1, it2) < 8) {
+			LOG(IPADataSerializer, Info)
+				<< "Not enough bytes to deserialize double, returning 0";
+			return 0;
+		}
+
+		std::vector data(it1, it2);
+		double ret;
+		memcpy(&ret, data.data(), sizeof(ret));
+
+		return ret;
+	}
+
+	static double deserialize(std::vector<uint8_t> &data,
+				  [[maybe_unused]] std::vector<int32_t> &fds,
+				  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<double>::deserialize(data.begin(), data.end());
+	}
+
+	static double deserialize(std::vector<uint8_t>::iterator data_it1,
+				  std::vector<uint8_t>::iterator data_it2,
+				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<double>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<std::string>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec(data.begin(), data.end());
+
+		return {data_vec, {}};
+	}
+
+	static std::string deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
+	}
+
+	static std::string deserialize(std::vector<uint8_t>::iterator it1,
+				       std::vector<uint8_t>::iterator it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::string str(it1, it2);
+
+		return str;
+	}
+
+	static std::string deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::string>::deserialize(data.begin(), data.end());
+	}
+
+	static std::string deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::string>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<FileDescriptor>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec = { data.isValid() };
+		std::vector<int32_t> fd_vec;
+		if (data.isValid())
+			fd_vec.push_back(data.fd());
+
+		return {data_vec, fd_vec};
+	}
+
+	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
+					  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<FileDescriptor>::deserialize(data.begin(), data.end(),
+								      fds.begin(), fds.end());
+	}
+
+	static FileDescriptor deserialize(std::vector<uint8_t>::iterator data_it1,
+					  std::vector<uint8_t>::iterator data_it2,
+					  std::vector<int32_t>::iterator fds_it1,
+					  std::vector<int32_t>::iterator fds_it2,
+					  [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		if (std::distance(data_it1, data_it2) < 1)
+			LOG(IPADataSerializer, Fatal)
+				<< "Invalid data to deserialize FileDescriptor";
+
+		bool valid = *data_it1;
+
+		if (valid && std::distance(fds_it1, fds_it2) < 1)
+			LOG(IPADataSerializer, Fatal)
+				<< "Invalid fds to deserialize FileDescriptor";
+
+		return valid ? FileDescriptor(*fds_it1) : FileDescriptor();
+	}
+};
+
+template<>
+class IPADataSerializer<IPASettings>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<std::string>::serialize(data.configurationFile);
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t>::iterator it1,
+				       std::vector<uint8_t>::iterator it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		IPASettings ret;
+		ret.configurationFile = IPADataSerializer<std::string>::deserialize(it1, it2);
+
+		return ret;
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data.begin(), data.end());
+	}
+
+	static IPASettings deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPASettings>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<CameraSensorInfo>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		uint32_t str_len = data.model.size();
+		appendUInt<uint32_t>(data_vec, str_len);
+
+		data_vec.insert(data_vec.end(), data.model.begin(), data.model.end());
+
+		appendUInt<uint32_t>(data_vec, data.bitsPerPixel);
+
+		appendUInt<uint32_t>(data_vec, data.activeAreaSize.width);
+		appendUInt<uint32_t>(data_vec, data.activeAreaSize.height);
+
+		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.x));
+		appendUInt<uint32_t>(data_vec, static_cast<uint32_t>(data.analogCrop.y));
+		appendUInt<uint32_t>(data_vec, data.analogCrop.width);
+		appendUInt<uint32_t>(data_vec, data.analogCrop.height);
+
+		appendUInt<uint32_t>(data_vec, data.outputSize.width);
+		appendUInt<uint32_t>(data_vec, data.outputSize.height);
+
+		appendUInt<uint64_t>(data_vec, data.pixelRate);
+
+		appendUInt<uint32_t>(data_vec, data.lineLength);
+
+		return {data_vec, {}};
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator it1,
+					    [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		CameraSensorInfo ret;
+
+		uint32_t str_len = readUInt<uint32_t>(it1);
+		std::string str(it1 + 4, it1 + 4 + str_len);
+		ret.model = str;
+
+		std::vector<uint8_t>::iterator it = it1 + 4 + str_len;
+
+		ret.bitsPerPixel = readUInt<uint32_t>(it);
+
+		ret.activeAreaSize.width = readUInt<uint32_t>(it + 4);
+		ret.activeAreaSize.height = readUInt<uint32_t>(it + 8);
+
+		ret.analogCrop.x = static_cast<int32_t>(readUInt<uint32_t>(it + 12));
+		ret.analogCrop.y = static_cast<int32_t>(readUInt<uint32_t>(it + 16));
+		ret.analogCrop.width = readUInt<uint32_t>(it + 20);
+		ret.analogCrop.height = readUInt<uint32_t>(it + 24);
+
+		ret.outputSize.width = readUInt<uint32_t>(it + 28);
+		ret.outputSize.height = readUInt<uint32_t>(it + 32);
+
+		ret.pixelRate = readUInt<uint64_t>(it + 36);
+
+		ret.lineLength = readUInt<uint64_t>(it + 44);
+
+		return ret;
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
+					    [[maybe_unused]] std::vector<int32_t> &fds,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data.begin(), data.end());
+	}
+
+	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator data_it1,
+					    std::vector<uint8_t>::iterator data_it2,
+					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					    [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					    [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<CameraSensorInfo>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<IPAStream>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		appendUInt<uint32_t>(data_vec, data.pixelFormat);
+
+		appendUInt<uint32_t>(data_vec, data.size.width);
+		appendUInt<uint32_t>(data_vec, data.size.height);
+
+		return {data_vec, {}};
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t> &data,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t>::iterator it1,
+				     [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		IPAStream ret;
+
+		ret.pixelFormat = readUInt<uint32_t>(it1);
+
+		ret.size.width = readUInt<uint32_t>(it1 + 4);
+		ret.size.height = readUInt<uint32_t>(it1 + 8);
+
+		return ret;
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t> &data,
+				     [[maybe_unused]] std::vector<int32_t> &fds,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data.begin(), data.end());
+	}
+
+	static IPAStream deserialize(std::vector<uint8_t>::iterator data_it1,
+				     std::vector<uint8_t>::iterator data_it2,
+				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				     [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				     [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPAStream>::deserialize(data_it1, data_it2);
+	}
+};
+
+template<>
+class IPADataSerializer<const ControlList>
+{
+public:
+	// map arg will be generated, since it's per-pipeline anyway
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlList &data, const ControlInfoMap &map,
+		  ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for serialization of ControlList";
+
+		size_t size = cs->binarySize(map);
+		std::vector<uint8_t> infoData(size);
+		ByteStreamBuffer buffer(infoData.data(), infoData.size());
+		int ret = cs->serialize(map, buffer);
+
+		if (ret < 0 || buffer.overflow()) {
+			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap";
+			return {{}, {}};
+		}
+
+		size = cs->binarySize(data);
+		std::vector<uint8_t> listData(size);
+		buffer = ByteStreamBuffer(listData.data(), listData.size());
+		ret = cs->serialize(data, buffer);
+
+		if (ret < 0 || buffer.overflow()) {
+			LOG(IPADataSerializer, Error) << "Failed to serialize ControlList";
+			return {{}, {}};
+		}
+
+		std::vector<uint8_t> data_vec;
+		appendUInt<uint32_t>(data_vec, infoData.size());
+		appendUInt<uint32_t>(data_vec, listData.size());
+		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
+		data_vec.insert(data_vec.end(), listData.begin(), listData.end());
+
+		return {data_vec, {}};
+	}
+
+	static const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlList deserialize(std::vector<uint8_t>::iterator it1,
+				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				       ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for deserialization of ControlList";
+
+		uint32_t infoDataSize = readUInt<uint32_t>(it1);
+		uint32_t listDataSize = readUInt<uint32_t>(it1 + 4);
+
+		std::vector<uint8_t>::iterator it = it1 + 8;
+
+		std::vector<uint8_t> infoData(it, it + infoDataSize);
+		std::vector<uint8_t> listData(it + infoDataSize, it + infoDataSize + listDataSize);
+
+		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
+		ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
+		/* It's fine if map is empty. */
+		if (buffer.overflow()) {
+			LOG(IPADataSerializer, Error)
+				<< "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow";
+			return ControlList();
+		}
+
+		buffer = ByteStreamBuffer(const_cast<const uint8_t *>(listData.data()), listData.size());
+		ControlList list = cs->deserialize<ControlList>(buffer);
+		if (buffer.overflow())
+			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow";
+		if (list.empty())
+			LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: empty list";
+
+		return list;
+	}
+
+	static const ControlList deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlList>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<ControlList>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlList &data, const ControlInfoMap &map,
+		  ControlSerializer *cs)
+	{
+		const ControlList &list_const = const_cast<const ControlList &>(data);
+
+		std::vector<uint8_t> data_vec;
+		std::tie(data_vec, std::ignore) =
+			IPADataSerializer<const ControlList>::serialize(list_const, map, cs);
+
+		return {data_vec, {}};
+	}
+
+	static ControlList deserialize(std::vector<uint8_t> &data,
+				       ControlSerializer *cs)
+	{
+		ControlList ret;
+		const ControlList &list = ret;
+		const_cast<ControlList &>(list) =
+			IPADataSerializer<const ControlList>::deserialize(data, cs);
+
+		return ret;
+	}
+
+	static ControlList deserialize(std::vector<uint8_t>::iterator it1,
+				       [[maybe_unused]] std::vector<uint8_t>::iterator it2,
+				       ControlSerializer *cs)
+	{
+		ControlList ret;
+		const ControlList &list = ret;
+		const_cast<ControlList &>(list) =
+			IPADataSerializer<const ControlList>::deserialize(it1, it2, cs);
+
+		return ret;
+	}
+
+	static ControlList deserialize(std::vector<uint8_t> &data,
+				       [[maybe_unused]] std::vector<int32_t> &fds,
+				       ControlSerializer *cs)
+	{
+		ControlList ret;
+		const ControlList &list = ret;
+		const_cast<ControlList &>(list) =
+			IPADataSerializer<const ControlList>::deserialize(data, fds, cs);
+
+		return ret;
+	}
+
+	static ControlList deserialize(std::vector<uint8_t>::iterator data_it1,
+				       std::vector<uint8_t>::iterator data_it2,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+				       [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+				       ControlSerializer *cs)
+	{
+		ControlList ret;
+		const ControlList &list = ret;
+		const_cast<ControlList &>(list) =
+			IPADataSerializer<const ControlList>::deserialize(data_it1, data_it2,
+									  fds_it1, fds_it2, cs);
+
+		return ret;
+	}
+};
+
+template<>
+class IPADataSerializer<const ControlInfoMap>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlInfoMap &map, ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for serialization of ControlInfoMap";
+
+		size_t size = cs->binarySize(map);
+		std::vector<uint8_t> infoData(size);
+		ByteStreamBuffer buffer(infoData.data(), infoData.size());
+		int ret = cs->serialize(map, buffer);
+
+		if (ret < 0 || buffer.overflow())
+			return {{}, {}};
+
+		std::vector<uint8_t> data_vec;
+		appendUInt<uint32_t>(data_vec, infoData.size());
+		data_vec.insert(data_vec.end(), infoData.begin(), infoData.end());
+
+		return {data_vec, {}};
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
+						ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
+						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
+						ControlSerializer *cs)
+	{
+		if (!cs)
+			LOG(IPADataSerializer, Fatal)
+				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
+
+		uint32_t infoDataSize = readUInt<uint32_t>(it1);
+
+		std::vector<uint8_t>::iterator it = it1 + 4;
+
+		std::vector<uint8_t> infoData(it, it + infoDataSize);
+
+		ByteStreamBuffer buffer(const_cast<const uint8_t *>(infoData.data()), infoData.size());
+		const ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);
+
+		return map;
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
+						[[maybe_unused]] std::vector<int32_t> &fds,
+						ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data.begin(), data.end(), cs);
+	}
+
+	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
+					  std::vector<uint8_t>::iterator data_it2,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs)
+	{
+		return IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2, cs);
+	}
+};
+
+template<>
+class IPADataSerializer<ControlInfoMap>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs)
+	{
+		const ControlInfoMap &map_const = const_cast<const ControlInfoMap &>(map);
+
+		std::vector<uint8_t> data_vec;
+		std::tie(data_vec, std::ignore) =
+			IPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);
+
+		return {data_vec, {}};
+	}
+
+	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
+					  ControlSerializer *cs)
+	{
+		ControlInfoMap ret;
+		const ControlInfoMap &map = ret;
+		const_cast<ControlInfoMap &>(map) =
+			IPADataSerializer<const ControlInfoMap>::deserialize(data, cs);
+
+		return ret;
+	}
+
+	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator it1,
+						[[maybe_unused]] std::vector<uint8_t>::iterator it2,
+						ControlSerializer *cs)
+	{
+		ControlInfoMap ret;
+		const ControlInfoMap &map = ret;
+		const_cast<ControlInfoMap &>(map) =
+			IPADataSerializer<const ControlInfoMap>::deserialize(it1, it2, cs);
+
+		return ret;
+	}
+
+	static ControlInfoMap deserialize(std::vector<uint8_t> &data,
+						[[maybe_unused]] std::vector<int32_t> &fds,
+						ControlSerializer *cs)
+	{
+		ControlInfoMap ret;
+		const ControlInfoMap &map = ret;
+		const_cast<ControlInfoMap &>(map) =
+			IPADataSerializer<const ControlInfoMap>::deserialize(data, fds, cs);
+
+		return ret;
+	}
+
+	static ControlInfoMap deserialize(std::vector<uint8_t>::iterator data_it1,
+					  std::vector<uint8_t>::iterator data_it2,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it1,
+					  [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					  ControlSerializer *cs)
+	{
+		ControlInfoMap ret;
+		const ControlInfoMap &map = ret;
+		const_cast<ControlInfoMap &>(map) =
+			IPADataSerializer<const ControlInfoMap>::deserialize(data_it1, data_it2,
+									     fds_it1, fds_it2, cs);
+
+		return ret;
+	}
+};
+
+template<>
+class IPADataSerializer<FrameBuffer::Plane>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+		std::vector<int32_t> fds_vec;
+
+		// fd
+		std::vector<uint8_t> fdBuf;
+		std::vector<int32_t> fdFds;
+		std::tie(fdBuf, fdFds) =
+			IPADataSerializer<FileDescriptor>::serialize(data.fd);
+		data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end());
+		fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end());
+
+		// length
+		appendUInt<uint32_t>(data_vec, data.length);
+
+		return {data_vec, fds_vec};
+	}
+
+	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
+					      std::vector<int32_t> &fds,
+					      ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<FrameBuffer::Plane>::deserialize(data.begin(), data.end(),
+									  fds.begin(), fds.end(),
+									  cs);
+	}
+
+	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator data_it1,
+					      [[maybe_unused]] std::vector<uint8_t>::iterator data_it2,
+					      std::vector<int32_t>::iterator fds_it1,
+					      [[maybe_unused]] std::vector<int32_t>::iterator fds_it2,
+					      [[maybe_unused]] ControlSerializer *cs = nullptr)
+	{
+		FrameBuffer::Plane ret;
+
+		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(data_it1, data_it1 + 1,
+									fds_it1, fds_it1 + 1);
+		ret.length = readUInt<uint32_t>(data_it1 + 1);
+
+		return ret;
+	}
+};
+
+
+template<>
+class IPADataSerializer<IPABuffer>
+{
+public:
+	static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
+	serialize(const IPABuffer &data, ControlSerializer *cs = nullptr)
+	{
+		std::vector<uint8_t> data_vec;
+
+		appendUInt<uint32_t>(data_vec, data.id);
+
+		std::vector<uint8_t> planes_data_vec;
+		std::vector<int32_t> planes_fds_vec;
+		std::tie(planes_data_vec, planes_fds_vec) =
+			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
+
+		data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end());
+
+		return {data_vec, planes_fds_vec};
+	}
+
+	static IPABuffer deserialize(std::vector<uint8_t> &data,
+				     std::vector<int32_t> &fds,
+				     ControlSerializer *cs = nullptr)
+	{
+		return IPADataSerializer<IPABuffer>::deserialize(data.begin(), data.end(),
+								 fds.begin(), fds.end(), cs);
+	}
+
+	static IPABuffer deserialize(std::vector<uint8_t>::iterator data_it1,
+				     std::vector<uint8_t>::iterator data_it2,
+				     std::vector<int32_t>::iterator fds_it1,
+				     std::vector<int32_t>::iterator fds_it2,
+				     ControlSerializer *cs = nullptr)
+	{
+		IPABuffer ret;
+
+		ret.id = readUInt<uint32_t>(data_it1);
+
+		ret.planes =
+			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
+				data_it1 + 4, data_it2, fds_it1, fds_it2, cs);
+
+		return ret;
+	}
+};
+
+#endif /* __DOXYGEN__ */
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */
diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp
new file mode 100644
index 00000000..5029cdf6
--- /dev/null
+++ b/src/libcamera/ipa_data_serializer.cpp
@@ -0,0 +1,154 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_data_serializer.cpp - Image Processing Algorithm data serializer
+ */
+
+#include "libcamera/internal/ipa_data_serializer.h"
+
+#include "libcamera/internal/log.h"
+
+/**
+ * \file ipa_data_serializer.h
+ * \brief IPA Data Serializer
+ */
+
+namespace libcamera {
+
+LOG_DEFINE_CATEGORY(IPADataSerializer)
+
+/**
+ * \class IPADataSerializer
+ * \brief IPA Data Serializer
+ *
+ * Static template class that provides functions for serializing and
+ * deserializing IPA data.
+ */
+
+/**
+ * \fn template<typename T> void appendUInt(std::vector<uint8_t> &vec, T val)
+ * \brief Append uint to end of byte vector, in little-endian order
+ * \tparam T Type of uint to append
+ * \param[in] vec Byte vector to append to
+ * \param[in] val Value of uint to append
+ */
+
+/**
+ * \fn template<typename T> T readUInt(std::vector<uint8_t> &vec, size_t pos)
+ * \brief Read uint from byte vector, in little-endian order
+ * \tparam T Type of uint to read
+ * \param[in] vec Byte vector to read from
+ * \param[in] pos Index in vec to start reading from
+ *
+ * \return The uint read from \a vec, or 0 if reading goes past end of \a vec
+ */
+
+/**
+ * \fn template<typename T> T readUInt(std::vector<uint8_t>::iterator it)
+ * \brief Read uint from byte vector, in little-endian order
+ * \tparam T Type of uint to read
+ * \param[in] it Iterator of byte vector to read from
+ *
+ * \return The uint read from \a vec
+ */
+
+/**
+ * \fn template<typename T> IPADataSerializer<T>::serialize(
+ * 	T data,
+ * 	ControlSerializer *cs = nullptr)
+ * \brief Serialize an object into byte vector and fd vector
+ * \tparam T Type of object to serialize
+ * \param[in] data Object to serialize
+ * \param[in] cs ControlSerializer
+ *
+ * \a cs is only necessary if the object type \a T or its members contain
+ * ControlList or ControlInfoMap.
+ *
+ * \return Tuple of byte vector and fd vector, that is the serialized form
+ * of \a data
+ */
+
+/**
+ * \fn template<typename T> IPADataSerializer<T>::deserialize(
+ * 	std::vector<uint8_t> &data,
+ * 	ControlSerializer *cs = nullptr)
+ * \brief Deserialize byte vector into an object
+ * \tparam T Type of object to deserialize to
+ * \param[in] data Byte vector to deserialize from
+ * \param[in] cs ControlSerializer
+ *
+ * This version of deserialize() can be used if the object type \a T and its
+ * members don't have any FileDescriptor.
+ *
+ * \a cs is only necessary if the object type \a T or its members contain
+ * ControlList or ControlInfoMap.
+ *
+ * \return The deserialized object
+ */
+
+/**
+ * \fn template<typename T> IPADataSerializer<T>::deserialize(
+ * 	std::vector<uint8_t>::iterator it1,
+ * 	std::vector<uint8_t>::iterator it2,
+ * 	ControlSerializer *cs = nullptr)
+ * \brief Deserialize byte vector into an object
+ * \tparam T Type of object to deserialize to
+ * \param[in] it1 Begin iterator of byte vector to deserialize from
+ * \param[in] it2 End iterator of byte vector to deserialize from
+ * \param[in] cs ControlSerializer
+ *
+ * This version of deserialize() can be used if the object type \a T and its
+ * members don't have any FileDescriptor.
+ *
+ * \a cs is only necessary if the object type \a T or its members contain
+ * ControlList or ControlInfoMap.
+ *
+ * \return The deserialized object
+ */
+
+/**
+ * \fn template<typename T> IPADataSerializer<T>::deserialize(
+ * 	std::vector<uint8_t> &data,
+ * 	std::vector<int32_t> &fds,
+ * 	ControlSerializer *cs = nullptr)
+ * \brief Deserialize byte vector and fd vector into an object
+ * \tparam T Type of object to deserialize to
+ * \param[in] data Byte vector to deserialize from
+ * \param[in] fds Fd vector to deserialize from
+ * \param[in] cs ControlSerializer
+ *
+ * This version of deserialize() (or the iterator version) must be used if
+ * the object type \a T or its members contain FileDescriptor.
+ *
+ * \a cs is only necessary if the object type \a T or its members contain
+ * ControlList or ControlInfoMap.
+ *
+ * \return The deserialized object
+ */
+
+/**
+ * \fn template<typename T> IPADataSerializer::deserialize(
+ * 	std::vector<uint8_t>::iterator data_it1,
+ * 	std::vector<uint8_t>::iterator data_it2,
+ * 	std::vector<int32_t>::iterator fds_it1,
+ * 	std::vector<int32_t>::iterator fds_it2,
+ * 	ControlSerializer *cs = nullptr)
+ * \brief Deserialize byte vector and fd vector into an object
+ * \tparam T Type of object to deserialize to
+ * \param[in] data_it1 Begin iterator of byte vector to deserialize from
+ * \param[in] data_it2 End iterator of byte vector to deserialize from
+ * \param[in] fds_it1 Begin iterator of fd vector to deserialize from
+ * \param[in] fds_it2 End iterator of fd vector to deserialize from
+ * \param[in] cs ControlSerializer
+ *
+ * This version of deserialize() (or the vector version) must be used if
+ * the object type \a T or its members contain FileDescriptor.
+ *
+ * \a cs is only necessary if the object type \a T or its members contain
+ * ControlList or ControlInfoMap.
+ *
+ * \return The deserialized object
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 9d7442fa..61aad08e 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -23,6 +23,7 @@  libcamera_sources = files([
     'geometry.cpp',
     'ipa_context_wrapper.cpp',
     'ipa_controls.cpp',
+    'ipa_data_serializer.cpp',
     'ipa_interface.cpp',
     'ipa_manager.cpp',
     'ipa_module.cpp',