From patchwork Sun Mar 1 17:54:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 2952 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5E7DD60429 for ; Sun, 1 Mar 2020 18:55:23 +0100 (CET) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3694754A; Sun, 1 Mar 2020 18:55:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1583085323; bh=17zeQeqNVmch8YRNWzh/awTzE4u/yK4UxWXZ4KOGVQI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CGdC4QqmOYEza4y1MoY7+DF6Pxb8xFMvC4+hMO3+xNuvheakcD3nTdcIZ1K0lJSsS eL3GkZ2/hhr1g+GejrfUlpMtUPV89GSRYNMHamIBe3px6i3QESL85PGj2v8pE2DAfI GAZfr36kWQdWi9gKDjAhqJ1GQ5SVQHDsaOJ3D5Yw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 1 Mar 2020 19:54:54 +0200 Message-Id: <20200301175454.10921-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200229164254.23604-17-laurent.pinchart@ideasonboard.com> References: <20200229164254.23604-17-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1.2 16/31] libcamera: controls: Support array controls in ControlValue 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: , X-List-Received-Date: Sun, 01 Mar 2020 17:55:23 -0000 From: Jacopo Mondi Add array controls support to the ControlValue class. The polymorphic class can now store more than a single element and supports access and creation through the use of Span<>. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- Changes since v1: - Use T::value_type instead of T::element_type to benefit from std::remove_cv - Fix ControlTypeNone test in ControlValue::toString() - Separate array elements with ", " in ControlValue::toString() --- include/libcamera/controls.h | 81 ++++++++++++--- src/libcamera/controls.cpp | 185 +++++++++++++++++++++++++++++------ 2 files changed, 225 insertions(+), 41 deletions(-) diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h index 4538be06af93..1e24ae30ab36 100644 --- a/include/libcamera/controls.h +++ b/include/libcamera/controls.h @@ -9,6 +9,7 @@ #define __LIBCAMERA_CONTROLS_H__ #include +#include #include #include @@ -51,6 +52,10 @@ struct control_type { static constexpr ControlType value = ControlTypeInteger64; }; +template +struct control_type> : public control_type> { +}; + } /* namespace details */ class ControlValue @@ -58,15 +63,35 @@ class ControlValue public: ControlValue(); +#ifndef __DOXYGEN__ + template::value, std::nullptr_t> = nullptr> + ControlValue(const T &value) + : type_(ControlTypeNone), numElements_(0) + { + set(details::control_type>::value, false, + &value, 1, sizeof(T)); + } + + template::value, std::nullptr_t> = nullptr> +#else template - ControlValue(T value) - : type_(details::control_type>::value) +#endif + ControlValue(const T &value) + : type_(ControlTypeNone), numElements_(0) { - *reinterpret_cast(&bool_) = value; + set(details::control_type>::value, true, + value.data(), value.size(), sizeof(typename T::value_type)); } + ~ControlValue(); + + ControlValue(const ControlValue &other); + ControlValue &operator=(const ControlValue &other); + ControlType type() const { return type_; } bool isNone() const { return type_ == ControlTypeNone; } + bool isArray() const { return isArray_; } + std::size_t numElements() const { return numElements_; } Span data() const; std::string toString() const; @@ -77,31 +102,61 @@ public: return !(*this == other); } +#ifndef __DOXYGEN__ + template::value, std::nullptr_t> = nullptr> + T get() const + { + assert(type_ == details::control_type>::value); + assert(!isArray_); + + return *reinterpret_cast(data().data()); + } + + template::value, std::nullptr_t> = nullptr> +#else template +#endif T get() const { assert(type_ == details::control_type>::value); + assert(isArray_); + + using V = typename T::value_type; + const V *value = reinterpret_cast(data().data()); + return { value, numElements_ }; + } - return *reinterpret_cast(&bool_); +#ifndef __DOXYGEN__ + template::value, std::nullptr_t> = nullptr> + void set(const T &value) + { + set(details::control_type>::value, false, + reinterpret_cast(&value), 1, sizeof(T)); } + template::value, std::nullptr_t> = nullptr> +#else template +#endif void set(const T &value) { - type_ = details::control_type>::value; - *reinterpret_cast(&bool_) = value; + set(details::control_type>::value, true, + value.data(), value.size(), sizeof(typename T::value_type)); } private: - ControlType type_; - - union { - bool bool_; - int32_t integer32_; - int64_t integer64_; - }; + ControlType type_ : 8; + bool isArray_ : 1; + std::size_t numElements_ : 16; + uint64_t storage_; + + void release(); + void set(ControlType type, bool isArray, const void *data, + std::size_t numElements, std::size_t elementSize); }; +static_assert(sizeof(ControlValue) == 16, "Invalid size of ControlValue class"); + class ControlId { public: diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp index b2331ab7540d..a5a385aa1b0a 100644 --- a/src/libcamera/controls.cpp +++ b/src/libcamera/controls.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "control_validator.h" #include "log.h" @@ -50,7 +51,7 @@ LOG_DEFINE_CATEGORY(Controls) namespace { static constexpr size_t ControlValueSize[] = { - [ControlTypeNone] = 1, + [ControlTypeNone] = 0, [ControlTypeBool] = sizeof(bool), [ControlTypeInteger32] = sizeof(int32_t), [ControlTypeInteger64] = sizeof(int64_t), @@ -80,15 +81,63 @@ static constexpr size_t ControlValueSize[] = { * \brief Construct an empty ControlValue. */ ControlValue::ControlValue() - : type_(ControlTypeNone) + : type_(ControlTypeNone), isArray_(false), numElements_(0) { } /** - * \fn template T ControlValue::ControlValue(T value) + * \fn template T ControlValue::ControlValue(const T &value) * \brief Construct a ControlValue of type T * \param[in] value Initial value + * + * This function constructs a new instance of ControlValue and stores the \a + * value inside it. If the type \a T is equivalent to Span, the instance + * stores an array of values of type \a R. Otherwise the instance stores a + * single value of type \a T. The numElements() and type() are updated to + * reflect the stored value. + */ + +void ControlValue::release() +{ + std::size_t size = numElements_ * ControlValueSize[type_]; + + if (size > sizeof storage_) { + delete[] *reinterpret_cast(&storage_); + storage_ = 0; + } +} + +ControlValue::~ControlValue() +{ + release(); +} + +/** + * \brief Contruct a ControlValue with the content of \a other + * \param[in] other The ControlValue to copy content from + */ +ControlValue::ControlValue(const ControlValue &other) + : type_(ControlTypeNone), numElements_(0) +{ + *this = other; +} + +/** + * \brief Replace the content of the ControlValue with the one of \a other + * \param[in] other The ControlValue to copy content from + * + * Deep-copy the content of \a other into the ControlValue by reserving memory + * and copy data there in case \a other transports arrays of values in one of + * its pointer data members. + * + * \return The ControlValue with its content replaced with the one of \a other */ +ControlValue &ControlValue::operator=(const ControlValue &other) +{ + set(other.type_, other.isArray_, other.data().data(), + other.numElements_, ControlValueSize[other.type_]); + return *this; +} /** * \fn ControlValue::type() @@ -102,16 +151,33 @@ ControlValue::ControlValue() * \return True if the value type is ControlTypeNone, false otherwise */ +/** + * \fn ControlValue::isArray() + * \brief Determine if the value stores an array + * \return True if the value stores an array, false otherwise + */ + +/** + * \fn ControlValue::numElements() + * \brief Retrieve the number of elements stored in the ControlValue + * + * For instances storing an array, this function returns the number of elements + * in the array. Otherwise, it returns 1. + * + * \return The number of elements stored in the ControlValue + */ + /** * \brief Retrieve the raw of a control value * \return The raw data of the control value as a span of uint8_t */ Span ControlValue::data() const { - return { - reinterpret_cast(&bool_), - ControlValueSize[type_] - }; + std::size_t size = numElements_ * ControlValueSize[type_]; + const uint8_t *data = size > sizeof storage_ + ? *reinterpret_cast(&storage_) + : reinterpret_cast(&storage_); + return { data, size }; } /** @@ -120,18 +186,43 @@ Span ControlValue::data() const */ std::string ControlValue::toString() const { - switch (type_) { - case ControlTypeNone: - return ""; - case ControlTypeBool: - return bool_ ? "True" : "False"; - case ControlTypeInteger32: - return std::to_string(integer32_); - case ControlTypeInteger64: - return std::to_string(integer64_); + if (type_ == ControlTypeNone) + return ""; + + const uint8_t *data = ControlValue::data().data(); + 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 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 ControlTypeNone: + break; + } + + if (i + 1 != numElements_) + str += ", "; + + data += ControlValueSize[type_]; } - return ""; + if (isArray_) + str += " ]"; + + return str; } /** @@ -143,16 +234,13 @@ bool ControlValue::operator==(const ControlValue &other) const if (type_ != other.type_) return false; - switch (type_) { - case ControlTypeBool: - return bool_ == other.bool_; - case ControlTypeInteger32: - return integer32_ == other.integer32_; - case ControlTypeInteger64: - return integer64_ == other.integer64_; - default: + if (numElements_ != other.numElements()) return false; - } + + if (isArray_ != other.isArray_) + return false; + + return memcmp(data().data(), other.data().data(), data().size()) == 0; } /** @@ -165,8 +253,16 @@ bool ControlValue::operator==(const ControlValue &other) const * \fn template T ControlValue::get() const * \brief Get the control value * - * The control value type shall match the type T, otherwise the behaviour is - * undefined. + * This function returns the contained value as an instance of \a T. If the + * ControlValue instance stores a single value, the type \a T shall match the + * stored value type(). If the instance stores an array of values, the type + * \a T should be equal to Span, and the type \a R shall match the + * stored value type(). The behaviour is undefined otherwise. + * + * Note that a ControlValue instance that stores a non-array value is not + * equivalent to an instance that stores an array value containing a single + * element. The latter shall be accessed through a Span type, while + * the former shall be accessed through a type \a T corresponding to type(). * * \return The control value */ @@ -175,8 +271,41 @@ bool ControlValue::operator==(const ControlValue &other) const * \fn template void ControlValue::set(const T &value) * \brief Set the control value to \a value * \param[in] value The control value + * + * This function stores the \a value in the instance. If the type \a T is + * equivalent to Span, the instance stores an array of values of type \a R. + * Otherwise the instance stores a single value of type \a T. The numElements() + * and type() are updated to reflect the stored value. + * + * The entire content of \a value is copied to the instance, no reference to \a + * value or to the data it references is retained. This may be an expensive + * operation for Span<> values that refer to large arrays. */ +void ControlValue::set(ControlType type, bool isArray, const void *data, + std::size_t numElements, std::size_t elementSize) +{ + ASSERT(elementSize == ControlValueSize[type]); + + release(); + + type_ = type; + numElements_ = numElements; + isArray_ = isArray; + + std::size_t size = elementSize * numElements; + void *storage; + + if (size > sizeof storage_) { + storage = reinterpret_cast(new char[size]); + *reinterpret_cast(&storage_) = storage; + } else { + storage = reinterpret_cast(&storage_); + } + + memcpy(storage, data, size); +} + /** * \class ControlId * \brief Control static metadata