From patchwork Fri Nov 6 10:36:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 10356 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CB0CDBDB89 for ; Fri, 6 Nov 2020 10:37:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9566362D34; Fri, 6 Nov 2020 11:37:41 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NbPp9F/N"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 71D3C62D2B for ; Fri, 6 Nov 2020 11:37:40 +0100 (CET) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EBB2FB16; Fri, 6 Nov 2020 11:37:38 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1604659060; bh=7vhmcqhdbfghme6zzD10/G4Jl8ckE03XJg4uSUJfNEg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NbPp9F/NGzjK5KYMc4UTxingTJB7icJWr74pFNXjqFE8sDvJIeaZE9AKFK5P105Q+ mfKvXFNNoMHbaPr/0QjKdd6iJKNLU3fTT0+bMDdeHLW7kwcQvS5Hqw1iWdzBqlgX9a +3IOgO+MwV2IX1mZo+MACHVNqZ4LOwQ4OrPrw+04= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 6 Nov 2020 19:36:38 +0900 Message-Id: <20201106103707.49660-9-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201106103707.49660-1-paul.elder@ideasonboard.com> References: <20201106103707.49660-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 08/37] libcamera: Add IPADataSerializer X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Jacopo Mondi --- 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:: 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +void appendPOD(std::vector &vec, T val) +{ + size_t byteWidth = sizeof(val); + std::vector v(byteWidth); + memcpy(&v[0], &val, byteWidth); + + vec.insert(vec.end(), v.begin(), v.end()); +} + +template +T readPOD(std::vector &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 +T readPOD(std::vector::iterator it, size_t pos, + std::vector::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 v(it + pos, it + pos + byteWidth); + memcpy(&ret, v.data(), byteWidth); + return ret; +} + +} /* namespace */ + +template +class IPADataSerializer +{ +#ifdef __DOXYGEN__ +public: + static std::tuple, std::vector> + serialize(T data, ControlSerializer *cs); + + static T deserialize(std::vector &data, + ControlSerializer *cs); + static T deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + ControlSerializer *cs); + + static T deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs); + static T deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + std::vector::iterator fdsEnd, + ControlSerializer *cs); +#endif /* __DOXYGEN__ */ +}; + +#ifndef __DOXYGEN__ + +template +class IPADataSerializer> +{ +public: + static std::tuple, std::vector> + serialize(const std::vector &data, ControlSerializer *cs = nullptr) + { + std::vector dataVec; + std::vector fdsVec; + + /* Serialize the length. */ + uint32_t vecLen = data.size(); + appendPOD(dataVec, vecLen); + + /* Serialize the members. */ + for (auto const &it : data) { + std::vector dvec; + std::vector fvec; + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(it, cs); + + appendPOD(dataVec, dvec.size()); + appendPOD(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 deserialize(std::vector &data, ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), cs); + } + + static std::vector deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + ControlSerializer *cs = nullptr) + { + std::vector fds; + return deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs); + } + + static std::vector deserialize(std::vector &data, std::vector &fds, + ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs); + } + + static std::vector deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs = nullptr) + { + uint32_t vecLen = readPOD(dataBegin, 0, dataEnd); + std::vector ret(vecLen); + + std::vector::iterator dataIter = dataBegin + 4; + std::vector::iterator fdIter = fdsBegin; + for (uint32_t i = 0; i < vecLen; i++) { + uint32_t sizeofData = readPOD(dataIter, 0, dataEnd); + uint32_t sizeofFds = readPOD(dataIter, 4, dataEnd); + + ret[i] = IPADataSerializer::deserialize(dataIter + 8, + dataIter + 8 + sizeofData, + fdIter, + fdIter + sizeofFds, + cs); + + dataIter += 8 + sizeofData; + fdIter += sizeofFds; + } + + return ret; + } +}; + +template +class IPADataSerializer> +{ +public: + static std::tuple, std::vector> + serialize(const std::map &data, ControlSerializer *cs = nullptr) + { + std::vector dataVec; + std::vector fdsVec; + + /* Serialize the length. */ + uint32_t mapLen = data.size(); + appendPOD(dataVec, mapLen); + + /* Serialize the members. */ + for (auto const &it : data) { + std::vector dvec; + std::vector fvec; + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(it.first, cs); + + appendPOD(dataVec, dvec.size()); + appendPOD(dataVec, fvec.size()); + + dataVec.insert(dataVec.end(), dvec.begin(), dvec.end()); + fdsVec.insert(fdsVec.end(), fvec.begin(), fvec.end()); + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(it.second, cs); + + appendPOD(dataVec, dvec.size()); + appendPOD(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 deserialize(std::vector &data, ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), cs); + } + + static std::map deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + ControlSerializer *cs = nullptr) + { + std::vector fds; + return deserialize(dataBegin, dataEnd, fds.begin(), fds.end(), cs); + } + + static std::map deserialize(std::vector &data, std::vector &fds, + ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs); + } + + static std::map deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs = nullptr) + { + std::map ret; + + uint32_t mapLen = readPOD(dataBegin, 0, dataEnd); + + std::vector::iterator dataIter = dataBegin + 4; + std::vector::iterator fdIter = fdsBegin; + for (uint32_t i = 0; i < mapLen; i++) { + uint32_t sizeofData = readPOD(dataIter, 0, dataEnd); + uint32_t sizeofFds = readPOD(dataIter, 4, dataEnd); + + K key = IPADataSerializer::deserialize(dataIter + 8, + dataIter + 8 + sizeofData, + fdIter, + fdIter + sizeofFds, + cs); + + dataIter += 8 + sizeofData; + fdIter += sizeofFds; + sizeofData = readPOD(dataIter, 0, dataEnd); + sizeofFds = readPOD(dataIter, 4, dataEnd); + + const V value = IPADataSerializer::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 \ +{ \ +public: \ + static std::tuple, std::vector> \ + serialize(const type data, \ + [[maybe_unused]] ControlSerializer *cs = nullptr) \ + { \ + std::vector dataVec; \ + dataVec.reserve(sizeof(type)); \ + appendPOD(dataVec, data); \ + \ + return {dataVec, {}}; \ + } \ + \ + static type deserialize(std::vector &data, \ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return deserialize(data.begin(), data.end()); \ + } \ + \ + static type deserialize(std::vector::iterator dataBegin,\ + [[maybe_unused]] std::vector::iterator dataEnd,\ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return readPOD(dataBegin, 0, dataEnd); \ + } \ + \ + static type deserialize(std::vector &data, \ + [[maybe_unused]] std::vector &fds,\ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return deserialize(data.begin(), data.end()); \ + } \ + \ + static type deserialize(std::vector::iterator dataBegin,\ + std::vector::iterator dataEnd,\ + [[maybe_unused]] std::vector::iterator fdsBegin,\ + [[maybe_unused]] std::vector::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 +{ +public: + static std::tuple, std::vector> + serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return {{data.begin(), data.end()}, {}}; + } + + static std::string deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return {data.begin(), data.end()}; + } + + static std::string deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return {dataBegin, dataEnd}; + } + + static std::string deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return {data.begin(), data.end()}; + } + + static std::string deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return {dataBegin, dataEnd}; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector dataVec = { data.isValid() }; + std::vector fdVec; + if (data.isValid()) + fdVec.push_back(data.fd()); + + return {dataVec, fdVec}; + } + + static FileDescriptor deserialize(std::vector &data, std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), fds.begin(), fds.end()); + } + + static FileDescriptor deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + std::vector::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 +{ +public: + static std::tuple, std::vector> + serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::serialize(data.configurationFile); + } + + static IPASettings deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return { IPADataSerializer::deserialize(data.begin(), data.end()) }; + } + + static IPASettings deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return { IPADataSerializer::deserialize(dataBegin, dataEnd) }; + } + + static IPASettings deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return { IPADataSerializer::deserialize(data.begin(), data.end()) }; + } + + static IPASettings deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return { IPADataSerializer::deserialize(dataBegin, dataEnd) }; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector dataVec; + + uint32_t strLen = data.model.size(); + appendPOD(dataVec, strLen); + + dataVec.insert(dataVec.end(), data.model.begin(), data.model.end()); + + appendPOD(dataVec, data.bitsPerPixel); + + appendPOD(dataVec, data.activeAreaSize.width); + appendPOD(dataVec, data.activeAreaSize.height); + + appendPOD(dataVec, static_cast(data.analogCrop.x)); + appendPOD(dataVec, static_cast(data.analogCrop.y)); + appendPOD(dataVec, data.analogCrop.width); + appendPOD(dataVec, data.analogCrop.height); + + appendPOD(dataVec, data.outputSize.width); + appendPOD(dataVec, data.outputSize.height); + + appendPOD(dataVec, data.pixelRate); + + appendPOD(dataVec, data.lineLength); + + return {dataVec, {}}; + } + + static CameraSensorInfo deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end()); + } + + static CameraSensorInfo deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + CameraSensorInfo ret; + + uint32_t strLen = readPOD(dataBegin, 0, dataEnd); + std::string str(dataBegin + 4, dataBegin + 4 + strLen); + ret.model = str; + + std::vector::iterator it = dataBegin + 4 + strLen; + + ret.bitsPerPixel = readPOD(it, 0, dataEnd); + + ret.activeAreaSize.width = readPOD(it, 4, dataEnd); + ret.activeAreaSize.height = readPOD(it, 8, dataEnd); + + ret.analogCrop.x = static_cast(readPOD(it, 12, dataEnd)); + ret.analogCrop.y = static_cast(readPOD(it, 16, dataEnd)); + ret.analogCrop.width = readPOD(it, 20, dataEnd); + ret.analogCrop.height = readPOD(it, 24, dataEnd); + + ret.outputSize.width = readPOD(it, 28, dataEnd); + ret.outputSize.height = readPOD(it, 32, dataEnd); + + ret.pixelRate = readPOD(it, 36, dataEnd); + + ret.lineLength = readPOD(it, 44, dataEnd); + + return ret; + } + + static CameraSensorInfo deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end()); + } + + static CameraSensorInfo deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(dataBegin, dataEnd); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector dataVec; + + appendPOD(dataVec, data.pixelFormat); + + appendPOD(dataVec, data.size.width); + appendPOD(dataVec, data.size.height); + + return {dataVec, {}}; + } + + static IPAStream deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end()); + } + + static IPAStream deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + IPAStream ret; + + ret.pixelFormat = readPOD(dataBegin, 0, dataEnd); + + ret.size.width = readPOD(dataBegin, 4, dataEnd); + ret.size.height = readPOD(dataBegin, 8, dataEnd); + + return ret; + } + + static IPAStream deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end()); + } + + static IPAStream deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return deserialize(dataBegin, dataEnd); + } +}; + +template<> +class IPADataSerializer +{ +public: + /* map arg will be generated, since it's per-pipeline anyway. */ + static std::tuple, std::vector> + 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 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 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 dataVec; + dataVec.reserve(8 + infoData.size() + listData.size()); + appendPOD(dataVec, infoData.size()); + appendPOD(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 &data, ControlSerializer *cs) + { + return deserialize(data.begin(), data.end(), cs); + } + + static const ControlList deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlList"; + + uint32_t infoDataSize = readPOD(dataBegin, 0, dataEnd); + uint32_t listDataSize = readPOD(dataBegin, 4, dataEnd); + + std::vector::iterator it = dataBegin + 8; + + std::vector infoData(it, it + infoDataSize); + std::vector listData(it + infoDataSize, it + infoDataSize + listDataSize); + + ByteStreamBuffer buffer(const_cast(infoData.data()), infoData.size()); + ControlInfoMap map = cs->deserialize(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(listData.data()), listData.size()); + ControlList list = cs->deserialize(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 &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + return deserialize(data.begin(), data.end(), cs); + } + + static const ControlList deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs) + { + return deserialize(dataBegin, dataEnd, cs); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const ControlList &data, const ControlInfoMap &map, + ControlSerializer *cs) + { + const ControlList &list_const = const_cast(data); + + std::vector dataVec; + std::tie(dataVec, std::ignore) = + IPADataSerializer::serialize(list_const, map, cs); + + return {dataVec, {}}; + } + + static ControlList deserialize(std::vector &data, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(data, cs); + + return ret; + } + + static ControlList deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(dataBegin, dataEnd, cs); + + return ret; + } + + static ControlList deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(data, fds, cs); + + return ret; + } + + static ControlList deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(dataBegin, dataEnd, + fdsBegin, fdsEnd, cs); + + return ret; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + 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 infoData(size); + ByteStreamBuffer buffer(infoData.data(), infoData.size()); + int ret = cs->serialize(map, buffer); + + if (ret < 0 || buffer.overflow()) + return {{}, {}}; + + std::vector dataVec; + appendPOD(dataVec, infoData.size()); + dataVec.insert(dataVec.end(), infoData.begin(), infoData.end()); + + return {dataVec, {}}; + } + + static const ControlInfoMap deserialize(std::vector &data, + ControlSerializer *cs) + { + return deserialize(data.begin(), data.end(), cs); + } + + static const ControlInfoMap deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlInfoMap"; + + uint32_t infoDataSize = readPOD(dataBegin, 0, dataEnd); + + std::vector::iterator it = dataBegin + 4; + + std::vector infoData(it, it + infoDataSize); + + ByteStreamBuffer buffer(const_cast(infoData.data()), infoData.size()); + const ControlInfoMap map = cs->deserialize(buffer); + + return map; + } + + static const ControlInfoMap deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + return deserialize(data.begin(), data.end(), cs); + } + + static const ControlInfoMap deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs) + { + return deserialize(dataBegin, dataEnd, cs); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs) + { + const ControlInfoMap &map_const = const_cast(map); + + std::vector dataVec; + std::tie(dataVec, std::ignore) = + IPADataSerializer::serialize(map_const, cs); + + return {dataVec, {}}; + } + + static ControlInfoMap deserialize(std::vector &data, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(data, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(dataBegin, dataEnd, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(data, fds, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + [[maybe_unused]] std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(dataBegin, dataEnd, + fdsBegin, fdsEnd, cs); + + return ret; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector dataVec; + std::vector fdsVec; + + std::vector fdBuf; + std::vector fdFds; + std::tie(fdBuf, fdFds) = + IPADataSerializer::serialize(data.fd); + dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end()); + fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end()); + + appendPOD(dataVec, data.length); + + return {dataVec, fdsVec}; + } + + static FrameBuffer::Plane deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs); + } + + static FrameBuffer::Plane deserialize(std::vector::iterator dataBegin, + [[maybe_unused]] std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + [[maybe_unused]] std::vector::iterator fdsEnd, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + FrameBuffer::Plane ret; + + ret.fd = IPADataSerializer::deserialize(dataBegin, dataBegin + 1, + fdsBegin, fdsBegin + 1); + ret.length = readPOD(dataBegin, 1, dataEnd); + + return ret; + } +}; + + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const IPABuffer &data, ControlSerializer *cs = nullptr) + { + std::vector dataVec; + + appendPOD(dataVec, data.id); + + std::vector planesDataVec; + std::vector planesFdsVec; + std::tie(planesDataVec, planesFdsVec) = + IPADataSerializer>::serialize(data.planes, cs); + + dataVec.insert(dataVec.end(), planesDataVec.begin(), planesDataVec.end()); + + return {dataVec, planesFdsVec}; + } + + static IPABuffer deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs = nullptr) + { + return deserialize(data.begin(), data.end(), fds.begin(), fds.end(), cs); + } + + static IPABuffer deserialize(std::vector::iterator dataBegin, + std::vector::iterator dataEnd, + std::vector::iterator fdsBegin, + std::vector::iterator fdsEnd, + ControlSerializer *cs = nullptr) + { + IPABuffer ret; + + ret.id = readPOD(dataBegin, 0, dataEnd); + + ret.planes = + IPADataSerializer>::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 void appendPOD(std::vector &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 T readPOD(std::vector &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 T readPOD(std::vector::iterator it, size_t pos, + * std::vector::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 IPADataSerializer::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 IPADataSerializer::deserialize( + * std::vector &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 IPADataSerializer::deserialize( + * std::vector::iterator dataBegin, + * std::vector::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 IPADataSerializer::deserialize( + * std::vector &data, + * std::vector &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 IPADataSerializer::deserialize( + * std::vector::iterator dataBegin, + * std::vector::iterator dataEnd, + * std::vector::iterator fdsBegin, + * std::vector::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',