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

paul.elder at ideasonboard.com paul.elder at ideasonboard.com
Fri Nov 13 07:41:48 CET 2020


Hi Niklas,

On Thu, Nov 12, 2020 at 12:25:01PM +0100, Niklas Söderlund wrote:
> Hi Paul,
> 
> Thanks for your patch.
> 
> On 2020-11-06 19:36:38 +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.
> 
> As this is a rather large patch in a rather large series I wonder if 
> this could be broken out into it's own series together with it's test 
> cases. Perhaps this patch could even be broken up to smaller bits each 
> dealing with a specific data type?

Maybe? It's like:
- read/appendPOD()
- base de/serializer (to appease doxygen)
- vector and map de/serializer
- PODs and std::string de/serializer
- libcamera IPA-related structs de/serializer
- docs

The bulk of the code, the de/serializers, are all slighly modified
versions of each other.

> If so I think this together with the tests could be merged as soon as 
> they are ready and just listed as a dependency to the main series. What 
> do others think about this?

I'm fine with it of course :)


Paul

> > 
> > 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;
> > +}
> > +
> > +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);
> > +
> > +	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);
> > +	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__ */
> > +};
> > +
> > +#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());
> > +
> > +		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);
> > +
> > +		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 {{}, {}};
> > +
> > +		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
> > + * \tparam T Type of uint to append
> > + * \param[in] vec Byte vector to append to
> > + * \param[in] val Value of uint to append
> > + *
> > + * 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',
> > -- 
> > 2.27.0
> > 
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel at lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
> 
> -- 
> Regards,
> Niklas Söderlund


More information about the libcamera-devel mailing list