/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2020, Google Inc.
 *
 * Image Processing Algorithm data serializer
 */

#include "libcamera/internal/ipa_data_serializer.h"

#include <unistd.h>

#include <libcamera/base/log.h>

#include "libcamera/internal/byte_stream_buffer.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.
 *
 * \todo Switch to Span instead of byte and fd vector
 *
 * \todo Harden the vector and map deserializer
 *
 * \todo For SharedFDs, instead of storing a validity flag, store an
 * index into the fd array. This will allow us to use views instead of copying.
 */

namespace {

/**
 * \fn template<typename T> void appendPOD(std::vector<uint8_t> &vec, T val)
 * \brief Append POD to end of byte vector, in little-endian order
 * \tparam T Type of POD to append
 * \param[in] vec Byte vector to append to
 * \param[in] val Value 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>::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
 */

/**
 * \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
 */

} /* 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(
 * 	const 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 SharedFD.
 *
 * \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>::const_iterator dataBegin,
 * 	std::vector<uint8_t>::const_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 SharedFD.
 *
 * \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(
 * 	const std::vector<uint8_t> &data,
 * 	const std::vector<SharedFD> &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 SharedFD.
 *
 * \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>::const_iterator dataBegin,
 * 	std::vector<uint8_t>::const_iterator dataEnd,
 * 	std::vector<SharedFD>::const_iterator fdsBegin,
 * 	std::vector<SharedFD>::const_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 SharedFD.
 *
 * \a cs is only necessary if the object type \a T or its members contain
 * ControlList or ControlInfoMap.
 *
 * \return The deserialized object
 */

#ifndef __DOXYGEN__

#define DEFINE_POD_SERIALIZER(type)					\
									\
template<>								\
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>		\
IPADataSerializer<type>::serialize(const type &data,			\
				  [[maybe_unused]] ControlSerializer *cs) \
{									\
	std::vector<uint8_t> dataVec;					\
	appendPOD<type>(dataVec, data);					\
									\
	return { dataVec, {} };						\
}									\
									\
template<>								\
type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
					  std::vector<uint8_t>::const_iterator dataEnd, \
					  [[maybe_unused]] ControlSerializer *cs) \
{									\
	return readPOD<type>(dataBegin, 0, dataEnd);			\
}									\
									\
template<>								\
type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
					  ControlSerializer *cs)	\
{									\
	return deserialize(data.cbegin(), data.end(), cs);		\
}									\
									\
template<>								\
type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \
					  [[maybe_unused]] const std::vector<SharedFD> &fds, \
					  ControlSerializer *cs)	\
{									\
	return deserialize(data.cbegin(), data.end(), cs);		\
}									\
									\
template<>								\
type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \
					  std::vector<uint8_t>::const_iterator dataEnd, \
					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \
					  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \
					  ControlSerializer *cs)	\
{									\
	return deserialize(dataBegin, dataEnd, cs);			\
}

DEFINE_POD_SERIALIZER(bool)
DEFINE_POD_SERIALIZER(uint8_t)
DEFINE_POD_SERIALIZER(uint16_t)
DEFINE_POD_SERIALIZER(uint32_t)
DEFINE_POD_SERIALIZER(uint64_t)
DEFINE_POD_SERIALIZER(int8_t)
DEFINE_POD_SERIALIZER(int16_t)
DEFINE_POD_SERIALIZER(int32_t)
DEFINE_POD_SERIALIZER(int64_t)
DEFINE_POD_SERIALIZER(float)
DEFINE_POD_SERIALIZER(double)

/*
 * Strings are serialized simply by converting by {string.cbegin(), string.end()}.
 * The size of the string is recorded by the container (struct, vector, map, or
 * function parameter serdes).
 */
template<>
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
IPADataSerializer<std::string>::serialize(const std::string &data,
					  [[maybe_unused]] ControlSerializer *cs)
{
	return { { data.cbegin(), data.end() }, {} };
}

template<>
std::string
IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
					    [[maybe_unused]] ControlSerializer *cs)
{
	return { data.cbegin(), data.cend() };
}

