[libcamera-devel] [PATCH v4 08/37] libcamera: Add IPADataSerializer

Jacopo Mondi jacopo at jmondi.org
Tue Nov 17 15:48:05 CET 2020


Hi Paul,

On Fri, Nov 06, 2020 at 07:36:38PM +0900, Paul Elder wrote:
> Add an IPADataSerializer which implments de/serialization of built-in
> (PODs, vector, map, string) 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 at ideasonboard.com>
>
> ---
> Changes in v4:
> - rename readUInt/appendUInt to readPOD/appendPOD
>   - put them in anonymous namespace
>   - and add length check
>     - fatal if failure, because it means not enough data to deserialize,
>       which is technically a segfault
>   - use the new readPOD/appendPOD with length protections
>   - expand on their docs correspondingly
> - change snake_case to camelCase
> - add optimizations to the hand-written de/serializers
> - reserve vector length where trivially possible
> - remove unnecessary IPADataSerializer<type>:: explicit calls (if
>   they're calling a specialization from the same specialization)
>
> Changes in v3:
> - reimplement append/readUInt with memcpy (intead of bit shifting)
> - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER
>   - use this for int64_t, bool, float, and double
> - fix comment style
>
> 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  | 1004 +++++++++++++++++
>  src/libcamera/ipa_data_serializer.cpp         |  178 +++
>  src/libcamera/meson.build                     |    1 +
>  3 files changed, 1183 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..9a18f914
> --- /dev/null
> +++ b/include/libcamera/internal/ipa_data_serializer.h
> @@ -0,0 +1,1004 @@
> +/* 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 <string.h>
> +#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)
> +
> +namespace {
> +
> +template<typename T>
> +void appendPOD(std::vector<uint8_t> &vec, T val)
> +{
> +	size_t byteWidth = sizeof(val);
> +	std::vector<uint8_t> v(byteWidth);
> +	memcpy(&v[0], &val, byteWidth);
> +
> +	vec.insert(vec.end(), v.begin(), v.end());
> +}
> +
> +template<typename T>
> +T readPOD(std::vector<uint8_t> &vec, size_t pos)
> +{
> +	T ret = 0;
> +	size_t byteWidth = sizeof(ret);
> +	if (pos + byteWidth > vec.size())
> +		LOG(IPADataSerializer, Fatal)
> +			<< "Not enough data to deserialize POD";
> +
> +	memcpy(&ret, &vec[pos], byteWidth);
> +	return ret;
> +}

Can this function just be:
	return readPOD<T>(vec.begin(), pos, vec.end());

> +
> +template<typename T>
> +T readPOD(std::vector<uint8_t>::iterator it, size_t pos,
> +	  std::vector<uint8_t>::iterator end)
> +{
> +	T ret = 0;
> +	size_t byteWidth = sizeof(ret);
> +
Can you align to this style (empty line between the variable declarations
and the if()) in the two readPOD implementations ?

> +	if (pos + it >= end)
> +		LOG(IPADataSerializer, Fatal)
> +			<< "Not enough data to deserialize POD";
> +
> +	std::vector<uint8_t> v(it + pos, it + pos + byteWidth);
> +	memcpy(&ret, v.data(), byteWidth);

Can you
	it += pos;
	memcpy(&ret, &(*it), byteWidth);

Instead of going through a vector ?

The end result looks like:

template<typename T>
T readPOD(std::vector<uint8_t>::iterator it, size_t pos,
	  std::vector<uint8_t>::iterator end)
{
	if (pos + it >= end)
		LOG(IPADataSerializer, Fatal)
			<< "Not enough data to deserialize POD";

	T ret = 0;
	memcpy(&ret, &(*(it + pos)), sizeof(ret));

	return ret;
}

template<typename T>
T readPOD(std::vector<uint8_t> &vec, size_t pos)
{
	return readPOD<T>(vec.begin(), pos, vec.end());
}

(I would have liked to run tests, but serialization tests get skipped
and I've not investigated tbh)

> +	return ret;
> +}
> +
> +} /* namespace */
> +
> +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 dataBegin,
> +			     std::vector<uint8_t>::iterator dataEnd,
> +			     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 dataBegin,
> +			     std::vector<uint8_t>::iterator dataEnd,
> +			     std::vector<int32_t>::iterator fdsBegin,
> +			     std::vector<int32_t>::iterator fdsEnd,
> +			     ControlSerializer *cs);
> +#endif /* __DOXYGEN__ */

