diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 8d09517e..16c6a21f 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -14,7 +14,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h index 67761cb6..11fccd27 100644 --- a/include/libcamera/internal/matrix.h +++ b/include/libcamera/internal/matrix.h @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 4d2a09bd..21ce8b39 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -49,6 +49,7 @@ libcamera_internal_headers = files([ 'v4l2_subdevice.h', 'v4l2_videodevice.h', 'vector.h', + 'yaml_object.h', 'yaml_parser.h', ]) diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h index cfd8882c..af24485f 100644 --- a/include/libcamera/internal/vector.h +++ b/include/libcamera/internal/vector.h @@ -19,7 +19,7 @@ #include #include "libcamera/internal/matrix.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/yaml_object.h new file mode 100644 index 00000000..804f4a5b --- /dev/null +++ b/include/libcamera/internal/yaml_object.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * Copyright (C) 2026, Ideas on Board + * + * libcamera YAML object + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace libcamera { + +class YamlObject +{ +private: + struct Value { + Value(std::string k, std::unique_ptr &&v) + : key(std::move(k)), value(std::move(v)) + { + } + std::string key; + std::unique_ptr value; + }; + + using ValueContainer = std::vector; + +public: +#ifndef __DOXYGEN__ + template + class Iterator + { + public: + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + Iterator(typename ValueContainer::const_iterator it) + : it_(it) + { + } + + Derived &operator++() + { + ++it_; + return *static_cast(this); + } + + Derived operator++(int) + { + Derived it = *static_cast(this); + it_++; + return it; + } + + friend bool operator==(const Iterator &a, const Iterator &b) + { + return a.it_ == b.it_; + } + + friend bool operator!=(const Iterator &a, const Iterator &b) + { + return a.it_ != b.it_; + } + + protected: + ValueContainer::const_iterator it_; + }; + + template + class Adapter + { + public: + Adapter(const ValueContainer &container) + : container_(container) + { + } + + Iterator begin() const + { + return Iterator{ container_.begin() }; + } + + Iterator end() const + { + return Iterator{ container_.end() }; + } + + protected: + const ValueContainer &container_; + }; + + class ListIterator : public Iterator + { + public: + using value_type = const YamlObject &; + using pointer = const YamlObject *; + using reference = value_type; + + value_type operator*() const + { + return *it_->value.get(); + } + + pointer operator->() const + { + return it_->value.get(); + } + }; + + class DictIterator : public Iterator + { + public: + using value_type = std::pair; + using pointer = value_type *; + using reference = value_type &; + + value_type operator*() const + { + return { it_->key, *it_->value.get() }; + } + }; + + class DictAdapter : public Adapter + { + public: + using key_type = std::string; + }; + + class ListAdapter : public Adapter + { + }; +#endif /* __DOXYGEN__ */ + + YamlObject(); + ~YamlObject(); + + bool isValue() const + { + return type_ == Type::Value; + } + bool isList() const + { + return type_ == Type::List; + } + bool isDictionary() const + { + return type_ == Type::Dictionary; + } + bool isEmpty() const + { + return type_ == Type::Empty; + } + explicit operator bool() const + { + return type_ != Type::Empty; + } + + std::size_t size() const; + + template + std::optional get() const + { + return Accessor{}.get(*this); + } + + template + T get(U &&defaultValue) const + { + return get().value_or(std::forward(defaultValue)); + } + + template + void set(T &&value) + { + return Accessor>>{} + .set(*this, std::forward(value)); + } + + DictAdapter asDict() const { return DictAdapter{ list_ }; } + ListAdapter asList() const { return ListAdapter{ list_ }; } + + const YamlObject &operator[](std::size_t index) const; + + bool contains(std::string_view key) const; + const YamlObject &operator[](std::string_view key) const; + + YamlObject *add(std::unique_ptr &&child); + YamlObject *add(std::string key, std::unique_ptr &&child); + +private: + LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) + + template + friend struct Accessor; + + enum class Type { + Dictionary, + List, + Value, + Empty, + }; + + template + struct Accessor { + std::optional get(const YamlObject &obj) const; + void set(YamlObject &obj, T value); + }; + + Type type_; + + std::string value_; + ValueContainer list_; + std::map> dictionary_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h index 7707e746..e503e83a 100644 --- a/include/libcamera/internal/yaml_parser.h +++ b/include/libcamera/internal/yaml_parser.h @@ -7,222 +7,13 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include -#include +#include "libcamera/internal/yaml_object.h" namespace libcamera { class File; -class YamlParserContext; - -class YamlObject -{ -private: - struct Value { - Value(std::string k, std::unique_ptr &&v) - : key(std::move(k)), value(std::move(v)) - { - } - std::string key; - std::unique_ptr value; - }; - - using ValueContainer = std::vector; - -public: -#ifndef __DOXYGEN__ - template - class Iterator - { - public: - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; - - Iterator(typename ValueContainer::const_iterator it) - : it_(it) - { - } - - Derived &operator++() - { - ++it_; - return *static_cast(this); - } - - Derived operator++(int) - { - Derived it = *static_cast(this); - it_++; - return it; - } - - friend bool operator==(const Iterator &a, const Iterator &b) - { - return a.it_ == b.it_; - } - - friend bool operator!=(const Iterator &a, const Iterator &b) - { - return a.it_ != b.it_; - } - - protected: - ValueContainer::const_iterator it_; - }; - - template - class Adapter - { - public: - Adapter(const ValueContainer &container) - : container_(container) - { - } - - Iterator begin() const - { - return Iterator{ container_.begin() }; - } - - Iterator end() const - { - return Iterator{ container_.end() }; - } - - protected: - const ValueContainer &container_; - }; - - class ListIterator : public Iterator - { - public: - using value_type = const YamlObject &; - using pointer = const YamlObject *; - using reference = value_type; - - value_type operator*() const - { - return *it_->value.get(); - } - - pointer operator->() const - { - return it_->value.get(); - } - }; - - class DictIterator : public Iterator - { - public: - using value_type = std::pair; - using pointer = value_type *; - using reference = value_type &; - - value_type operator*() const - { - return { it_->key, *it_->value.get() }; - } - }; - - class DictAdapter : public Adapter - { - public: - using key_type = std::string; - }; - - class ListAdapter : public Adapter - { - }; -#endif /* __DOXYGEN__ */ - - YamlObject(); - ~YamlObject(); - - bool isValue() const - { - return type_ == Type::Value; - } - bool isList() const - { - return type_ == Type::List; - } - bool isDictionary() const - { - return type_ == Type::Dictionary; - } - bool isEmpty() const - { - return type_ == Type::Empty; - } - explicit operator bool() const - { - return type_ != Type::Empty; - } - - std::size_t size() const; - - template - std::optional get() const - { - return Accessor{}.get(*this); - } - - template - T get(U &&defaultValue) const - { - return get().value_or(std::forward(defaultValue)); - } - - template - void set(T &&value) - { - return Accessor>>{} - .set(*this, std::forward(value)); - } - - DictAdapter asDict() const { return DictAdapter{ list_ }; } - ListAdapter asList() const { return ListAdapter{ list_ }; } - - const YamlObject &operator[](std::size_t index) const; - - bool contains(std::string_view key) const; - const YamlObject &operator[](std::string_view key) const; - - YamlObject *add(std::unique_ptr &&child); - YamlObject *add(std::string key, std::unique_ptr &&child); - -private: - LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject) - - template - friend struct Accessor; - - enum class Type { - Dictionary, - List, - Value, - Empty, - }; - - template - struct Accessor { - std::optional get(const YamlObject &obj) const; - void set(YamlObject &obj, T value); - }; - - Type type_; - - std::string value_; - ValueContainer list_; - std::map> dictionary_; -}; class YamlParser final { diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 0df97b27..dfe1ccbe 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "exposure_mode_helper.h" #include "histogram.h" diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h index f4a86038..3f25d13f 100644 --- a/src/ipa/libipa/awb.h +++ b/src/ipa/libipa/awb.h @@ -14,7 +14,7 @@ #include #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h index 47ef3cce..79334ad3 100644 --- a/src/ipa/libipa/awb_bayes.h +++ b/src/ipa/libipa/awb_bayes.h @@ -10,7 +10,7 @@ #include #include "libcamera/internal/vector.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "awb.h" #include "interpolator.h" diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h index fb2c611d..af35fc55 100644 --- a/src/ipa/libipa/interpolator.h +++ b/src/ipa/libipa/interpolator.h @@ -15,7 +15,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h index c71f215d..19da4686 100644 --- a/src/ipa/libipa/lsc_polynomial.h +++ b/src/ipa/libipa/lsc_polynomial.h @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp index 899e8824..d79b116a 100644 --- a/src/ipa/libipa/lux.cpp +++ b/src/ipa/libipa/lux.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "histogram.h" diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h index c27af771..8e6c658a 100644 --- a/src/ipa/libipa/module.h +++ b/src/ipa/libipa/module.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "algorithm.h" diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp index d099219c..3bdf1914 100644 --- a/src/ipa/mali-c55/algorithms/blc.cpp +++ b/src/ipa/mali-c55/algorithms/blc.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file blc.h diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp index f75b9cd7..e32f5a83 100644 --- a/src/ipa/mali-c55/algorithms/lsc.cpp +++ b/src/ipa/mali-c55/algorithms/lsc.cpp @@ -7,7 +7,7 @@ #include "lsc.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" namespace libcamera { diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 7cc06f91..ef16a377 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -19,7 +19,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/histogram.h" diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp index 32fc44ff..19c262ff 100644 --- a/src/ipa/rkisp1/algorithms/blc.cpp +++ b/src/ipa/rkisp1/algorithms/blc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file blc.h diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp index 466d7a11..567891d1 100644 --- a/src/ipa/rkisp1/algorithms/ccm.cpp +++ b/src/ipa/rkisp1/algorithms/ccm.cpp @@ -16,7 +16,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/fixedpoint.h" #include "libipa/interpolator.h" diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp index 78946281..c1953347 100644 --- a/src/ipa/rkisp1/algorithms/dpcc.cpp +++ b/src/ipa/rkisp1/algorithms/dpcc.cpp @@ -9,7 +9,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp index a0e7030f..6c07bd8c 100644 --- a/src/ipa/rkisp1/algorithms/goc.cpp +++ b/src/ipa/rkisp1/algorithms/goc.cpp @@ -13,7 +13,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp index 7ac5dc21..292d0e80 100644 --- a/src/ipa/rkisp1/algorithms/gsl.cpp +++ b/src/ipa/rkisp1/algorithms/gsl.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp index 3f456210..7302eb58 100644 --- a/src/ipa/rkisp1/algorithms/lsc.cpp +++ b/src/ipa/rkisp1/algorithms/lsc.cpp @@ -14,7 +14,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "libipa/lsc_polynomial.h" #include "linux/rkisp1-config.h" diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp index ed81628c..9e2688a0 100644 --- a/src/ipa/rkisp1/algorithms/wdr.cpp +++ b/src/ipa/rkisp1/algorithms/wdr.cpp @@ -10,7 +10,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include #include diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h index 1971bfdc..8839daa9 100644 --- a/src/ipa/rpi/controller/algorithm.h +++ b/src/ipa/rpi/controller/algorithm.h @@ -15,7 +15,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "controller.h" diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h index fdb46557..57394288 100644 --- a/src/ipa/rpi/controller/controller.h +++ b/src/ipa/rpi/controller/controller.h @@ -16,7 +16,7 @@ #include #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "camera_mode.h" #include "device_status.h" diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp index 2e8e9e33..62100884 100644 --- a/src/libcamera/geometry.cpp +++ b/src/libcamera/geometry.cpp @@ -12,7 +12,7 @@ #include -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" /** * \file geometry.h diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 575408b2..1aafc84b 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -58,6 +58,7 @@ libcamera_internal_sources = files([ 'v4l2_subdevice.cpp', 'v4l2_videodevice.cpp', 'vector.cpp', + 'yaml_object.cpp', 'yaml_parser.cpp', ]) diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp index 6d2072e8..08a2b32d 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp @@ -21,6 +21,7 @@ #include "libcamera/internal/camera_lens.h" #include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/yaml_parser.h" using namespace std::chrono_literals; diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h index 597eb587..8b5d78d2 100644 --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h @@ -26,7 +26,7 @@ #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" #include "libcamera/internal/v4l2_videodevice.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include #include diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h index d2000de9..f696d886 100644 --- a/src/libcamera/pipeline/virtual/config_parser.h +++ b/src/libcamera/pipeline/virtual/config_parser.h @@ -13,7 +13,7 @@ #include #include "libcamera/internal/pipeline_handler.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "virtual.h" diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index e8ef7e52..3e8fdf49 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -37,7 +37,7 @@ #include "libcamera/internal/framebuffer.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/request.h" -#include "libcamera/internal/yaml_parser.h" +#include "libcamera/internal/yaml_object.h" #include "pipeline/virtual/config_parser.h" diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp new file mode 100644 index 00000000..2e233c46 --- /dev/null +++ b/src/libcamera/yaml_object.cpp @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Google Inc. + * Copyright (C) 2025, Ideas on Board. + * + * libcamera YAML object + */ + +#include "libcamera/internal/yaml_object.h" + +#include +#include +#include +#include + +#include + +/** + * \file yaml_object.h + * \brief YAML objects + */ + +namespace libcamera { + +namespace { + +/* Empty static YamlObject as a safe result for invalid operations */ +static const YamlObject empty; + +} /* namespace */ + +/** + * \class YamlObject + * \brief A class representing the tree structure of the YAML content + * + * The YamlObject class represents the tree structure of YAML content. A + * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a + * tree leaf. + */ + +YamlObject::YamlObject() + : type_(Type::Empty) +{ +} + +YamlObject::~YamlObject() = default; + +/** + * \fn YamlObject::isValue() + * \brief Return whether the YamlObject is a value + * + * \return True if the YamlObject is a value, false otherwise + */ + +/** + * \fn YamlObject::isList() + * \brief Return whether the YamlObject is a list + * + * \return True if the YamlObject is a list, false otherwise + */ + +/** + * \fn YamlObject::isDictionary() + * \brief Return whether the YamlObject is a dictionary + * + * \return True if the YamlObject is a dictionary, false otherwise + */ + +/** + * \fn YamlObject::isEmpty() + * \brief Return whether the YamlObject is an empty + * + * \return True if the YamlObject is empty, false otherwise + */ + +/** + * \fn YamlObject::operator bool() + * \brief Return whether the YamlObject is a non-empty + * + * \return False if the YamlObject is empty, true otherwise + */ + +/** + * \fn YamlObject::size() + * \brief Retrieve the number of elements in a dictionary or list YamlObject + * + * This function retrieves the size of the YamlObject, defined as the number of + * child elements it contains. Only YamlObject instances of Dictionary or List + * types have a size, calling this function on other types of instances is + * invalid and results in undefined behaviour. + * + * \return The size of the YamlObject + */ +std::size_t YamlObject::size() const +{ + switch (type_) { + case Type::Dictionary: + case Type::List: + return list_.size(); + default: + return 0; + } +} + +/** + * \fn template YamlObject::get() const + * \tparam T Type of the value + * \brief Parse the YamlObject as a \a T value + * + * This function parses the value of the YamlObject as a \a T object, and + * returns the value. If parsing fails (usually because the YamlObject doesn't + * store a \a T value), std::nullopt is returned. + * + * If the type \a T is an std::vector, the YamlObject will be parsed as a list + * of values. + * + * \return The YamlObject value, or std::nullopt if parsing failed + */ + +/** + * \fn template YamlObject::get(U &&defaultValue) const + * \brief Parse the YamlObject as a \a T value + * \param[in] defaultValue The default value when failing to parse + * + * This function parses the value of the YamlObject as a \a T object, and + * returns the value. If parsing fails (usually because the YamlObject doesn't + * store a \a T value), the \a defaultValue is returned. + * + * Unlike the get() function, this overload does not support std::vector for the + * type \a T. + * + * \return The YamlObject value, or \a defaultValue if parsing failed + */ + +/** + * \fn template YamlObject::set(T &&value) + * \brief Set the value of a YamlObject + * \param[in] value The value + * + * This function sets the value stored in a YamlObject to \a value. The value is + * converted to a string in an implementation-specific way that guarantees that + * subsequent calls to get() will return the same value. + * + * \todo Implement the conversion guarantee for floating point types + * + * Values can only be set on YamlObject of Type::Value type or empty YamlObject. + * Attempting to set a value on an object of type Type::Dict or Type::List does + * not modify the YamlObject. + */ + +#ifndef __DOXYGEN__ + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_ == "true") + return true; + else if (obj.value_ == "false") + return false; + + return std::nullopt; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, bool value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = value ? "true" : "false"; +} + +template +struct YamlObject::Accessor || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v>> +{ + std::optional get(const YamlObject &obj) const + { + if (obj.type_ != Type::Value) + return std::nullopt; + + const std::string &str = obj.value_; + T value = {}; + + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), + value); + if (ptr != str.data() + str.size() || ec != std::errc()) + return std::nullopt; + + return value; + } + + void set(YamlObject &obj, T value) + { + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); + } +}; + +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; +template struct YamlObject::Accessor; + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + return obj.get(); +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, float value) +{ + obj.set(value); +} + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + if (obj.value_.empty()) + return std::nullopt; + + char *end; + + errno = 0; + double value = utils::strtod(obj.value_.c_str(), &end); + + if ('\0' != *end || errno == ERANGE) + return std::nullopt; + + return value; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, double value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::to_string(value); +} + +template<> +std::optional +YamlObject::Accessor::get(const YamlObject &obj) const +{ + if (obj.type_ != Type::Value) + return std::nullopt; + + return obj.value_; +} + +template<> +void YamlObject::Accessor::set(YamlObject &obj, std::string value) +{ + if (obj.type_ != Type::Empty && obj.type_ != Type::Value) + return; + + obj.type_ = Type::Value; + obj.value_ = std::move(value); +} + +template +struct YamlObject::Accessor> { + std::optional> get(const YamlObject &obj) const + { + if (obj.type_ != Type::List) + return std::nullopt; + + std::vector values; + values.reserve(obj.list_.size()); + + for (const YamlObject &entry : obj.asList()) { + auto value = entry.get(); + if (!value) + return std::nullopt; + values.emplace_back(std::move(*value)); + } + + return values; + } +}; + +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +template struct YamlObject::Accessor>; +#endif /* __DOXYGEN__ */ + +/** + * \fn YamlObject::asDict() const + * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators + * + * The YamlObject class doesn't directly implement iterators, as the iterator + * type depends on whether the object is a Dictionary or List. This function + * wraps a YamlObject of Dictionary type into an adapter that exposes + * iterators, as well as begin() and end() functions, allowing usage of + * range-based for loops with YamlObject. As YAML mappings are not ordered, the + * iteration order is not specified. + * + * The iterator's value_type is a + * std::pair. + * + * If the YamlObject is not of Dictionary type, the returned adapter operates + * as an empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \fn YamlObject::asList() const + * \brief Wrap a list YamlObject in an adapter that exposes iterators + * + * The YamlObject class doesn't directly implement iterators, as the iterator + * type depends on whether the object is a Dictionary or List. This function + * wraps a YamlObject of List type into an adapter that exposes iterators, as + * well as begin() and end() functions, allowing usage of range-based for loops + * with YamlObject. As YAML lists are ordered, the iteration order is identical + * to the list order in the YAML data. + * + * The iterator's value_type is a const YamlObject &. + * + * If the YamlObject is not of List type, the returned adapter operates as an + * empty container. + * + * \return An adapter of unspecified type compatible with range-based for loops + */ + +/** + * \fn YamlObject::operator[](std::size_t index) const + * \brief Retrieve the element from list YamlObject by index + * + * This function retrieves an element of the YamlObject. Only YamlObject + * instances of List type associate elements with index, calling this function + * on other types of instances or with an invalid index results in an empty + * object. + * + * \return The YamlObject as an element of the list + */ +const YamlObject &YamlObject::operator[](std::size_t index) const +{ + if (type_ != Type::List || index >= size()) + return empty; + + return *list_[index].value; +} + +/** + * \fn YamlObject::contains() + * \brief Check if an element of a dictionary exists + * + * This function check if the YamlObject contains an element. Only YamlObject + * instances of Dictionary type associate elements with names, calling this + * function on other types of instances is invalid and results in undefined + * behaviour. + * + * \return True if an element exists, false otherwise + */ +bool YamlObject::contains(std::string_view key) const +{ + return dictionary_.find(key) != dictionary_.end(); +} + +/** + * \fn YamlObject::operator[](std::string_view key) const + * \brief Retrieve a member by name from the dictionary + * + * This function retrieve a member of a YamlObject by name. Only YamlObject + * instances of Dictionary type associate elements with names, calling this + * function on other types of instances or with a nonexistent key results in an + * empty object. + * + * \return The YamlObject corresponding to the \a key member + */ +const YamlObject &YamlObject::operator[](std::string_view key) const +{ + if (type_ != Type::Dictionary) + return empty; + + auto iter = dictionary_.find(key); + if (iter == dictionary_.end()) + return empty; + + return *iter->second; +} + +/** + * \brief Add a child object to a list + * \param[in] child The child object + * + * Append the \a child object as the last element of this object's children + * list. This object must be empty, in which case it is converted to the + * Type::List type, or be a list. Otherwise, the function returns a nullptr and + * the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::List; + + if (type_ != Type::List) + return nullptr; + + Value &elem = list_.emplace_back(std::string{}, std::move(child)); + return elem.value.get(); +} + +/** + * \brief Add a child object to a dictionary + * \param[in] key The dictionary key + * \param[in] child The child object + * + * Add the \a child object with the given \a key to this object's children. This + * object must be empty, in which case it is converted to the Type::Dictionary + * type, or be a dictionary. Otherwise, the function returns a nullptr and the + * \a child is not modified. + * + * Keys are unique. If a child with the same \a key already exists, the function + * returns a nullptr and the \a child is not modified. + * + * \return A pointer to the child object if successfully added, nullptr + * otherwise + */ +YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) +{ + if (type_ == Type::Empty) + type_ = Type::Dictionary; + + if (type_ != Type::Dictionary) + return nullptr; + + auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); + if (!inserted) + return nullptr; + + return list_.emplace_back(it->first, std::move(child)).value.get(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp index 2dcfcb4a..91c8f3e5 100644 --- a/src/libcamera/yaml_parser.cpp +++ b/src/libcamera/yaml_parser.cpp @@ -7,11 +7,10 @@ #include "libcamera/internal/yaml_parser.h" -#include #include #include -#include -#include +#include +#include #include #include @@ -27,449 +26,6 @@ namespace libcamera { LOG_DEFINE_CATEGORY(YamlParser) -namespace { - -/* Empty static YamlObject as a safe result for invalid operations */ -static const YamlObject empty; - -} /* namespace */ - -/** - * \class YamlObject - * \brief A class representing the tree structure of the YAML content - * - * The YamlObject class represents the tree structure of YAML content. A - * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a - * tree leaf. - */ - -YamlObject::YamlObject() - : type_(Type::Empty) -{ -} - -YamlObject::~YamlObject() = default; - -/** - * \fn YamlObject::isValue() - * \brief Return whether the YamlObject is a value - * - * \return True if the YamlObject is a value, false otherwise - */ - -/** - * \fn YamlObject::isList() - * \brief Return whether the YamlObject is a list - * - * \return True if the YamlObject is a list, false otherwise - */ - -/** - * \fn YamlObject::isDictionary() - * \brief Return whether the YamlObject is a dictionary - * - * \return True if the YamlObject is a dictionary, false otherwise - */ - -/** - * \fn YamlObject::isEmpty() - * \brief Return whether the YamlObject is an empty - * - * \return True if the YamlObject is empty, false otherwise - */ - -/** - * \fn YamlObject::operator bool() - * \brief Return whether the YamlObject is a non-empty - * - * \return False if the YamlObject is empty, true otherwise - */ - -/** - * \brief Retrieve the number of elements in a dictionary or list YamlObject - * - * This function retrieves the size of the YamlObject, defined as the number of - * child elements it contains. Only YamlObject instances of Dictionary or List - * types have a size, calling this function on other types of instances is - * invalid and results in undefined behaviour. - * - * \return The size of the YamlObject - */ -std::size_t YamlObject::size() const -{ - switch (type_) { - case Type::Dictionary: - case Type::List: - return list_.size(); - default: - return 0; - } -} - -/** - * \fn template YamlObject::get() const - * \tparam T Type of the value - * \brief Parse the YamlObject as a \a T value - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), std::nullopt is returned. - * - * If the type \a T is an std::vector, the YamlObject will be parsed as a list - * of values. - * - * \return The YamlObject value, or std::nullopt if parsing failed - */ - -/** - * \fn template YamlObject::get(U &&defaultValue) const - * \brief Parse the YamlObject as a \a T value - * \param[in] defaultValue The default value when failing to parse - * - * This function parses the value of the YamlObject as a \a T object, and - * returns the value. If parsing fails (usually because the YamlObject doesn't - * store a \a T value), the \a defaultValue is returned. - * - * Unlike the get() function, this overload does not support std::vector for the - * type \a T. - * - * \return The YamlObject value, or \a defaultValue if parsing failed - */ - -/** - * \fn template YamlObject::set(T &&value) - * \brief Set the value of a YamlObject - * \param[in] value The value - * - * This function sets the value stored in a YamlObject to \a value. The value is - * converted to a string in an implementation-specific way that guarantees that - * subsequent calls to get() will return the same value. - * - * \todo Implement the conversion guarantee for floating point types - * - * Values can only be set on YamlObject of Type::Value type or empty YamlObject. - * Attempting to set a value on an object of type Type::Dict or Type::List does - * not modify the YamlObject. - */ - -#ifndef __DOXYGEN__ - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_ == "true") - return true; - else if (obj.value_ == "false") - return false; - - return std::nullopt; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, bool value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = value ? "true" : "false"; -} - -template -struct YamlObject::Accessor || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v>> -{ - std::optional get(const YamlObject &obj) const - { - if (obj.type_ != Type::Value) - return std::nullopt; - - const std::string &str = obj.value_; - T value = {}; - - auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), - value); - if (ptr != str.data() + str.size() || ec != std::errc()) - return std::nullopt; - - return value; - } - - void set(YamlObject &obj, T value) - { - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); - } -}; - -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; -template struct YamlObject::Accessor; - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - return obj.get(); -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, float value) -{ - obj.set(value); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - if (obj.value_.empty()) - return std::nullopt; - - char *end; - - errno = 0; - double value = utils::strtod(obj.value_.c_str(), &end); - - if ('\0' != *end || errno == ERANGE) - return std::nullopt; - - return value; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, double value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::to_string(value); -} - -template<> -std::optional -YamlObject::Accessor::get(const YamlObject &obj) const -{ - if (obj.type_ != Type::Value) - return std::nullopt; - - return obj.value_; -} - -template<> -void YamlObject::Accessor::set(YamlObject &obj, std::string value) -{ - if (obj.type_ != Type::Empty && obj.type_ != Type::Value) - return; - - obj.type_ = Type::Value; - obj.value_ = std::move(value); -} - -template -struct YamlObject::Accessor> { - std::optional> get(const YamlObject &obj) const - { - if (obj.type_ != Type::List) - return std::nullopt; - - std::vector values; - values.reserve(obj.list_.size()); - - for (const YamlObject &entry : obj.asList()) { - auto value = entry.get(); - if (!value) - return std::nullopt; - values.emplace_back(std::move(*value)); - } - - return values; - } -}; - -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -template struct YamlObject::Accessor>; -#endif /* __DOXYGEN__ */ - -/** - * \fn YamlObject::asDict() const - * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of Dictionary type into an adapter that exposes - * iterators, as well as begin() and end() functions, allowing usage of - * range-based for loops with YamlObject. As YAML mappings are not ordered, the - * iteration order is not specified. - * - * The iterator's value_type is a - * std::pair. - * - * If the YamlObject is not of Dictionary type, the returned adapter operates - * as an empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \fn YamlObject::asList() const - * \brief Wrap a list YamlObject in an adapter that exposes iterators - * - * The YamlObject class doesn't directly implement iterators, as the iterator - * type depends on whether the object is a Dictionary or List. This function - * wraps a YamlObject of List type into an adapter that exposes iterators, as - * well as begin() and end() functions, allowing usage of range-based for loops - * with YamlObject. As YAML lists are ordered, the iteration order is identical - * to the list order in the YAML data. - * - * The iterator's value_type is a const YamlObject &. - * - * If the YamlObject is not of List type, the returned adapter operates as an - * empty container. - * - * \return An adapter of unspecified type compatible with range-based for loops - */ - -/** - * \fn YamlObject::operator[](std::size_t index) const - * \brief Retrieve the element from list YamlObject by index - * - * This function retrieves an element of the YamlObject. Only YamlObject - * instances of List type associate elements with index, calling this function - * on other types of instances or with an invalid index results in an empty - * object. - * - * \return The YamlObject as an element of the list - */ -const YamlObject &YamlObject::operator[](std::size_t index) const -{ - if (type_ != Type::List || index >= size()) - return empty; - - return *list_[index].value; -} - -/** - * \fn YamlObject::contains() - * \brief Check if an element of a dictionary exists - * - * This function check if the YamlObject contains an element. Only YamlObject - * instances of Dictionary type associate elements with names, calling this - * function on other types of instances is invalid and results in undefined - * behaviour. - * - * \return True if an element exists, false otherwise - */ -bool YamlObject::contains(std::string_view key) const -{ - return dictionary_.find(key) != dictionary_.end(); -} - -/** - * \fn YamlObject::operator[](std::string_view key) const - * \brief Retrieve a member by name from the dictionary - * - * This function retrieve a member of a YamlObject by name. Only YamlObject - * instances of Dictionary type associate elements with names, calling this - * function on other types of instances or with a nonexistent key results in an - * empty object. - * - * \return The YamlObject corresponding to the \a key member - */ -const YamlObject &YamlObject::operator[](std::string_view key) const -{ - if (type_ != Type::Dictionary) - return empty; - - auto iter = dictionary_.find(key); - if (iter == dictionary_.end()) - return empty; - - return *iter->second; -} - -/** - * \brief Add a child object to a list - * \param[in] child The child object - * - * Append the \a child object as the last element of this object's children - * list. This object must be empty, in which case it is converted to the - * Type::List type, or be a list. Otherwise, the function returns a nullptr and - * the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::List; - - if (type_ != Type::List) - return nullptr; - - Value &elem = list_.emplace_back(std::string{}, std::move(child)); - return elem.value.get(); -} - -/** - * \brief Add a child object to a dictionary - * \param[in] key The dictionary key - * \param[in] child The child object - * - * Add the \a child object with the given \a key to this object's children. This - * object must be empty, in which case it is converted to the Type::Dictionary - * type, or be a dictionary. Otherwise, the function returns a nullptr and the - * \a child is not modified. - * - * Keys are unique. If a child with the same \a key already exists, the function - * returns a nullptr and the \a child is not modified. - * - * \return A pointer to the child object if successfully added, nullptr - * otherwise - */ -YamlObject *YamlObject::add(std::string key, std::unique_ptr &&child) -{ - if (type_ == Type::Empty) - type_ = Type::Dictionary; - - if (type_ != Type::Dictionary) - return nullptr; - - auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get()); - if (!inserted) - return nullptr; - - return list_.emplace_back(it->first, std::move(child)).value.get(); -} - #ifndef __DOXYGEN__ class YamlParserContext