From patchwork Wed Aug 6 12:30:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 24061 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 D5492BDCC1 for ; Wed, 6 Aug 2025 12:31:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 605926921E; Wed, 6 Aug 2025 14:31:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="i9YcC17t"; 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 24A2E6921A for ; Wed, 6 Aug 2025 14:31:00 +0200 (CEST) Received: from [192.168.0.172] (mob-5-90-59-79.net.vodafone.it [5.90.59.79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8EDF81026; Wed, 6 Aug 2025 14:30:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1754483411; bh=Fpmad2CnP74YTyGc7B7ELKlkRy63OLmGB9iqWVIQGdw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=i9YcC17tz93Y1uD4w/R14ewNrwBD+CE7dUzKVGW1JGJWWujhwsFfTDnDQqoqNzRsw 40EetcRnuQERDeWb8R901d5s5JG/wccYtbZS4n18pZhf5uXhmXFnIT854t2Fsd5KCK P3LSRoajQKR6adtx/P3ik07bBGCc+dnr9b7WbaMA= From: Jacopo Mondi Date: Wed, 06 Aug 2025 14:30:45 +0200 Subject: [PATCH 1/3] libcamera: controls: Add `ControlValueView` MIME-Version: 1.0 Message-Id: <20250806-control_storage-v1-1-2ec8424f6f7d@ideasonboard.com> References: <20250806-control_storage-v1-0-2ec8424f6f7d@ideasonboard.com> In-Reply-To: <20250806-control_storage-v1-0-2ec8424f6f7d@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi , =?utf-8?b?QmFybmFiw6Fz?= =?utf-8?q?_P=C5=91cze?= X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=10928; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=m4620W2t6rZZWzeNXOe2nO5uvbS5USXMnLf4h6+uBIY=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBok0sCbQH+cYnBKdr8PTlifAnEzn86Lsdm+9z10 ku9WkjPIHKJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaJNLAgAKCRByNAaPFqFW PLAGEAC0DjK35O77RyTJ8IjY/bb98BhYo+f/n5QdyGIIQSkizrKjvl4b5f1t9GCkfw33KsYln9h rCt+q1jXQEzg8582gNWu05CA84qzy/HKzW/lYAkDx33YSIxptkO+VSG48vPLnBt+Qs7+fMvvUuV bumyt+tGMydGbpIK3w7TSETgNShDuZjMjoE1+WYzlJXPmRxQ0sQ5g92DUL8Qy9K06KD5Qs/W1ew /mSZ6JGlL1vP1J3SxQPzXoaG+O05PQKz+72YVztUcPcrlEuk9KxWJpU6E1NeaZFPFf8ApMRCRGa FKFUx3nmQNb7pXk/GSNiM+WDUSHOyDALea2kjtcR/tVswwfnAF3A+8f7U3+TAhvyRW9yX4ujv4L /BwO0jFNbmJaEIzcJ7vKFie5n61PURI7INemr872bIa7pyPBgiWwpHexaoU69ynJOxYTxe7ov2D XgVpyR58+Nl21Gkxu9nFL4AkB9H7kg2gmgVm94Ga93Vi8hJm8KtVWSdwUv9U8D3e9PXQOGQvOLv 5CrHuPlk/daV9rHn5HR7oYGn2rGOtW0JdDz1EdKnuBOWiy1OPQC6psBamIXiRAJB43zhm9KVlfz 5KV8hUgTgYRkq3e8wi1iVKgHvwGGwinyH6V+eHYiNcbPzaoEOSIABfz8bvIHG3RFXqKY7Er8TUc ragKX8FfIkVO2UQ== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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" From: Barnabás Pőcze Add `ControlValueView`, which is a non-owning read-only handle to a control value with a very similar interface. Signed-off-by: Barnabás Pőcze --- include/libcamera/controls.h | 68 +++++++++++ src/libcamera/controls.cpp | 273 ++++++++++++++++++++++++++++++------------- 2 files changed, 263 insertions(+), 78 deletions(-) diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h index 2ae4ec3d4438c57eb0ad8f820185dc63d4879fc8..3b691804fce18909fccdb420c212413a97d33be7 100644 --- a/include/libcamera/controls.h +++ b/include/libcamera/controls.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -25,6 +26,7 @@ namespace libcamera { class ControlValidator; +class ControlValueView; enum ControlType { ControlTypeNone, @@ -160,6 +162,8 @@ public: value.data(), value.size(), sizeof(typename T::value_type)); } + explicit ControlValue(const ControlValueView &cvv); + ~ControlValue(); ControlValue(const ControlValue &other); @@ -247,6 +251,70 @@ private: std::size_t numElements, std::size_t elementSize); }; +class ControlValueView +{ +public: + constexpr ControlValueView() noexcept + : type_(ControlTypeNone) + { + } + + ControlValueView(const ControlValue &cv) noexcept + : ControlValueView(cv.type(), cv.isArray(), cv.numElements(), + reinterpret_cast(cv.data().data())) + { + } + +#ifndef __DOXYGEN__ + // TODO: should have restricted access? + ControlValueView(ControlType type, bool isArray, std::size_t numElements, + const std::byte *data) noexcept + : type_(type), isArray_(isArray), numElements_(numElements), + data_(data) + { + assert(isArray || numElements == 1); + } +#endif + + [[nodiscard]] explicit operator bool() const { return type_ != ControlTypeNone; } + [[nodiscard]] ControlType type() const { return type_; } + [[nodiscard]] bool isNone() const { return type_ == ControlTypeNone; } + [[nodiscard]] bool isArray() const { return isArray_; } + [[nodiscard]] std::size_t numElements() const { return numElements_; } + [[nodiscard]] Span data() const; + + [[nodiscard]] bool operator==(const ControlValueView &other) const; + + [[nodiscard]] bool operator!=(const ControlValueView &other) const + { + return !(*this == other); + } + + template + [[nodiscard]] auto get() const + { + using TypeInfo = details::control_type>; + + assert(type_ == TypeInfo::value); + assert(isArray_ == (TypeInfo::size > 0)); + + if constexpr (TypeInfo::size > 0) { + return T(reinterpret_cast(data().data()), numElements_); + } else { + assert(numElements_ == 1); + return *reinterpret_cast(data().data()); + } + } + +private: + ControlType type_ : 8; + bool isArray_ = false; + uint32_t numElements_ = 0; + const std::byte *data_ = nullptr; +}; + +std::ostream &operator<<(std::ostream &s, const ControlValueView &v); + class ControlId { public: diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp index 98fa7583db6abfe3967ad7724fab2577240ff8c1..a238141a5c34856242bed7f76e938e706d49f1ff 100644 --- a/src/libcamera/controls.cpp +++ b/src/libcamera/controls.cpp @@ -106,6 +106,16 @@ ControlValue::ControlValue() { } +/** + * \brief Construct a ControlValue from a ControlValueView + */ +ControlValue::ControlValue(const ControlValueView &cvv) + : ControlValue() +{ + set(cvv.type(), cvv.isArray(), cvv.data().data(), + cvv.numElements(), ControlValueSize[cvv.type()]); +} + /** * \fn template T ControlValue::ControlValue(const T &value) * \brief Construct a ControlValue of type T @@ -213,84 +223,7 @@ Span ControlValue::data() */ std::string ControlValue::toString() const { - if (type_ == ControlTypeNone) - return ""; - - const uint8_t *data = ControlValue::data().data(); - - if (type_ == ControlTypeString) - return std::string(reinterpret_cast(data), - numElements_); - - std::string str(isArray_ ? "[ " : ""); - - for (unsigned int i = 0; i < numElements_; ++i) { - switch (type_) { - case ControlTypeBool: { - const bool *value = reinterpret_cast(data); - str += *value ? "true" : "false"; - break; - } - case ControlTypeByte: { - const uint8_t *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeUnsigned16: { - const uint16_t *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeUnsigned32: { - const uint32_t *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeInteger32: { - const int32_t *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeInteger64: { - const int64_t *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeFloat: { - const float *value = reinterpret_cast(data); - str += std::to_string(*value); - break; - } - case ControlTypeRectangle: { - const Rectangle *value = reinterpret_cast(data); - str += value->toString(); - break; - } - case ControlTypeSize: { - const Size *value = reinterpret_cast(data); - str += value->toString(); - break; - } - case ControlTypePoint: { - const Point *value = reinterpret_cast(data); - str += value->toString(); - break; - } - case ControlTypeNone: - case ControlTypeString: - break; - } - - if (i + 1 != numElements_) - str += ", "; - - data += ControlValueSize[type_]; - } - - if (isArray_) - str += " ]"; - - return str; + return static_cast(std::ostringstream{} << ControlValueView(*this)).str(); } /** @@ -395,6 +328,190 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen storage_ = reinterpret_cast(new uint8_t[newSize]); } +/** + * \class ControlValueView + * \brief A non-owning view-like type to the value of a control + */ + +/** + * \fn ControlValueView::ControlValueView() + * \brief Construct an empty view + * \sa ControlValue::ControlValue() + */ + +/** + * \fn ControlValueView::ControlValueView(const ControlValue &v) + * \brief Construct a view referring to \a v + * + * The constructed view will refer to the value stored by \a v, and + * thus \a v must not be modified or destroyed before the view. + * + * \sa ControlValue::ControlValue() + */ + +/** + * \fn ControlValueView::operator bool() const + * \brief Determine if the referred value is valid + * \sa ControlValueView::isNone() + */ + +/** + * \fn ControlType ControlValueView::type() const + * \copydoc ControlValue::type() + * \sa ControlValue::type() + */ + +/** + * \fn ControlValueView::isNone() const + * \copydoc ControlValue::isNone() + * \sa ControlValue::isNone() + */ + +/** + * \fn ControlValueView::isArray() const + * \copydoc ControlValue::isArray() + * \sa ControlValue::isArray() + */ + +/** + * \fn ControlValueView::numElements() const + * \copydoc ControlValue::numElements() + * \sa ControlValue::numElements() + */ + +/** + * \copydoc ControlValue::data() + * \sa ControlValue::data() + */ +Span ControlValueView::data() const +{ + return { data_, numElements_ * ControlValueSize[type_] }; +} + +/** + * \copydoc ControlValue::operator==() + * \sa ControlValue::operator==() + * \sa ControlValueView::operator!=() + */ +bool ControlValueView::operator==(const ControlValueView &other) const +{ + if (type_ != other.type_) + return false; + + if (numElements_ != other.numElements_) + return false; + + if (isArray_ != other.isArray_) + return false; + + const auto d = data(); + + return memcmp(d.data(), other.data_, d.size_bytes()) == 0; +} + +/** + * \fn ControlValueView::operator!=() const + * \copydoc ControlValue::operator!=() + * \sa ControlValue::operator!=() + * \sa ControlValueView::operator==() + */ + +/** + * \fn template T ControlValueView::get() const + * \copydoc ControlValue::get() + * \sa ControlValue::get() + */ + +/** + * \brief Insert a text representation of a value into an output stream + * \sa ControlValue::toString() + */ +std::ostream &operator<<(std::ostream &s, const ControlValueView &v) +{ + const auto type = v.type(); + if (type == ControlTypeNone) + return s << "None"; + + const auto *data = v.data().data(); + const auto numElements = v.numElements(); + + if (type == ControlTypeString) + return s << std::string_view(reinterpret_cast(data), + numElements); + + const bool isArray = v.isArray(); + if (isArray) + s << "[ "; + + for (std::size_t i = 0; i < numElements; ++i) { + if (i > 0) + s << ", "; + + switch (type) { + case ControlTypeBool: { + const bool *value = reinterpret_cast(data); + s << (*value ? "true" : "false"); + break; + } + case ControlTypeByte: { + const auto *value = reinterpret_cast(data); + s << static_cast(*value); + break; + } + case ControlTypeUnsigned16: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeUnsigned32: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeInteger32: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeInteger64: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeFloat: { + const auto *value = reinterpret_cast(data); + s << std::fixed << *value; + break; + } + case ControlTypeRectangle: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeSize: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypePoint: { + const auto *value = reinterpret_cast(data); + s << *value; + break; + } + case ControlTypeNone: + case ControlTypeString: + break; + } + + data += ControlValueSize[type]; + } + + if (isArray) + s << " ]"; + + return s; +} + /** * \class ControlId * \brief Control static metadata