I've already expressed my perplexity for this #ifdef, and I know you
have answered but I didn't get it :) you want this parsed by doxygen,
isn't it equal from removing the ifdef guard ?

> +};
> +
> +#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> dataVec;
> +		std::vector<int32_t> fdsVec;
> +
> +		/* Serialize the length. */
> +		uint32_t vecLen = data.size();
> +		appendPOD<uint32_t>(dataVec, vecLen);
> +
> +		/* Serialize the members. */
> +		for (auto const &it : data) {
> +			std::vector<uint8_t> dvec;
> +			std::vector<int32_t> fvec;
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<V>::serialize(it, cs);
> +
> +			appendPOD<uint32_t>(dataVec, dvec.size());
> +			appendPOD<uint32_t>(dataVec, fvec.size());
> +
> +			dataVec.insert(dataVec.end(), dvec.begin(), dvec.end());
> +			fdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());
> +		}
> +
> +		return {dataVec, fdsVec};
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  std::vector<uint8_t>::iterator dataEnd,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<int32_t> fds;
> +		return deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);
> +	}
> +
> +	static std::vector<V> deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +					  std::vector<int32_t>::iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +		std::vector<V> ret(vecLen);
> +
> +		std::vector<uint8_t>::iterator dataIter = dataBegin + 4;
> +		std::vector<int32_t>::iterator fdIter = fdsBegin;
> +		for (uint32_t i = 0; i < vecLen; i++) {
> +			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> +			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> +
> +			ret[i] = IPADataSerializer<V>::deserialize(dataIter + 8,
> +								   dataIter + 8 + sizeofData,
> +								   fdIter,
> +								   fdIter + sizeofFds,
> +								   cs);
> +
> +			dataIter += 8 + sizeofData;
> +			fdIter += sizeofFds;
> +		}
> +
> +		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> dataVec;
> +		std::vector<int32_t> fdsVec;
> +
> +		/* Serialize the length. */
> +		uint32_t mapLen = data.size();
> +		appendPOD<uint32_t>(dataVec, mapLen);
> +
> +		/* Serialize the members. */
> +		for (auto const &it : data) {
> +			std::vector<uint8_t> dvec;
> +			std::vector<int32_t> fvec;
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<K>::serialize(it.first, cs);
> +
> +			appendPOD<uint32_t>(dataVec, dvec.size());
> +			appendPOD<uint32_t>(dataVec, fvec.size());
> +
> +			dataVec.insert(dataVec.end(), dvec.begin(), dvec.end());
> +			fdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());
> +
> +			std::tie(dvec, fvec) =
> +				IPADataSerializer<V>::serialize(it.second, cs);
> +
> +			appendPOD<uint32_t>(dataVec, dvec.size());
> +			appendPOD<uint32_t>(dataVec, fvec.size());
> +
> +			dataVec.insert(dataVec.end(), dvec.begin(), dvec.end());
> +			fdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end());
> +		}
> +
> +		return {dataVec, fdsVec};
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t> &data, ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  std::vector<uint8_t>::iterator dataEnd,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::vector<int32_t> fds;
> +		return deserialize(dataBegin, dataEnd, 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 deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);
> +	}
> +
> +	static std::map<K, V> deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +					  std::vector<int32_t>::iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					  ControlSerializer *cs = nullptr)
> +	{
> +		std::map<K, V> ret;
> +
> +		uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +
> +		std::vector<uint8_t>::iterator dataIter = dataBegin + 4;
> +		std::vector<int32_t>::iterator fdIter = fdsBegin;
> +		for (uint32_t i = 0; i < mapLen; i++) {
> +			uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> +			uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> +
> +			K key = IPADataSerializer<K>::deserialize(dataIter + 8,
> +								  dataIter + 8 + sizeofData,
> +								  fdIter,
> +								  fdIter + sizeofFds,
> +								  cs);
> +
> +			dataIter += 8 + sizeofData;
> +			fdIter += sizeofFds;
> +			sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);
> +			sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);
> +
> +			const V value = IPADataSerializer<V>::deserialize(dataIter + 8,
> +									  dataIter + 8 + sizeofData,
> +									  fdIter,
> +									  fdIter + sizeofFds,
> +									  cs);
> +			ret.insert({key, value});
> +
> +			dataIter += 8 + sizeofData;
> +			fdIter += sizeofFds;
> +		}
> +
> +		return ret;
> +	}
> +};
> +
> +#define DECLARE_POD_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> dataVec;				\
> +		dataVec.reserve(sizeof(type));				\
> +		appendPOD<type>(dataVec, data);			\
> +									\
> +		return {dataVec, {}};					\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t> &data,		\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return deserialize(data.begin(), data.end());		\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t>::iterator dataBegin,\
> +				[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return readPOD<type>(dataBegin, 0, dataEnd);		\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t> &data,		\
> +				[[maybe_unused]] std::vector<int32_t> &fds,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return deserialize(data.begin(), data.end());		\
> +	}								\
> +									\
> +	static type deserialize(std::vector<uint8_t>::iterator dataBegin,\
> +				std::vector<uint8_t>::iterator dataEnd,\
> +				[[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,\
> +				[[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,\
> +				[[maybe_unused]] ControlSerializer *cs = nullptr)\
> +	{								\
> +		return deserialize(dataBegin, dataEnd);			\
> +	}								\
> +};
> +
> +DECLARE_POD_SERIALIZER(bool)
> +DECLARE_POD_SERIALIZER(uint8_t)
> +DECLARE_POD_SERIALIZER(uint16_t)
> +DECLARE_POD_SERIALIZER(uint32_t)
> +DECLARE_POD_SERIALIZER(uint64_t)
> +DECLARE_POD_SERIALIZER(int8_t)
> +DECLARE_POD_SERIALIZER(int16_t)
> +DECLARE_POD_SERIALIZER(int32_t)
> +DECLARE_POD_SERIALIZER(int64_t)
> +DECLARE_POD_SERIALIZER(float)
> +DECLARE_POD_SERIALIZER(double)
> +
> +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)
> +	{
> +		return {{data.begin(), data.end()}, {}};
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return {data.begin(), data.end()};
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return {dataBegin, dataEnd};
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t> &data,
> +				       [[maybe_unused]] std::vector<int32_t> &fds,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return {data.begin(), data.end()};
> +	}
> +
> +	static std::string deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return {dataBegin, dataEnd};
> +	}
> +};
> +
> +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> dataVec = { data.isValid() };
> +		std::vector<int32_t> fdVec;
> +		if (data.isValid())
> +			fdVec.push_back(data.fd());
> +
> +		return {dataVec, fdVec};
> +	}
> +
> +	static FileDescriptor deserialize(std::vector<uint8_t> &data, std::vector<int32_t> &fds,
> +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), fds.begin(), fds.end());
> +	}
> +
> +	static FileDescriptor deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  std::vector<uint8_t>::iterator dataEnd,
> +					  std::vector<int32_t>::iterator fdsBegin,
> +					  std::vector<int32_t>::iterator fdsEnd,
> +					  [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		if (std::distance(dataBegin, dataEnd) < 1)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "Invalid data to deserialize FileDescriptor";
> +
> +		bool valid = !!(*dataBegin);
> +
> +		if (valid && std::distance(fdsBegin, fdsEnd) < 1)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "Invalid fds to deserialize FileDescriptor";
> +
> +		return valid ? FileDescriptor(*fdsBegin) : 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<std::string>::deserialize(data.begin(), data.end()) };
> +	}
> +
> +	static IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };
> +	}
> +
> +	static IPASettings 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 IPASettings deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +				       [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return { IPADataSerializer<std::string>::deserialize(dataBegin, dataEnd) };
> +	}
> +};
> +
> +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> dataVec;
> +
> +		uint32_t strLen = data.model.size();
> +		appendPOD<uint32_t>(dataVec, strLen);
> +
> +		dataVec.insert(dataVec.end(), data.model.begin(), data.model.end());