template<>
std::string
IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					    std::vector<uint8_t>::const_iterator dataEnd,
					    [[maybe_unused]] ControlSerializer *cs)
{
	return { dataBegin, dataEnd };
}

template<>
std::string
IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,
					    [[maybe_unused]] const std::vector<SharedFD> &fds,
					    [[maybe_unused]] ControlSerializer *cs)
{
	return { data.cbegin(), data.cend() };
}

template<>
std::string
IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					    std::vector<uint8_t>::const_iterator dataEnd,
					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
					    [[maybe_unused]] ControlSerializer *cs)
{
	return { dataBegin, dataEnd };
}

/*
 * ControlList is serialized as:
 *
 * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes
 * 4 bytes - uint32_t Size of serialized ControlList, in bytes
 * X bytes - Serialized ControlInfoMap (using ControlSerializer)
 * X bytes - Serialized ControlList (using ControlSerializer)
 *
 * If data.infoMap() is nullptr, then the default controls::controls will
 * be used. The serialized ControlInfoMap will have zero length.
 */
template<>
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)
{
	if (!cs)
		LOG(IPADataSerializer, Fatal)
			<< "ControlSerializer not provided for serialization of ControlList";

	size_t size;
	std::vector<uint8_t> infoData;
	int ret;

	/*
	 * \todo Revisit this opportunistic serialization of the
	 * ControlInfoMap, as it could be fragile
	 */
	if (data.infoMap() && !cs->isCached(*data.infoMap())) {
		size = cs->binarySize(*data.infoMap());
		infoData.resize(size);
		ByteStreamBuffer buffer(infoData.data(), infoData.size());
		ret = cs->serialize(*data.infoMap(), 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);
	ByteStreamBuffer buffer(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, {} };
}

template<>
ControlList
IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					    std::vector<uint8_t>::const_iterator dataEnd,
					    ControlSerializer *cs)
{
	if (!cs)
		LOG(IPADataSerializer, Fatal)
			<< "ControlSerializer not provided for deserialization of ControlList";

	if (std::distance(dataBegin, dataEnd) < 8)
		return {};

	uint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);
	uint32_t listDataSize = readPOD<uint32_t>(dataBegin, 4, dataEnd);

	std::vector<uint8_t>::const_iterator it = dataBegin + 8;

	if (infoDataSize + listDataSize < infoDataSize ||
	    static_cast<uint32_t>(std::distance(it, dataEnd)) < infoDataSize + listDataSize)
		return {};

	if (infoDataSize > 0) {
		ByteStreamBuffer buffer(&*it, infoDataSize);
		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();
		}
	}

	it += infoDataSize;
	ByteStreamBuffer buffer(&*it, listDataSize);
	ControlList list = cs->deserialize<ControlList>(buffer);
	if (buffer.overflow())
		LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow";

	return list;
}

template<>
ControlList
IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
					    ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), cs);
}

template<>
ControlList
IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,
					    [[maybe_unused]] const std::vector<SharedFD> &fds,
					    ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), cs);
}

template<>
ControlList
IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					    std::vector<uint8_t>::const_iterator dataEnd,
					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
					    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
					    ControlSerializer *cs)
{
	return deserialize(dataBegin, dataEnd, cs);
}

/*
 * const ControlInfoMap is serialized as:
 *
 * 4 bytes - uint32_t Size of serialized ControlInfoMap, in bytes
 * X bytes - Serialized ControlInfoMap (using ControlSerializer)
 */
template<>
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
IPADataSerializer<ControlInfoMap>::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()) {
		LOG(IPADataSerializer, Error) << "Failed to serialize ControlInfoMap";
		return { {}, {} };
	}

	std::vector<uint8_t> dataVec;
	appendPOD<uint32_t>(dataVec, infoData.size());
	dataVec.insert(dataVec.end(), infoData.begin(), infoData.end());

	return { dataVec, {} };
}

