blob: 0a259305afa233157729d2d909f4e5d5f752dd97 [file] [log] [blame]
/* 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 <unistd.h>
#include <libcamera/base/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.
*
* \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; \
dataVec.reserve(sizeof(type)); \
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 */