Can this be done using the <string> IPADataSerializer specialization ?

> +
> +		appendPOD<uint32_t>(dataVec, data.bitsPerPixel);
> +
> +		appendPOD<uint32_t>(dataVec, data.activeAreaSize.width);
> +		appendPOD<uint32_t>(dataVec, data.activeAreaSize.height);
> +
> +		appendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.x));
> +		appendPOD<uint32_t>(dataVec, static_cast<uint32_t>(data.analogCrop.y));
> +		appendPOD<uint32_t>(dataVec, data.analogCrop.width);
> +		appendPOD<uint32_t>(dataVec, data.analogCrop.height);

Depending on how many users we could potentially have <Size> and
<Rectangle> serializer might be an helpful helper

> +
> +		appendPOD<uint32_t>(dataVec, data.outputSize.width);
> +		appendPOD<uint32_t>(dataVec, data.outputSize.height);
> +
> +		appendPOD<uint64_t>(dataVec, data.pixelRate);
> +
> +		appendPOD<uint32_t>(dataVec, data.lineLength);
> +
> +		return {dataVec, {}};
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end());
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					    [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		CameraSensorInfo ret;
> +
> +		uint32_t strLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +		std::string str(dataBegin + 4, dataBegin + 4 + strLen);
> +		ret.model = str;
> +
> +		std::vector<uint8_t>::iterator it = dataBegin + 4 + strLen;
> +
> +		ret.bitsPerPixel = readPOD<uint32_t>(it, 0, dataEnd);
> +
> +		ret.activeAreaSize.width = readPOD<uint32_t>(it, 4, dataEnd);
> +		ret.activeAreaSize.height = readPOD<uint32_t>(it, 8, dataEnd);
> +
> +		ret.analogCrop.x = static_cast<int32_t>(readPOD<uint32_t>(it, 12, dataEnd));
> +		ret.analogCrop.y = static_cast<int32_t>(readPOD<uint32_t>(it, 16, dataEnd));
> +		ret.analogCrop.width = readPOD<uint32_t>(it, 20, dataEnd);
> +		ret.analogCrop.height = readPOD<uint32_t>(it, 24, dataEnd);
> +
> +		ret.outputSize.width = readPOD<uint32_t>(it, 28, dataEnd);
> +		ret.outputSize.height = readPOD<uint32_t>(it, 32, dataEnd);
> +
> +		ret.pixelRate = readPOD<uint64_t>(it, 36, dataEnd);
> +
> +		ret.lineLength = readPOD<uint32_t>(it, 44, dataEnd);
> +
> +		return ret;
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t> &data,
> +					    [[maybe_unused]] std::vector<int32_t> &fds,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end());
> +	}
> +
> +	static CameraSensorInfo deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					    std::vector<uint8_t>::iterator dataEnd,
> +					    [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +					    [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					    [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(dataBegin, dataEnd);
> +	}
> +};
> +
> +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> dataVec;
> +
> +		appendPOD<uint32_t>(dataVec, data.pixelFormat);
> +
> +		appendPOD<uint32_t>(dataVec, data.size.width);
> +		appendPOD<uint32_t>(dataVec, data.size.height);
> +
> +		return {dataVec, {}};
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t> &data,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				     [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		IPAStream ret;
> +
> +		ret.pixelFormat = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +
> +		ret.size.width = readPOD<uint32_t>(dataBegin, 4, dataEnd);
> +		ret.size.height = readPOD<uint32_t>(dataBegin, 8, dataEnd);
> +
> +		return ret;
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t> &data,
> +				     [[maybe_unused]] std::vector<int32_t> &fds,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end());
> +	}
> +
> +	static IPAStream deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				     std::vector<uint8_t>::iterator dataEnd,
> +				     [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +				     [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +				     [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(dataBegin, dataEnd);
> +	}
> +};
> +
> +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> dataVec;
> +		dataVec.reserve(8 + infoData.size() + listData.size());
> +		appendPOD<uint32_t>(dataVec, infoData.size());
> +		appendPOD<uint32_t>(dataVec, listData.size());
> +		dataVec.insert(dataVec.end(), infoData.begin(), infoData.end());
> +		dataVec.insert(dataVec.end(), listData.begin(), listData.end());
> +
> +		return {dataVec, {}};
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t> &data, ControlSerializer *cs)
> +	{
> +		return deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +				       ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for deserialization of ControlList";
> +
> +		uint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +		uint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);
> +
> +		std::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlList deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +				       ControlSerializer *cs)
> +	{
> +		return deserialize(dataBegin, dataEnd, 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> dataVec;
> +		std::tie(dataVec, std::ignore) =
> +			IPADataSerializer<const ControlList>::serialize(list_const, map, cs);
> +
> +		return {dataVec, {}};
> +	}
> +
> +	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 dataBegin,
> +				       [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(dataBegin, dataEnd, 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 dataBegin,
> +				       std::vector<uint8_t>::iterator dataEnd,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +				       [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +				       ControlSerializer *cs)
> +	{
> +		ControlList ret;
> +		const ControlList &list = ret;
> +		const_cast<ControlList &>(list) =
> +			IPADataSerializer<const ControlList>::deserialize(dataBegin, dataEnd,
> +									  fdsBegin, fdsEnd, 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 {{}, {}};

Error message maybe ?

> +
> +		std::vector<uint8_t> dataVec;
> +		appendPOD<uint32_t>(dataVec, infoData.size());
> +		dataVec.insert(dataVec.end(), infoData.begin(), infoData.end());
> +
> +		return {dataVec, {}};
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t> &data,
> +						ControlSerializer *cs)
> +	{
> +		return deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,
> +						[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +						ControlSerializer *cs)
> +	{
> +		if (!cs)
> +			LOG(IPADataSerializer, Fatal)
> +				<< "ControlSerializer not provided for deserialization of ControlInfoMap";
> +
> +		uint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +
> +		std::vector<uint8_t>::iterator it = dataBegin + 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 deserialize(data.begin(), data.end(), cs);
> +	}
> +
> +	static const ControlInfoMap deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					  std::vector<uint8_t>::iterator dataEnd,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					  ControlSerializer *cs)
> +	{
> +		return deserialize(dataBegin, dataEnd, 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> dataVec;
> +		std::tie(dataVec, std::ignore) =
> +			IPADataSerializer<const ControlInfoMap>::serialize(map_const, cs);
> +
> +		return {dataVec, {}};
> +	}
> +
> +	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 dataBegin,
> +						[[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +						ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(dataBegin, dataEnd, 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 dataBegin,
> +					  std::vector<uint8_t>::iterator dataEnd,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsBegin,
> +					  [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					  ControlSerializer *cs)
> +	{
> +		ControlInfoMap ret;
> +		const ControlInfoMap &map = ret;
> +		const_cast<ControlInfoMap &>(map) =
> +			IPADataSerializer<const ControlInfoMap>::deserialize(dataBegin, dataEnd,
> +									     fdsBegin, fdsEnd, 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> dataVec;
> +		std::vector<int32_t> fdsVec;
> +
> +		std::vector<uint8_t> fdBuf;
> +		std::vector<int32_t> fdFds;
> +		std::tie(fdBuf, fdFds) =
> +			IPADataSerializer<FileDescriptor>::serialize(data.fd);
> +		dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
> +		fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());
> +
> +		appendPOD<uint32_t>(dataVec, data.length);
> +
> +		return {dataVec, fdsVec};
> +	}
> +
> +	static FrameBuffer::Plane deserialize(std::vector<uint8_t> &data,
> +					      std::vector<int32_t> &fds,
> +					      ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);
> +	}
> +
> +	static FrameBuffer::Plane deserialize(std::vector<uint8_t>::iterator dataBegin,
> +					      [[maybe_unused]] std::vector<uint8_t>::iterator dataEnd,
> +					      std::vector<int32_t>::iterator fdsBegin,
> +					      [[maybe_unused]] std::vector<int32_t>::iterator fdsEnd,
> +					      [[maybe_unused]] ControlSerializer *cs = nullptr)
> +	{
> +		FrameBuffer::Plane ret;
> +
> +		ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 1,
> +									fdsBegin, fdsBegin + 1);
> +		ret.length = readPOD<uint32_t>(dataBegin, 1, dataEnd);
> +
> +		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> dataVec;
> +
> +		appendPOD<uint32_t>(dataVec, data.id);
> +
> +		std::vector<uint8_t> planesDataVec;
> +		std::vector<int32_t> planesFdsVec;
> +		std::tie(planesDataVec, planesFdsVec) =
> +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::serialize(data.planes, cs);
> +
> +		dataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end());
> +
> +		return {dataVec, planesFdsVec};
> +	}
> +
> +	static IPABuffer deserialize(std::vector<uint8_t> &data,
> +				     std::vector<int32_t> &fds,
> +				     ControlSerializer *cs = nullptr)
> +	{
> +		return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs);
> +	}
> +
> +	static IPABuffer deserialize(std::vector<uint8_t>::iterator dataBegin,
> +				     std::vector<uint8_t>::iterator dataEnd,
> +				     std::vector<int32_t>::iterator fdsBegin,
> +				     std::vector<int32_t>::iterator fdsEnd,
> +				     ControlSerializer *cs = nullptr)
> +	{
> +		IPABuffer ret;
> +
> +		ret.id = readPOD<uint32_t>(dataBegin, 0, dataEnd);
> +
> +		ret.planes =
> +			IPADataSerializer<std::vector<FrameBuffer::Plane>>::deserialize(
> +				dataBegin + 4, dataEnd, fdsBegin, fdsEnd, 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..eb3d6362
> --- /dev/null
> +++ b/src/libcamera/ipa_data_serializer.cpp
> @@ -0,0 +1,178 @@
> +/* 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.
> + */
> +
> +namespace {
> +
> +/**
> + * \fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)
> + * \brief Append uint to end of byte vector, in little-endian order

s/uint/POD/

> + * \tparam T Type of uint to append

s/uint/POD/

> + * \param[in] vec Byte vector to append to
> + * \param[in] val Value of uint to append

s/of uint//

> + *
> + * This function is meant to be used by the IPA data serializer, and the
> + * generated IPA proxies.
> + */
> +
> +/**
> + * \fn template<typename T> T readPOD(std::vector<uint8_t> &vec, size_t pos)
> + * \brief Read POD from byte vector, in little-endian order
> + * \tparam T Type of POD to read
> + * \param[in] vec Byte vector to read from
> + * \param[in] pos Index in vec to start reading from
> + *
> + * This function is meant to be used by the IPA data serializer, and the
> + * generated IPA proxies.
> + *
> + * If the \a pos plus the byte-width of the desired POD is past the end of
> + * \a vec, a fatal error will occur, as it means there is insufficient data
> + * for deserialization, which should never happen.
> + *
> + * \return The POD read from \a vec at index \a pos
> + */
> +
> +/**
> + * \fn template<typename T> T readPOD(std::vector<uint8_t>::iterator it, size_t pos,
> + * 				      std::vector<uint8_t>::iterator end)
> + * \brief Read POD from byte vector, in little-endian order
> + * \tparam T Type of POD to read
> + * \param[in] it Iterator of byte vector to read from
> + * \param[in] pos Index in byte vector to read from
> + * \param[in] end Iterator marking end of byte vector
> + *
> + * This function is meant to be used by the IPA data serializer, and the
> + * generated IPA proxies.
> + *
> + * If the \a pos plus the byte-width of the desired POD is past \a end, it is
> + * a fata error will occur, as it means there is insufficient data for
> + * deserialization, which should never happen.
> + *
> + * \return The POD read from \a it at index \a pos
> + */
> +
> +} /* namespace */
> +
> +/**
> + * \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 dataBegin,
> + * 	std::vector<uint8_t>::iterator dataEnd,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] dataBegin Begin iterator of byte vector to deserialize from
> + * \param[in] dataEnd 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 dataBegin,
> + * 	std::vector<uint8_t>::iterator dataEnd,
> + * 	std::vector<int32_t>::iterator fdsBegin,
> + * 	std::vector<int32_t>::iterator fdsEnd,
> + * 	ControlSerializer *cs = nullptr)
> + * \brief Deserialize byte vector and fd vector into an object
> + * \tparam T Type of object to deserialize to
> + * \param[in] dataBegin Begin iterator of byte vector to deserialize from
> + * \param[in] dataEnd End iterator of byte vector to deserialize from
> + * \param[in] fdsBegin Begin iterator of fd vector to deserialize from
> + * \param[in] fdsEnd 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 99b27e66..01bcffd4 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -24,6 +24,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',

Still wishing for the serialization format for each type to be
described, but this version is very nice according to my tastes!

Thanks for keep pushing and addressing the numerous comments!

Reviewed-by: Jacopo Mondi <jacopo at jmondi.org>

Thanks
  j

> --
> 2.27.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel


More information about the libcamera-devel mailing list