| Message ID | 20251030165816.1095180-3-barnabas.pocze@ideasonboard.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Quoting Barnabás Pőcze (2025-10-30 16:57:56) > 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 <barnabas.pocze@ideasonboard.com> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > --- > changes in v3: > * reword some documentation > > changes in v2: > * rewrite `ControlValue::toString()` in terms of `operator<<(std::ostream&, const ControlValueView&) > to avoid duplication > --- > include/libcamera/controls.h | 68 +++++++++ > src/libcamera/controls.cpp | 268 +++++++++++++++++++++++++---------- > 2 files changed, 258 insertions(+), 78 deletions(-) > > diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h > index 1ee1971c4d..4af52b4f7d 100644 > --- a/include/libcamera/controls.h > +++ b/include/libcamera/controls.h > @@ -8,6 +8,7 @@ > #pragma once > > #include <assert.h> > +#include <cstddef> > #include <map> > #include <optional> > #include <stdint.h> > @@ -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<const std::byte *>(cv.data().data())) > + { > + } > + > +#ifndef __DOXYGEN__ > + // TODO: should have restricted access? Restricted access to what ? Do you mean doxygen should? or this constructor should be private? I think this is an internal detail of just how ControlValueView(const ControlValue &cv) is constructed right? So can it does look like it could be a private constructor...? As this is public API I think keeping it minimal makes sense. It's easier to add than subtract :D > + 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<const std::byte> data() const; > + > + [[nodiscard]] bool operator==(const ControlValueView &other) const; > + > + [[nodiscard]] bool operator!=(const ControlValueView &other) const > + { > + return !(*this == other); > + } > + > + template<typename T> > + [[nodiscard]] auto get() const > + { > + using TypeInfo = details::control_type<std::remove_cv_t<T>>; > + > + assert(type_ == TypeInfo::value); > + assert(isArray_ == (TypeInfo::size > 0)); > + > + if constexpr (TypeInfo::size > 0) { > + return T(reinterpret_cast<const typename T::value_type *>(data().data()), numElements_); > + } else { > + assert(numElements_ == 1); > + return *reinterpret_cast<const T *>(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 1e1b49e6bd..222030c434 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<typename T> T ControlValue::ControlValue(const T &value) > * \brief Construct a ControlValue of type T > @@ -213,84 +223,7 @@ Span<uint8_t> ControlValue::data() > */ > std::string ControlValue::toString() const > { > - if (type_ == ControlTypeNone) > - return "<ValueType Error>"; > - > - const uint8_t *data = ControlValue::data().data(); > - > - if (type_ == ControlTypeString) > - return std::string(reinterpret_cast<const char *>(data), > - numElements_); > - > - std::string str(isArray_ ? "[ " : ""); > - > - for (unsigned int i = 0; i < numElements_; ++i) { > - switch (type_) { > - case ControlTypeBool: { > - const bool *value = reinterpret_cast<const bool *>(data); > - str += *value ? "true" : "false"; > - break; > - } > - case ControlTypeByte: { > - const uint8_t *value = reinterpret_cast<const uint8_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeUnsigned16: { > - const uint16_t *value = reinterpret_cast<const uint16_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeUnsigned32: { > - const uint32_t *value = reinterpret_cast<const uint32_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeInteger32: { > - const int32_t *value = reinterpret_cast<const int32_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeInteger64: { > - const int64_t *value = reinterpret_cast<const int64_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeFloat: { > - const float *value = reinterpret_cast<const float *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeRectangle: { > - const Rectangle *value = reinterpret_cast<const Rectangle *>(data); > - str += value->toString(); > - break; > - } > - case ControlTypeSize: { > - const Size *value = reinterpret_cast<const Size *>(data); > - str += value->toString(); > - break; > - } > - case ControlTypePoint: { > - const Point *value = reinterpret_cast<const Point *>(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&&>(std::ostringstream{} << ControlValueView(*this)).str(); > } > > /** > @@ -395,6 +328,185 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen > storage_ = reinterpret_cast<void *>(new uint8_t[newSize]); > } > > +/** > + * \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 is destroyed. > + * > + * \sa ControlValue::ControlValue() > + */ > + > +/** > + * \fn ControlValueView::operator bool() const > + * \brief Determine if the referenced ControlValue 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<const std::byte> 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<typename T> 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<const char *>(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<const bool *>(data); > + s << (*value ? "true" : "false"); > + break; > + } > + case ControlTypeByte: { > + const auto *value = reinterpret_cast<const uint8_t *>(data); > + s << static_cast<unsigned int>(*value); > + break; > + } > + case ControlTypeUnsigned16: { > + const auto *value = reinterpret_cast<const uint16_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeUnsigned32: { > + const auto *value = reinterpret_cast<const uint32_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeInteger32: { > + const auto *value = reinterpret_cast<const int32_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeInteger64: { > + const auto *value = reinterpret_cast<const int64_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeFloat: { > + const auto *value = reinterpret_cast<const float *>(data); > + s << std::fixed << *value; > + break; > + } > + case ControlTypeRectangle: { > + const auto *value = reinterpret_cast<const Rectangle *>(data); > + s << *value; > + break; > + } > + case ControlTypeSize: { > + const auto *value = reinterpret_cast<const Size *>(data); > + s << *value; > + break; > + } > + case ControlTypePoint: { > + const auto *value = reinterpret_cast<const Point *>(data); > + s << *value; > + break; > + } > + case ControlTypeNone: > + case ControlTypeString: haha I was about to say what about strings - but it's handled early on so: Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + break; > + } > + > + data += ControlValueSize[type]; > + } > + > + if (isArray) > + s << " ]"; > + > + return s; > +} > + > /** > * \class ControlId > * \brief Control static metadata > -- > 2.51.1 >
Hi Barnabas On 30/10/2025 16:57, Barnabás Pőcze wrote: > 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 <barnabas.pocze@ideasonboard.com> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > --- I think the code here is fine, but I don't quite understand the need for the new class over just using ControlValue, what's it going to be used for? > changes in v3: > * reword some documentation > > changes in v2: > * rewrite `ControlValue::toString()` in terms of `operator<<(std::ostream&, const ControlValueView&) > to avoid duplication > --- > include/libcamera/controls.h | 68 +++++++++ > src/libcamera/controls.cpp | 268 +++++++++++++++++++++++++---------- > 2 files changed, 258 insertions(+), 78 deletions(-) > > diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h > index 1ee1971c4d..4af52b4f7d 100644 > --- a/include/libcamera/controls.h > +++ b/include/libcamera/controls.h > @@ -8,6 +8,7 @@ > #pragma once > > #include <assert.h> > +#include <cstddef> > #include <map> > #include <optional> > #include <stdint.h> > @@ -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<const std::byte *>(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<const std::byte> data() const; > + > + [[nodiscard]] bool operator==(const ControlValueView &other) const; > + > + [[nodiscard]] bool operator!=(const ControlValueView &other) const > + { > + return !(*this == other); > + } > + > + template<typename T> > + [[nodiscard]] auto get() const > + { > + using TypeInfo = details::control_type<std::remove_cv_t<T>>; > + > + assert(type_ == TypeInfo::value); > + assert(isArray_ == (TypeInfo::size > 0)); > + > + if constexpr (TypeInfo::size > 0) { > + return T(reinterpret_cast<const typename T::value_type *>(data().data()), numElements_); > + } else { > + assert(numElements_ == 1); > + return *reinterpret_cast<const T *>(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 1e1b49e6bd..222030c434 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<typename T> T ControlValue::ControlValue(const T &value) > * \brief Construct a ControlValue of type T > @@ -213,84 +223,7 @@ Span<uint8_t> ControlValue::data() > */ > std::string ControlValue::toString() const > { > - if (type_ == ControlTypeNone) > - return "<ValueType Error>"; > - > - const uint8_t *data = ControlValue::data().data(); > - > - if (type_ == ControlTypeString) > - return std::string(reinterpret_cast<const char *>(data), > - numElements_); > - > - std::string str(isArray_ ? "[ " : ""); > - > - for (unsigned int i = 0; i < numElements_; ++i) { > - switch (type_) { > - case ControlTypeBool: { > - const bool *value = reinterpret_cast<const bool *>(data); > - str += *value ? "true" : "false"; > - break; > - } > - case ControlTypeByte: { > - const uint8_t *value = reinterpret_cast<const uint8_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeUnsigned16: { > - const uint16_t *value = reinterpret_cast<const uint16_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeUnsigned32: { > - const uint32_t *value = reinterpret_cast<const uint32_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeInteger32: { > - const int32_t *value = reinterpret_cast<const int32_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeInteger64: { > - const int64_t *value = reinterpret_cast<const int64_t *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeFloat: { > - const float *value = reinterpret_cast<const float *>(data); > - str += std::to_string(*value); > - break; > - } > - case ControlTypeRectangle: { > - const Rectangle *value = reinterpret_cast<const Rectangle *>(data); > - str += value->toString(); > - break; > - } > - case ControlTypeSize: { > - const Size *value = reinterpret_cast<const Size *>(data); > - str += value->toString(); > - break; > - } > - case ControlTypePoint: { > - const Point *value = reinterpret_cast<const Point *>(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&&>(std::ostringstream{} << ControlValueView(*this)).str(); > } > > /** > @@ -395,6 +328,185 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen > storage_ = reinterpret_cast<void *>(new uint8_t[newSize]); > } > > +/** > + * \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 is destroyed. > + * > + * \sa ControlValue::ControlValue() > + */ > + > +/** > + * \fn ControlValueView::operator bool() const > + * \brief Determine if the referenced ControlValue 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<const std::byte> 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<typename T> 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<const char *>(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<const bool *>(data); > + s << (*value ? "true" : "false"); > + break; > + } > + case ControlTypeByte: { > + const auto *value = reinterpret_cast<const uint8_t *>(data); > + s << static_cast<unsigned int>(*value); > + break; > + } > + case ControlTypeUnsigned16: { > + const auto *value = reinterpret_cast<const uint16_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeUnsigned32: { > + const auto *value = reinterpret_cast<const uint32_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeInteger32: { > + const auto *value = reinterpret_cast<const int32_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeInteger64: { > + const auto *value = reinterpret_cast<const int64_t *>(data); > + s << *value; > + break; > + } > + case ControlTypeFloat: { > + const auto *value = reinterpret_cast<const float *>(data); > + s << std::fixed << *value; > + break; > + } > + case ControlTypeRectangle: { > + const auto *value = reinterpret_cast<const Rectangle *>(data); > + s << *value; > + break; > + } > + case ControlTypeSize: { > + const auto *value = reinterpret_cast<const Size *>(data); > + s << *value; > + break; > + } > + case ControlTypePoint: { > + const auto *value = reinterpret_cast<const Point *>(data); > + s << *value; > + break; > + } > + case ControlTypeNone: > + case ControlTypeString: > + break; > + } > + > + data += ControlValueSize[type]; > + } > + > + if (isArray) > + s << " ]"; > + > + return s; > +} > + > /** > * \class ControlId > * \brief Control static metadata
diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h index 1ee1971c4d..4af52b4f7d 100644 --- a/include/libcamera/controls.h +++ b/include/libcamera/controls.h @@ -8,6 +8,7 @@ #pragma once #include <assert.h> +#include <cstddef> #include <map> #include <optional> #include <stdint.h> @@ -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<const std::byte *>(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<const std::byte> data() const; + + [[nodiscard]] bool operator==(const ControlValueView &other) const; + + [[nodiscard]] bool operator!=(const ControlValueView &other) const + { + return !(*this == other); + } + + template<typename T> + [[nodiscard]] auto get() const + { + using TypeInfo = details::control_type<std::remove_cv_t<T>>; + + assert(type_ == TypeInfo::value); + assert(isArray_ == (TypeInfo::size > 0)); + + if constexpr (TypeInfo::size > 0) { + return T(reinterpret_cast<const typename T::value_type *>(data().data()), numElements_); + } else { + assert(numElements_ == 1); + return *reinterpret_cast<const T *>(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 1e1b49e6bd..222030c434 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<typename T> T ControlValue::ControlValue(const T &value) * \brief Construct a ControlValue of type T @@ -213,84 +223,7 @@ Span<uint8_t> ControlValue::data() */ std::string ControlValue::toString() const { - if (type_ == ControlTypeNone) - return "<ValueType Error>"; - - const uint8_t *data = ControlValue::data().data(); - - if (type_ == ControlTypeString) - return std::string(reinterpret_cast<const char *>(data), - numElements_); - - std::string str(isArray_ ? "[ " : ""); - - for (unsigned int i = 0; i < numElements_; ++i) { - switch (type_) { - case ControlTypeBool: { - const bool *value = reinterpret_cast<const bool *>(data); - str += *value ? "true" : "false"; - break; - } - case ControlTypeByte: { - const uint8_t *value = reinterpret_cast<const uint8_t *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeUnsigned16: { - const uint16_t *value = reinterpret_cast<const uint16_t *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeUnsigned32: { - const uint32_t *value = reinterpret_cast<const uint32_t *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeInteger32: { - const int32_t *value = reinterpret_cast<const int32_t *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeInteger64: { - const int64_t *value = reinterpret_cast<const int64_t *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeFloat: { - const float *value = reinterpret_cast<const float *>(data); - str += std::to_string(*value); - break; - } - case ControlTypeRectangle: { - const Rectangle *value = reinterpret_cast<const Rectangle *>(data); - str += value->toString(); - break; - } - case ControlTypeSize: { - const Size *value = reinterpret_cast<const Size *>(data); - str += value->toString(); - break; - } - case ControlTypePoint: { - const Point *value = reinterpret_cast<const Point *>(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&&>(std::ostringstream{} << ControlValueView(*this)).str(); } /** @@ -395,6 +328,185 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen storage_ = reinterpret_cast<void *>(new uint8_t[newSize]); } +/** + * \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 is destroyed. + * + * \sa ControlValue::ControlValue() + */ + +/** + * \fn ControlValueView::operator bool() const + * \brief Determine if the referenced ControlValue 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<const std::byte> 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<typename T> 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<const char *>(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<const bool *>(data); + s << (*value ? "true" : "false"); + break; + } + case ControlTypeByte: { + const auto *value = reinterpret_cast<const uint8_t *>(data); + s << static_cast<unsigned int>(*value); + break; + } + case ControlTypeUnsigned16: { + const auto *value = reinterpret_cast<const uint16_t *>(data); + s << *value; + break; + } + case ControlTypeUnsigned32: { + const auto *value = reinterpret_cast<const uint32_t *>(data); + s << *value; + break; + } + case ControlTypeInteger32: { + const auto *value = reinterpret_cast<const int32_t *>(data); + s << *value; + break; + } + case ControlTypeInteger64: { + const auto *value = reinterpret_cast<const int64_t *>(data); + s << *value; + break; + } + case ControlTypeFloat: { + const auto *value = reinterpret_cast<const float *>(data); + s << std::fixed << *value; + break; + } + case ControlTypeRectangle: { + const auto *value = reinterpret_cast<const Rectangle *>(data); + s << *value; + break; + } + case ControlTypeSize: { + const auto *value = reinterpret_cast<const Size *>(data); + s << *value; + break; + } + case ControlTypePoint: { + const auto *value = reinterpret_cast<const Point *>(data); + s << *value; + break; + } + case ControlTypeNone: + case ControlTypeString: + break; + } + + data += ControlValueSize[type]; + } + + if (isArray) + s << " ]"; + + return s; +} + /** * \class ControlId * \brief Control static metadata