template<>
ControlInfoMap
IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					       std::vector<uint8_t>::const_iterator dataEnd,
					       ControlSerializer *cs)
{
	if (!cs)
		LOG(IPADataSerializer, Fatal)
			<< "ControlSerializer not provided for deserialization of ControlInfoMap";

	if (std::distance(dataBegin, dataEnd) < 4)
		return {};

	uint32_t infoDataSize = readPOD<uint32_t>(dataBegin, 0, dataEnd);

	std::vector<uint8_t>::const_iterator it = dataBegin + 4;

	if (static_cast<uint32_t>(std::distance(it, dataEnd)) < infoDataSize)
		return {};

	ByteStreamBuffer buffer(&*it, infoDataSize);
	ControlInfoMap map = cs->deserialize<ControlInfoMap>(buffer);

	return map;
}

template<>
ControlInfoMap
IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
					       ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), cs);
}

template<>
ControlInfoMap
IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,
					       [[maybe_unused]] const std::vector<SharedFD> &fds,
					       ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), cs);
}

template<>
ControlInfoMap
IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
					       std::vector<uint8_t>::const_iterator dataEnd,
					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,
					       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
					       ControlSerializer *cs)
{
	return deserialize(dataBegin, dataEnd, cs);
}

/*
 * SharedFD instances are serialized into four bytes that tells if the SharedFD
 * is valid or not. If it is valid, then for serialization the fd will be
 * written to the fd vector, or for deserialization the fd vector const_iterator
 * will be valid.
 *
 * This validity is necessary so that we don't send -1 fd over sendmsg(). It
 * also allows us to simply send the entire fd vector into the deserializer
 * and it will be recursively consumed as necessary.
 */
template<>
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
IPADataSerializer<SharedFD>::serialize(const SharedFD &data,
				       [[maybe_unused]] ControlSerializer *cs)
{
	std::vector<uint8_t> dataVec;
	std::vector<SharedFD> fdVec;

	/*
	 * Store as uint32_t to prepare for conversion from validity flag
	 * to index, and for alignment.
	 */
	appendPOD<uint32_t>(dataVec, data.isValid());

	if (data.isValid())
		fdVec.push_back(data);

	return { dataVec, fdVec };
}

template<>
SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,
						  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,
						  std::vector<SharedFD>::const_iterator fdsBegin,
						  std::vector<SharedFD>::const_iterator fdsEnd,
						  [[maybe_unused]] ControlSerializer *cs)
{
	ASSERT(std::distance(dataBegin, dataEnd) >= 4);

	uint32_t valid = readPOD<uint32_t>(dataBegin, 0, dataEnd);

	ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));

	return valid ? *fdsBegin : SharedFD();
}

template<>
SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,
						  const std::vector<SharedFD> &fds,
						  [[maybe_unused]] ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());
}

/*
 * FrameBuffer::Plane is serialized as:
 *
 * 4 byte  - SharedFD
 * 4 bytes - uint32_t Offset
 * 4 bytes - uint32_t Length
 */
template<>
std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>
IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,
						 [[maybe_unused]] ControlSerializer *cs)
{
	std::vector<uint8_t> dataVec;
	std::vector<SharedFD> fdsVec;

	std::vector<uint8_t> fdBuf;
	std::vector<SharedFD> fdFds;
	std::tie(fdBuf, fdFds) =
		IPADataSerializer<SharedFD>::serialize(data.fd);
	dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());
	fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());

	appendPOD<uint32_t>(dataVec, data.offset);
	appendPOD<uint32_t>(dataVec, data.length);

	return { dataVec, fdsVec };
}

template<>
FrameBuffer::Plane
IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,
						   std::vector<uint8_t>::const_iterator dataEnd,
						   std::vector<SharedFD>::const_iterator fdsBegin,
						   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,
						   [[maybe_unused]] ControlSerializer *cs)
{
	FrameBuffer::Plane ret;

	ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,
							  fdsBegin, fdsBegin + 1);
	ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);
	ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);

	return ret;
}

template<>
FrameBuffer::Plane
IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,
						   const std::vector<SharedFD> &fds,
						   ControlSerializer *cs)
{
	return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);
}

#endif /* __DOXYGEN__ */

} /* namespace libcamera */
