[{"id":37648,"web_url":"https://patchwork.libcamera.org/comment/37648/","msgid":"<dca5917c-45cb-453d-9355-ffcce276aecd@ideasonboard.com>","date":"2026-01-14T11:17:59","subject":"Re: [PATCH 17/36] libcamera: yaml_parser: Split YamlObject from\n\tYamlParser","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n> The YamlObject class was designed to represent data parsed from YAML\n> files. It is outgrowing the initial needs. Move it to a separate file to\n> prepare for a rename.\n> \n> Most files that include yaml_parser.h only need access to a YamlObject.\n> Switch them to yaml_object.h. pipeline_base.cpp was including\n> yaml_parser.h indirectly through pipeline_base.h, include it directly\n> now.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n\nLooks ok to me.\n\nReviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n\n\n>   .../libcamera/internal/global_configuration.h |   2 +-\n>   include/libcamera/internal/matrix.h           |   2 +-\n>   include/libcamera/internal/meson.build        |   1 +\n>   include/libcamera/internal/vector.h           |   2 +-\n>   include/libcamera/internal/yaml_object.h      | 226 +++++++++\n>   include/libcamera/internal/yaml_parser.h      | 212 +-------\n>   src/ipa/libipa/agc_mean_luminance.h           |   2 +-\n>   src/ipa/libipa/awb.h                          |   2 +-\n>   src/ipa/libipa/awb_bayes.h                    |   2 +-\n>   src/ipa/libipa/interpolator.cpp               |   2 -\n>   src/ipa/libipa/interpolator.h                 |   2 +-\n>   src/ipa/libipa/lsc_polynomial.h               |   2 +-\n>   src/ipa/libipa/lux.cpp                        |   2 +-\n>   src/ipa/libipa/module.h                       |   2 +-\n>   src/ipa/mali-c55/algorithms/blc.cpp           |   2 +-\n>   src/ipa/mali-c55/algorithms/lsc.cpp           |   2 +-\n>   src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/blc.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/ccm.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/dpcc.cpp            |   2 +-\n>   src/ipa/rkisp1/algorithms/goc.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/gsl.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/lsc.cpp             |   2 +-\n>   src/ipa/rkisp1/algorithms/wdr.cpp             |   2 +-\n>   src/ipa/rpi/controller/algorithm.h            |   2 +-\n>   src/ipa/rpi/controller/controller.h           |   2 +-\n>   src/libcamera/geometry.cpp                    |   2 +-\n>   src/libcamera/meson.build                     |   1 +\n>   .../pipeline/rpi/common/pipeline_base.cpp     |   1 +\n>   .../pipeline/rpi/common/pipeline_base.h       |   2 +-\n>   .../pipeline/virtual/config_parser.h          |   2 +-\n>   src/libcamera/pipeline/virtual/virtual.cpp    |   2 +-\n>   src/libcamera/yaml_object.cpp                 | 477 ++++++++++++++++++\n>   src/libcamera/yaml_parser.cpp                 | 458 +----------------\n>   34 files changed, 736 insertions(+), 694 deletions(-)\n>   create mode 100644 include/libcamera/internal/yaml_object.h\n>   create mode 100644 src/libcamera/yaml_object.cpp\n> \n> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> index 8d09517edca1..16c6a21f2a8a 100644\n> --- a/include/libcamera/internal/global_configuration.h\n> +++ b/include/libcamera/internal/global_configuration.h\n> @@ -14,7 +14,7 @@\n>   \n>   #include <libcamera/base/utils.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h\n> index 67761cb6037d..11fccd27547e 100644\n> --- a/include/libcamera/internal/matrix.h\n> +++ b/include/libcamera/internal/matrix.h\n> @@ -14,7 +14,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/base/span.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index dc48619e5ac8..c24c9f062f29 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -50,6 +50,7 @@ libcamera_internal_headers = files([\n>       'v4l2_subdevice.h',\n>       'v4l2_videodevice.h',\n>       'vector.h',\n> +    'yaml_object.h',\n>       'yaml_parser.h',\n>   ])\n>   \n> diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h\n> index cfd8882ce0e6..af24485f3bb1 100644\n> --- a/include/libcamera/internal/vector.h\n> +++ b/include/libcamera/internal/vector.h\n> @@ -19,7 +19,7 @@\n>   #include <libcamera/base/span.h>\n>   \n>   #include \"libcamera/internal/matrix.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/yaml_object.h\n> new file mode 100644\n> index 000000000000..1771f8821e2e\n> --- /dev/null\n> +++ b/include/libcamera/internal/yaml_object.h\n> @@ -0,0 +1,226 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2022, Google Inc.\n> + * Copyright (C) 2025, Ideas on Board\n> + *\n> + * libcamera YAML object\n> + */\n> +\n> +#pragma once\n> +\n> +#include <iterator>\n> +#include <map>\n> +#include <memory>\n> +#include <optional>\n> +#include <string>\n> +#include <string_view>\n> +#include <type_traits>\n> +#include <utility>\n> +#include <vector>\n> +\n> +#include <libcamera/base/class.h>\n> +\n> +namespace libcamera {\n> +\n> +class YamlObject\n> +{\n> +private:\n> +\tstruct Value {\n> +\t\tValue(std::string &&k, std::unique_ptr<YamlObject> &&v)\n> +\t\t\t: key(std::move(k)), value(std::move(v))\n> +\t\t{\n> +\t\t}\n> +\t\tstd::string key;\n> +\t\tstd::unique_ptr<YamlObject> value;\n> +\t};\n> +\n> +\tusing ValueContainer = std::vector<Value>;\n> +\n> +public:\n> +#ifndef __DOXYGEN__\n> +\ttemplate<typename Derived>\n> +\tclass Iterator\n> +\t{\n> +\tpublic:\n> +\t\tusing difference_type = std::ptrdiff_t;\n> +\t\tusing iterator_category = std::forward_iterator_tag;\n> +\n> +\t\tIterator(typename ValueContainer::const_iterator it)\n> +\t\t\t: it_(it)\n> +\t\t{\n> +\t\t}\n> +\n> +\t\tDerived &operator++()\n> +\t\t{\n> +\t\t\t++it_;\n> +\t\t\treturn *static_cast<Derived *>(this);\n> +\t\t}\n> +\n> +\t\tDerived operator++(int)\n> +\t\t{\n> +\t\t\tDerived it = *static_cast<Derived *>(this);\n> +\t\t\tit_++;\n> +\t\t\treturn it;\n> +\t\t}\n> +\n> +\t\tfriend bool operator==(const Iterator &a, const Iterator &b)\n> +\t\t{\n> +\t\t\treturn a.it_ == b.it_;\n> +\t\t}\n> +\n> +\t\tfriend bool operator!=(const Iterator &a, const Iterator &b)\n> +\t\t{\n> +\t\t\treturn a.it_ != b.it_;\n> +\t\t}\n> +\n> +\tprotected:\n> +\t\tValueContainer::const_iterator it_;\n> +\t};\n> +\n> +\ttemplate<typename Iterator>\n> +\tclass Adapter\n> +\t{\n> +\tpublic:\n> +\t\tAdapter(const ValueContainer &container)\n> +\t\t\t: container_(container)\n> +\t\t{\n> +\t\t}\n> +\n> +\t\tIterator begin() const\n> +\t\t{\n> +\t\t\treturn Iterator{ container_.begin() };\n> +\t\t}\n> +\n> +\t\tIterator end() const\n> +\t\t{\n> +\t\t\treturn Iterator{ container_.end() };\n> +\t\t}\n> +\n> +\tprotected:\n> +\t\tconst ValueContainer &container_;\n> +\t};\n> +\n> +\tclass ListIterator : public Iterator<ListIterator>\n> +\t{\n> +\tpublic:\n> +\t\tusing value_type = const YamlObject &;\n> +\t\tusing pointer = const YamlObject *;\n> +\t\tusing reference = value_type;\n> +\n> +\t\tvalue_type operator*() const\n> +\t\t{\n> +\t\t\treturn *it_->value.get();\n> +\t\t}\n> +\n> +\t\tpointer operator->() const\n> +\t\t{\n> +\t\t\treturn it_->value.get();\n> +\t\t}\n> +\t};\n> +\n> +\tclass DictIterator : public Iterator<DictIterator>\n> +\t{\n> +\tpublic:\n> +\t\tusing value_type = std::pair<const std::string &, const YamlObject &>;\n> +\t\tusing pointer = value_type *;\n> +\t\tusing reference = value_type &;\n> +\n> +\t\tvalue_type operator*() const\n> +\t\t{\n> +\t\t\treturn { it_->key, *it_->value.get() };\n> +\t\t}\n> +\t};\n> +\n> +\tclass DictAdapter : public Adapter<DictIterator>\n> +\t{\n> +\tpublic:\n> +\t\tusing key_type = std::string;\n> +\t};\n> +\n> +\tclass ListAdapter : public Adapter<ListIterator>\n> +\t{\n> +\t};\n> +#endif /* __DOXYGEN__ */\n> +\n> +\tYamlObject();\n> +\t~YamlObject();\n> +\n> +\tbool isValue() const\n> +\t{\n> +\t\treturn type_ == Type::Value;\n> +\t}\n> +\tbool isList() const\n> +\t{\n> +\t\treturn type_ == Type::List;\n> +\t}\n> +\tbool isDictionary() const\n> +\t{\n> +\t\treturn type_ == Type::Dictionary;\n> +\t}\n> +\tbool isEmpty() const\n> +\t{\n> +\t\treturn type_ == Type::Empty;\n> +\t}\n> +\texplicit operator bool() const\n> +\t{\n> +\t\treturn type_ != Type::Empty;\n> +\t}\n> +\n> +\tstd::size_t size() const;\n> +\n> +\ttemplate<typename T>\n> +\tstd::optional<T> get() const\n> +\t{\n> +\t\treturn Accessor<T>{}.get(*this);\n> +\t}\n> +\n> +\ttemplate<typename T, typename U>\n> +\tT get(U &&defaultValue) const\n> +\t{\n> +\t\treturn get<T>().value_or(std::forward<U>(defaultValue));\n> +\t}\n> +\n> +\ttemplate<typename T>\n> +\tvoid set(T &&value)\n> +\t{\n> +\t\treturn Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));\n> +\t}\n> +\n> +\tDictAdapter asDict() const { return DictAdapter{ list_ }; }\n> +\tListAdapter asList() const { return ListAdapter{ list_ }; }\n> +\n> +\tconst YamlObject &operator[](std::size_t index) const;\n> +\n> +\tbool contains(std::string_view key) const;\n> +\tconst YamlObject &operator[](std::string_view key) const;\n> +\n> +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> +\n> +private:\n> +\tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> +\n> +\ttemplate<typename T>\n> +\tfriend struct Accessor;\n> +\n> +\tenum class Type {\n> +\t\tDictionary,\n> +\t\tList,\n> +\t\tValue,\n> +\t\tEmpty,\n> +\t};\n> +\n> +\ttemplate<typename T, typename Enable = void>\n> +\tstruct Accessor {\n> +\t\tstd::optional<T> get(const YamlObject &obj) const;\n> +\t\tvoid set(YamlObject &obj, T value);\n> +\t};\n> +\n> +\tType type_;\n> +\n> +\tstd::string value_;\n> +\tValueContainer list_;\n> +\tstd::map<std::string, YamlObject *, std::less<>> dictionary_;\n> +};\n> +\n> +} /* namespace libcamera */\n> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> index 7b6d16de1a04..e503e83a80da 100644\n> --- a/include/libcamera/internal/yaml_parser.h\n> +++ b/include/libcamera/internal/yaml_parser.h\n> @@ -7,221 +7,13 @@\n>   \n>   #pragma once\n>   \n> -#include <iterator>\n> -#include <map>\n> -#include <optional>\n> -#include <stdint.h>\n> -#include <string>\n> -#include <string_view>\n> -#include <vector>\n> +#include <memory>\n>   \n> -#include <libcamera/base/class.h>\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n>   class File;\n> -class YamlParserContext;\n> -\n> -class YamlObject\n> -{\n> -private:\n> -\tstruct Value {\n> -\t\tValue(std::string &&k, std::unique_ptr<YamlObject> &&v)\n> -\t\t\t: key(std::move(k)), value(std::move(v))\n> -\t\t{\n> -\t\t}\n> -\t\tstd::string key;\n> -\t\tstd::unique_ptr<YamlObject> value;\n> -\t};\n> -\n> -\tusing ValueContainer = std::vector<Value>;\n> -\n> -public:\n> -#ifndef __DOXYGEN__\n> -\ttemplate<typename Derived>\n> -\tclass Iterator\n> -\t{\n> -\tpublic:\n> -\t\tusing difference_type = std::ptrdiff_t;\n> -\t\tusing iterator_category = std::forward_iterator_tag;\n> -\n> -\t\tIterator(typename ValueContainer::const_iterator it)\n> -\t\t\t: it_(it)\n> -\t\t{\n> -\t\t}\n> -\n> -\t\tDerived &operator++()\n> -\t\t{\n> -\t\t\t++it_;\n> -\t\t\treturn *static_cast<Derived *>(this);\n> -\t\t}\n> -\n> -\t\tDerived operator++(int)\n> -\t\t{\n> -\t\t\tDerived it = *static_cast<Derived *>(this);\n> -\t\t\tit_++;\n> -\t\t\treturn it;\n> -\t\t}\n> -\n> -\t\tfriend bool operator==(const Iterator &a, const Iterator &b)\n> -\t\t{\n> -\t\t\treturn a.it_ == b.it_;\n> -\t\t}\n> -\n> -\t\tfriend bool operator!=(const Iterator &a, const Iterator &b)\n> -\t\t{\n> -\t\t\treturn a.it_ != b.it_;\n> -\t\t}\n> -\n> -\tprotected:\n> -\t\tValueContainer::const_iterator it_;\n> -\t};\n> -\n> -\ttemplate<typename Iterator>\n> -\tclass Adapter\n> -\t{\n> -\tpublic:\n> -\t\tAdapter(const ValueContainer &container)\n> -\t\t\t: container_(container)\n> -\t\t{\n> -\t\t}\n> -\n> -\t\tIterator begin() const\n> -\t\t{\n> -\t\t\treturn Iterator{ container_.begin() };\n> -\t\t}\n> -\n> -\t\tIterator end() const\n> -\t\t{\n> -\t\t\treturn Iterator{ container_.end() };\n> -\t\t}\n> -\n> -\tprotected:\n> -\t\tconst ValueContainer &container_;\n> -\t};\n> -\n> -\tclass ListIterator : public Iterator<ListIterator>\n> -\t{\n> -\tpublic:\n> -\t\tusing value_type = const YamlObject &;\n> -\t\tusing pointer = const YamlObject *;\n> -\t\tusing reference = value_type;\n> -\n> -\t\tvalue_type operator*() const\n> -\t\t{\n> -\t\t\treturn *it_->value.get();\n> -\t\t}\n> -\n> -\t\tpointer operator->() const\n> -\t\t{\n> -\t\t\treturn it_->value.get();\n> -\t\t}\n> -\t};\n> -\n> -\tclass DictIterator : public Iterator<DictIterator>\n> -\t{\n> -\tpublic:\n> -\t\tusing value_type = std::pair<const std::string &, const YamlObject &>;\n> -\t\tusing pointer = value_type *;\n> -\t\tusing reference = value_type &;\n> -\n> -\t\tvalue_type operator*() const\n> -\t\t{\n> -\t\t\treturn { it_->key, *it_->value.get() };\n> -\t\t}\n> -\t};\n> -\n> -\tclass DictAdapter : public Adapter<DictIterator>\n> -\t{\n> -\tpublic:\n> -\t\tusing key_type = std::string;\n> -\t};\n> -\n> -\tclass ListAdapter : public Adapter<ListIterator>\n> -\t{\n> -\t};\n> -#endif /* __DOXYGEN__ */\n> -\n> -\tYamlObject();\n> -\t~YamlObject();\n> -\n> -\tbool isValue() const\n> -\t{\n> -\t\treturn type_ == Type::Value;\n> -\t}\n> -\tbool isList() const\n> -\t{\n> -\t\treturn type_ == Type::List;\n> -\t}\n> -\tbool isDictionary() const\n> -\t{\n> -\t\treturn type_ == Type::Dictionary;\n> -\t}\n> -\tbool isEmpty() const\n> -\t{\n> -\t\treturn type_ == Type::Empty;\n> -\t}\n> -\texplicit operator bool() const\n> -\t{\n> -\t\treturn type_ != Type::Empty;\n> -\t}\n> -\n> -\tstd::size_t size() const;\n> -\n> -\ttemplate<typename T>\n> -\tstd::optional<T> get() const\n> -\t{\n> -\t\treturn Accessor<T>{}.get(*this);\n> -\t}\n> -\n> -\ttemplate<typename T, typename U>\n> -\tT get(U &&defaultValue) const\n> -\t{\n> -\t\treturn get<T>().value_or(std::forward<U>(defaultValue));\n> -\t}\n> -\n> -\ttemplate<typename T>\n> -\tvoid set(T &&value)\n> -\t{\n> -\t\treturn Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));\n> -\t}\n> -\n> -\tDictAdapter asDict() const { return DictAdapter{ list_ }; }\n> -\tListAdapter asList() const { return ListAdapter{ list_ }; }\n> -\n> -\tconst YamlObject &operator[](std::size_t index) const;\n> -\n> -\tbool contains(std::string_view key) const;\n> -\tconst YamlObject &operator[](std::string_view key) const;\n> -\n> -\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> -\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> -\n> -private:\n> -\tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> -\n> -\ttemplate<typename T>\n> -\tfriend struct Accessor;\n> -\n> -\tenum class Type {\n> -\t\tDictionary,\n> -\t\tList,\n> -\t\tValue,\n> -\t\tEmpty,\n> -\t};\n> -\n> -\ttemplate<typename T, typename Enable = void>\n> -\tstruct Accessor {\n> -\t\tstd::optional<T> get(const YamlObject &obj) const;\n> -\t\tvoid set(YamlObject &obj, T value);\n> -\t};\n> -\n> -\tType type_;\n> -\n> -\tstd::string value_;\n> -\tValueContainer list_;\n> -\tstd::map<std::string, YamlObject *, std::less<>> dictionary_;\n> -};\n>   \n>   class YamlParser final\n>   {\n> diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\n> index 0df97b27332d..dfe1ccbe948b 100644\n> --- a/src/ipa/libipa/agc_mean_luminance.h\n> +++ b/src/ipa/libipa/agc_mean_luminance.h\n> @@ -16,7 +16,7 @@\n>   \n>   #include <libcamera/controls.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"exposure_mode_helper.h\"\n>   #include \"histogram.h\"\n> diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h\n> index f4a86038635f..3f25d13feaa8 100644\n> --- a/src/ipa/libipa/awb.h\n> +++ b/src/ipa/libipa/awb.h\n> @@ -14,7 +14,7 @@\n>   #include <libcamera/controls.h>\n>   \n>   #include \"libcamera/internal/vector.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h\n> index 47ef3cce4d58..79334ad3e7a3 100644\n> --- a/src/ipa/libipa/awb_bayes.h\n> +++ b/src/ipa/libipa/awb_bayes.h\n> @@ -10,7 +10,7 @@\n>   #include <libcamera/controls.h>\n>   \n>   #include \"libcamera/internal/vector.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"awb.h\"\n>   #include \"interpolator.h\"\n> diff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp\n> index f901a86e4c74..9355802f796a 100644\n> --- a/src/ipa/libipa/interpolator.cpp\n> +++ b/src/ipa/libipa/interpolator.cpp\n> @@ -11,8 +11,6 @@\n>   \n>   #include <libcamera/base/log.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> -\n>   #include \"interpolator.h\"\n>   \n>   /**\n> diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h\n> index 7880db6976d1..08003a06dbba 100644\n> --- a/src/ipa/libipa/interpolator.h\n> +++ b/src/ipa/libipa/interpolator.h\n> @@ -15,7 +15,7 @@\n>   \n>   #include <libcamera/base/log.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\n> index c71f215de3fc..19da46860849 100644\n> --- a/src/ipa/libipa/lsc_polynomial.h\n> +++ b/src/ipa/libipa/lsc_polynomial.h\n> @@ -16,7 +16,7 @@\n>   \n>   #include <libcamera/geometry.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\n> index 899e88248f04..d79b116a3339 100644\n> --- a/src/ipa/libipa/lux.cpp\n> +++ b/src/ipa/libipa/lux.cpp\n> @@ -12,7 +12,7 @@\n>   \n>   #include <libcamera/base/log.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"histogram.h\"\n>   \n> diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h\n> index c27af7718feb..8e6c658a6b63 100644\n> --- a/src/ipa/libipa/module.h\n> +++ b/src/ipa/libipa/module.h\n> @@ -15,7 +15,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/base/utils.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"algorithm.h\"\n>   \n> diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp\n> index d099219c3e43..3bdf19141e1d 100644\n> --- a/src/ipa/mali-c55/algorithms/blc.cpp\n> +++ b/src/ipa/mali-c55/algorithms/blc.cpp\n> @@ -10,7 +10,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/control_ids.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   /**\n>    * \\file blc.h\n> diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp\n> index f75b9cd7b7b8..e32f5a83485e 100644\n> --- a/src/ipa/mali-c55/algorithms/lsc.cpp\n> +++ b/src/ipa/mali-c55/algorithms/lsc.cpp\n> @@ -7,7 +7,7 @@\n>   \n>   #include \"lsc.h\"\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   namespace libcamera {\n>   \n> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\n> index 7cc06f91ac2b..ef16a3775056 100644\n> --- a/src/ipa/rkisp1/algorithms/agc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/agc.cpp\n> @@ -19,7 +19,7 @@\n>   #include <libcamera/control_ids.h>\n>   #include <libcamera/ipa/core_ipa_interface.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"libipa/histogram.h\"\n>   \n> diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp\n> index 32fc44ffff92..19c262fffa73 100644\n> --- a/src/ipa/rkisp1/algorithms/blc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/blc.cpp\n> @@ -13,7 +13,7 @@\n>   \n>   #include <libcamera/control_ids.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   /**\n>    * \\file blc.h\n> diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp\n> index de2b6fe775aa..3ed307280677 100644\n> --- a/src/ipa/rkisp1/algorithms/ccm.cpp\n> +++ b/src/ipa/rkisp1/algorithms/ccm.cpp\n> @@ -16,7 +16,7 @@\n>   \n>   #include <libcamera/ipa/core_ipa_interface.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"libipa/fixedpoint.h\"\n>   #include \"libipa/interpolator.h\"\n> diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp\n> index 7894628144f3..c195334750e1 100644\n> --- a/src/ipa/rkisp1/algorithms/dpcc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/dpcc.cpp\n> @@ -9,7 +9,7 @@\n>   \n>   #include <libcamera/base/log.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"linux/rkisp1-config.h\"\n>   \n> diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp\n> index a0e7030fe5db..6c07bd8c71e5 100644\n> --- a/src/ipa/rkisp1/algorithms/goc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/goc.cpp\n> @@ -13,7 +13,7 @@\n>   \n>   #include <libcamera/control_ids.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"linux/rkisp1-config.h\"\n>   \n> diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp\n> index 7ac5dc215850..292d0e80dc57 100644\n> --- a/src/ipa/rkisp1/algorithms/gsl.cpp\n> +++ b/src/ipa/rkisp1/algorithms/gsl.cpp\n> @@ -10,7 +10,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/base/utils.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"linux/rkisp1-config.h\"\n>   \n> diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\n> index 429565a0b51f..9c0ed44b3943 100644\n> --- a/src/ipa/rkisp1/algorithms/lsc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n> @@ -14,7 +14,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/base/utils.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"libipa/lsc_polynomial.h\"\n>   #include \"linux/rkisp1-config.h\"\n> diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp\n> index ed81628c032c..9e2688a05179 100644\n> --- a/src/ipa/rkisp1/algorithms/wdr.cpp\n> +++ b/src/ipa/rkisp1/algorithms/wdr.cpp\n> @@ -10,7 +10,7 @@\n>   #include <libcamera/base/log.h>\n>   #include <libcamera/base/utils.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include <libipa/agc_mean_luminance.h>\n>   #include <libipa/histogram.h>\n> diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h\n> index 1971bfdcc8ad..8839daa90916 100644\n> --- a/src/ipa/rpi/controller/algorithm.h\n> +++ b/src/ipa/rpi/controller/algorithm.h\n> @@ -15,7 +15,7 @@\n>   #include <memory>\n>   #include <map>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"controller.h\"\n>   \n> diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h\n> index fdb46557de9c..573942886bc0 100644\n> --- a/src/ipa/rpi/controller/controller.h\n> +++ b/src/ipa/rpi/controller/controller.h\n> @@ -16,7 +16,7 @@\n>   #include <string>\n>   \n>   #include <libcamera/base/utils.h>\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"camera_mode.h\"\n>   #include \"device_status.h\"\n> diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp\n> index 2e8e9e33ea78..621008844c74 100644\n> --- a/src/libcamera/geometry.cpp\n> +++ b/src/libcamera/geometry.cpp\n> @@ -12,7 +12,7 @@\n>   \n>   #include <libcamera/base/log.h>\n>   \n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   /**\n>    * \\file geometry.h\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 6f952bd9832a..da89aa3714c3 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -58,6 +58,7 @@ libcamera_internal_sources = files([\n>       'v4l2_subdevice.cpp',\n>       'v4l2_videodevice.cpp',\n>       'vector.cpp',\n> +    'yaml_object.cpp',\n>       'yaml_parser.cpp',\n>   ])\n>   \n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> index 7eb197ce435c..684438bd5fb7 100644\n> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n> @@ -21,6 +21,7 @@\n>   \n>   #include \"libcamera/internal/camera_lens.h\"\n>   #include \"libcamera/internal/v4l2_subdevice.h\"\n> +#include \"libcamera/internal/yaml_parser.h\"\n>   \n>   using namespace std::chrono_literals;\n>   \n> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> index 6257a93467df..c69a690f580c 100644\n> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n> @@ -26,7 +26,7 @@\n>   #include \"libcamera/internal/pipeline_handler.h\"\n>   #include \"libcamera/internal/request.h\"\n>   #include \"libcamera/internal/v4l2_videodevice.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include <libcamera/ipa/raspberrypi_ipa_interface.h>\n>   #include <libcamera/ipa/raspberrypi_ipa_proxy.h>\n> diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h\n> index d2000de9c12f..f696d8862897 100644\n> --- a/src/libcamera/pipeline/virtual/config_parser.h\n> +++ b/src/libcamera/pipeline/virtual/config_parser.h\n> @@ -13,7 +13,7 @@\n>   #include <libcamera/base/file.h>\n>   \n>   #include \"libcamera/internal/pipeline_handler.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"virtual.h\"\n>   \n> diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp\n> index 40c35264c568..b032db54ded4 100644\n> --- a/src/libcamera/pipeline/virtual/virtual.cpp\n> +++ b/src/libcamera/pipeline/virtual/virtual.cpp\n> @@ -36,7 +36,7 @@\n>   #include \"libcamera/internal/framebuffer.h\"\n>   #include \"libcamera/internal/pipeline_handler.h\"\n>   #include \"libcamera/internal/request.h\"\n> -#include \"libcamera/internal/yaml_parser.h\"\n> +#include \"libcamera/internal/yaml_object.h\"\n>   \n>   #include \"pipeline/virtual/config_parser.h\"\n>   \n> diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp\n> new file mode 100644\n> index 000000000000..5e92189fdb8e\n> --- /dev/null\n> +++ b/src/libcamera/yaml_object.cpp\n> @@ -0,0 +1,477 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2022, Google Inc.\n> + * Copyright (C) 2025, Ideas on Board.\n> + *\n> + * libcamera YAML object\n> + */\n> +\n> +#include \"libcamera/internal/yaml_object.h\"\n> +\n> +#include <charconv>\n> +#include <errno.h>\n> +#include <string>\n> +#include <vector>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +/**\n> + * \\file yaml_object.h\n> + * \\brief YAML objects\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace {\n> +\n> +/* Empty static YamlObject as a safe result for invalid operations */\n> +static const YamlObject empty;\n> +\n> +} /* namespace */\n> +\n> +/**\n> + * \\class YamlObject\n> + * \\brief A class representing the tree structure of the YAML content\n> + *\n> + * The YamlObject class represents the tree structure of YAML content. A\n> + * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a\n> + * tree leaf.\n> + */\n> +\n> +YamlObject::YamlObject()\n> +\t: type_(Type::Empty)\n> +{\n> +}\n> +\n> +YamlObject::~YamlObject() = default;\n> +\n> +/**\n> + * \\fn YamlObject::isValue()\n> + * \\brief Return whether the YamlObject is a value\n> + *\n> + * \\return True if the YamlObject is a value, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::isList()\n> + * \\brief Return whether the YamlObject is a list\n> + *\n> + * \\return True if the YamlObject is a list, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::isDictionary()\n> + * \\brief Return whether the YamlObject is a dictionary\n> + *\n> + * \\return True if the YamlObject is a dictionary, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::isEmpty()\n> + * \\brief Return whether the YamlObject is an empty\n> + *\n> + * \\return True if the YamlObject is empty, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::operator bool()\n> + * \\brief Return whether the YamlObject is a non-empty\n> + *\n> + * \\return False if the YamlObject is empty, true otherwise\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::size()\n> + * \\brief Retrieve the number of elements in a dictionary or list YamlObject\n> + *\n> + * This function retrieves the size of the YamlObject, defined as the number of\n> + * child elements it contains. Only YamlObject instances of Dictionary or List\n> + * types have a size, calling this function on other types of instances is\n> + * invalid and results in undefined behaviour.\n> + *\n> + * \\return The size of the YamlObject\n> + */\n> +std::size_t YamlObject::size() const\n> +{\n> +\tswitch (type_) {\n> +\tcase Type::Dictionary:\n> +\tcase Type::List:\n> +\t\treturn list_.size();\n> +\tdefault:\n> +\t\treturn 0;\n> +\t}\n> +}\n> +\n> +/**\n> + * \\fn template<typename T> YamlObject::get<T>() const\n> + * \\tparam T Type of the value\n> + * \\brief Parse the YamlObject as a \\a T value\n> + *\n> + * This function parses the value of the YamlObject as a \\a T object, and\n> + * returns the value. If parsing fails (usually because the YamlObject doesn't\n> + * store a \\a T value), std::nullopt is returned.\n> + *\n> + * If the type \\a T is an std::vector, the YamlObject will be parsed as a list\n> + * of values.\n> + *\n> + * \\return The YamlObject value, or std::nullopt if parsing failed\n> + */\n> +\n> +/**\n> + * \\fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const\n> + * \\brief Parse the YamlObject as a \\a T value\n> + * \\param[in] defaultValue The default value when failing to parse\n> + *\n> + * This function parses the value of the YamlObject as a \\a T object, and\n> + * returns the value. If parsing fails (usually because the YamlObject doesn't\n> + * store a \\a T value), the \\a defaultValue is returned.\n> + *\n> + * Unlike the get() function, this overload does not support std::vector for the\n> + * type \\a T.\n> + *\n> + * \\return The YamlObject value, or \\a defaultValue if parsing failed\n> + */\n> +\n> +/**\n> + * \\fn template<typename T> YamlObject::set<T>(T &&value)\n> + * \\brief Set the value of a YamlObject\n> + * \\param[in] value The value\n> + *\n> + * This function sets the value stored in a YamlObject to \\a value. The value is\n> + * converted to a string in an implementation-specific way that guarantees that\n> + * subsequent calls to get<T>() will return the same value.\n> + *\n> + * Values can only be set on YamlObject of Type::Value type or empty YamlObject.\n> + * Attempting to set a value on an object of type Type::Dict or Type::List does\n> + * not modify the YamlObject.\n> + */\n> +\n> +#ifndef __DOXYGEN__\n> +\n> +template<>\n> +std::optional<bool>\n> +YamlObject::Accessor<bool>::get(const YamlObject &obj) const\n> +{\n> +\tif (obj.type_ != Type::Value)\n> +\t\treturn std::nullopt;\n> +\n> +\tif (obj.value_ == \"true\")\n> +\t\treturn true;\n> +\telse if (obj.value_ == \"false\")\n> +\t\treturn false;\n> +\n> +\treturn std::nullopt;\n> +}\n> +\n> +template<>\n> +void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)\n> +{\n> +\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> +\t\treturn;\n> +\n> +\tobj.type_ = Type::Value;\n> +\tobj.value_ = value ? \"true\" : \"false\";\n> +}\n> +\n> +template<typename T>\n> +struct YamlObject::Accessor<T, std::enable_if_t<\n> +\tstd::is_same_v<int8_t, T> ||\n> +\tstd::is_same_v<uint8_t, T> ||\n> +\tstd::is_same_v<int16_t, T> ||\n> +\tstd::is_same_v<uint16_t, T> ||\n> +\tstd::is_same_v<int32_t, T> ||\n> +\tstd::is_same_v<uint32_t, T>>>\n> +{\n> +\tstd::optional<T> get(const YamlObject &obj) const\n> +\t{\n> +\t\tif (obj.type_ != Type::Value)\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\tconst std::string &str = obj.value_;\n> +\t\tT value;\n> +\n> +\t\tauto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),\n> +\t\t\t\t\t\t value);\n> +\t\tif (ptr != str.data() + str.size() || ec != std::errc())\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\treturn value;\n> +\t}\n> +\n> +\tvoid set(YamlObject &obj, T value)\n> +\t{\n> +\t\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> +\t\t\treturn;\n> +\n> +\t\tobj.type_ = Type::Value;\n> +\t\tobj.value_ = std::to_string(value);\n> +\t}\n> +};\n> +\n> +template struct YamlObject::Accessor<int8_t>;\n> +template struct YamlObject::Accessor<uint8_t>;\n> +template struct YamlObject::Accessor<int16_t>;\n> +template struct YamlObject::Accessor<uint16_t>;\n> +template struct YamlObject::Accessor<int32_t>;\n> +template struct YamlObject::Accessor<uint32_t>;\n> +\n> +template<>\n> +std::optional<float>\n> +YamlObject::Accessor<float>::get(const YamlObject &obj) const\n> +{\n> +\treturn obj.get<double>();\n> +}\n> +\n> +template<>\n> +void YamlObject::Accessor<float>::set(YamlObject &obj, float value)\n> +{\n> +\tobj.set<double>(std::forward<float>(value));\n> +}\n> +\n> +template<>\n> +std::optional<double>\n> +YamlObject::Accessor<double>::get(const YamlObject &obj) const\n> +{\n> +\tif (obj.type_ != Type::Value)\n> +\t\treturn std::nullopt;\n> +\n> +\tif (obj.value_.empty())\n> +\t\treturn std::nullopt;\n> +\n> +\tchar *end;\n> +\n> +\terrno = 0;\n> +\tdouble value = utils::strtod(obj.value_.c_str(), &end);\n> +\n> +\tif ('\\0' != *end || errno == ERANGE)\n> +\t\treturn std::nullopt;\n> +\n> +\treturn value;\n> +}\n> +\n> +template<>\n> +void YamlObject::Accessor<double>::set(YamlObject &obj, double value)\n> +{\n> +\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> +\t\treturn;\n> +\n> +\tobj.type_ = Type::Value;\n> +\tobj.value_ = std::to_string(value);\n> +}\n> +\n> +template<>\n> +std::optional<std::string>\n> +YamlObject::Accessor<std::string>::get(const YamlObject &obj) const\n> +{\n> +\tif (obj.type_ != Type::Value)\n> +\t\treturn std::nullopt;\n> +\n> +\treturn obj.value_;\n> +}\n> +\n> +template<>\n> +void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)\n> +{\n> +\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> +\t\treturn;\n> +\n> +\tobj.type_ = Type::Value;\n> +\tobj.value_ = std::move(value);\n> +}\n> +\n> +template<typename T>\n> +struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<\n> +\tstd::is_same_v<bool, T> ||\n> +\tstd::is_same_v<float, T> ||\n> +\tstd::is_same_v<double, T> ||\n> +\tstd::is_same_v<int8_t, T> ||\n> +\tstd::is_same_v<uint8_t, T> ||\n> +\tstd::is_same_v<int16_t, T> ||\n> +\tstd::is_same_v<uint16_t, T> ||\n> +\tstd::is_same_v<int32_t, T> ||\n> +\tstd::is_same_v<uint32_t, T> ||\n> +\tstd::is_same_v<std::string, T>>>\n> +{\n> +\tstd::optional<std::vector<T>> get(const YamlObject &obj) const\n> +\t{\n> +\t\tif (obj.type_ != Type::List)\n> +\t\t\treturn std::nullopt;\n> +\n> +\t\tstd::vector<T> values;\n> +\t\tvalues.reserve(obj.list_.size());\n> +\n> +\t\tfor (const YamlObject &entry : obj.asList()) {\n> +\t\t\tconst auto value = entry.get<T>();\n> +\t\t\tif (!value)\n> +\t\t\t\treturn std::nullopt;\n> +\t\t\tvalues.emplace_back(*value);\n> +\t\t}\n> +\n> +\t\treturn values;\n> +\t}\n> +};\n> +\n> +template struct YamlObject::Accessor<std::vector<bool>>;\n> +template struct YamlObject::Accessor<std::vector<float>>;\n> +template struct YamlObject::Accessor<std::vector<double>>;\n> +template struct YamlObject::Accessor<std::vector<int8_t>>;\n> +template struct YamlObject::Accessor<std::vector<uint8_t>>;\n> +template struct YamlObject::Accessor<std::vector<int16_t>>;\n> +template struct YamlObject::Accessor<std::vector<uint16_t>>;\n> +template struct YamlObject::Accessor<std::vector<int32_t>>;\n> +template struct YamlObject::Accessor<std::vector<uint32_t>>;\n> +template struct YamlObject::Accessor<std::vector<std::string>>;\n> +#endif /* __DOXYGEN__ */\n> +\n> +/**\n> + * \\fn YamlObject::asDict() const\n> + * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n> + *\n> + * The YamlObject class doesn't directly implement iterators, as the iterator\n> + * type depends on whether the object is a Dictionary or List. This function\n> + * wraps a YamlObject of Dictionary type into an adapter that exposes\n> + * iterators, as well as begin() and end() functions, allowing usage of\n> + * range-based for loops with YamlObject. As YAML mappings are not ordered, the\n> + * iteration order is not specified.\n> + *\n> + * The iterator's value_type is a\n> + * <em>std::pair<const std::string &, const \\ref YamlObject &></em>.\n> + *\n> + * If the YamlObject is not of Dictionary type, the returned adapter operates\n> + * as an empty container.\n> + *\n> + * \\return An adapter of unspecified type compatible with range-based for loops\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::asList() const\n> + * \\brief Wrap a list YamlObject in an adapter that exposes iterators\n> + *\n> + * The YamlObject class doesn't directly implement iterators, as the iterator\n> + * type depends on whether the object is a Dictionary or List. This function\n> + * wraps a YamlObject of List type into an adapter that exposes iterators, as\n> + * well as begin() and end() functions, allowing usage of range-based for loops\n> + * with YamlObject. As YAML lists are ordered, the iteration order is identical\n> + * to the list order in the YAML data.\n> + *\n> + * The iterator's value_type is a <em>const YamlObject &</em>.\n> + *\n> + * If the YamlObject is not of List type, the returned adapter operates as an\n> + * empty container.\n> + *\n> + * \\return An adapter of unspecified type compatible with range-based for loops\n> + */\n> +\n> +/**\n> + * \\fn YamlObject::operator[](std::size_t index) const\n> + * \\brief Retrieve the element from list YamlObject by index\n> + *\n> + * This function retrieves an element of the YamlObject. Only YamlObject\n> + * instances of List type associate elements with index, calling this function\n> + * on other types of instances or with an invalid index results in an empty\n> + * object.\n> + *\n> + * \\return The YamlObject as an element of the list\n> + */\n> +const YamlObject &YamlObject::operator[](std::size_t index) const\n> +{\n> +\tif (type_ != Type::List || index >= size())\n> +\t\treturn empty;\n> +\n> +\treturn *list_[index].value;\n> +}\n> +\n> +/**\n> + * \\fn YamlObject::contains()\n> + * \\brief Check if an element of a dictionary exists\n> + *\n> + * This function check if the YamlObject contains an element. Only YamlObject\n> + * instances of Dictionary type associate elements with names, calling this\n> + * function on other types of instances is invalid and results in undefined\n> + * behaviour.\n> + *\n> + * \\return True if an element exists, false otherwise\n> + */\n> +bool YamlObject::contains(std::string_view key) const\n> +{\n> +\treturn dictionary_.find(key) != dictionary_.end();\n> +}\n> +\n> +/**\n> + * \\fn YamlObject::operator[](std::string_view key) const\n> + * \\brief Retrieve a member by name from the dictionary\n> + *\n> + * This function retrieve a member of a YamlObject by name. Only YamlObject\n> + * instances of Dictionary type associate elements with names, calling this\n> + * function on other types of instances or with a nonexistent key results in an\n> + * empty object.\n> + *\n> + * \\return The YamlObject corresponding to the \\a key member\n> + */\n> +const YamlObject &YamlObject::operator[](std::string_view key) const\n> +{\n> +\tif (type_ != Type::Dictionary)\n> +\t\treturn empty;\n> +\n> +\tauto iter = dictionary_.find(key);\n> +\tif (iter == dictionary_.end())\n> +\t\treturn empty;\n> +\n> +\treturn *iter->second;\n> +}\n> +\n> +/**\n> + * \\brief Add a child object to a list\n> + * \\param[in] child The child object\n> + *\n> + * Append the \\a child node as the last element of this node's children list.\n> + * This node must be empty, in which case it is converted to the Type::List\n> + * type, or be a list. Otherwise, the \\a child is discarded and the function\n> + * returns a nullptr.\n> + *\n> + * \\return The child object if successfully added, nullptr otherwise\n> + */\n> +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> +{\n> +\tif (type_ == Type::Empty)\n> +\t\ttype_ = Type::List;\n> +\n> +\tif (type_ != Type::List)\n> +\t\treturn nullptr;\n> +\n> +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> +\treturn elem.value.get();\n> +}\n> +\n> +/**\n> + * \\brief Add a child object to a dictionary\n> + * \\param[in] key The dictionary key\n> + * \\param[in] child The child object\n> + *\n> + * Add the \\a child node with the given \\a key to this node's children. This\n> + * node must be empty, in which case it is converted to the Type::Dictionary\n> + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> + * function returns a nullptr.\n> + *\n> + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> + * is discarded and the function returns a nullptr.\n> + *\n> + * \\return The child object if successfully added, nullptr otherwise\n> + */\n> +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> +{\n> +\tif (type_ == Type::Empty)\n> +\t\ttype_ = Type::Dictionary;\n> +\n> +\tif (type_ != Type::Dictionary)\n> +\t\treturn nullptr;\n> +\n> +\tif (dictionary_.find(key) != dictionary_.end())\n> +\t\treturn nullptr;\n> +\n> +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> +\tdictionary_.emplace(elem.key, elem.value.get());\n> +\treturn elem.value.get();\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> index 0ef1f3ea303b..d83df0fb9597 100644\n> --- a/src/libcamera/yaml_parser.cpp\n> +++ b/src/libcamera/yaml_parser.cpp\n> @@ -7,11 +7,10 @@\n>   \n>   #include \"libcamera/internal/yaml_parser.h\"\n>   \n> -#include <charconv>\n>   #include <errno.h>\n>   #include <functional>\n> -#include <limits>\n> -#include <stdlib.h>\n> +#include <memory>\n> +#include <string>\n>   \n>   #include <libcamera/base/file.h>\n>   #include <libcamera/base/log.h>\n> @@ -27,459 +26,6 @@ namespace libcamera {\n>   \n>   LOG_DEFINE_CATEGORY(YamlParser)\n>   \n> -namespace {\n> -\n> -/* Empty static YamlObject as a safe result for invalid operations */\n> -static const YamlObject empty;\n> -\n> -} /* namespace */\n> -\n> -/**\n> - * \\class YamlObject\n> - * \\brief A class representing the tree structure of the YAML content\n> - *\n> - * The YamlObject class represents the tree structure of YAML content. A\n> - * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a\n> - * tree leaf.\n> - */\n> -\n> -YamlObject::YamlObject()\n> -\t: type_(Type::Empty)\n> -{\n> -}\n> -\n> -YamlObject::~YamlObject() = default;\n> -\n> -/**\n> - * \\fn YamlObject::isValue()\n> - * \\brief Return whether the YamlObject is a value\n> - *\n> - * \\return True if the YamlObject is a value, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::isList()\n> - * \\brief Return whether the YamlObject is a list\n> - *\n> - * \\return True if the YamlObject is a list, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::isDictionary()\n> - * \\brief Return whether the YamlObject is a dictionary\n> - *\n> - * \\return True if the YamlObject is a dictionary, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::isEmpty()\n> - * \\brief Return whether the YamlObject is an empty\n> - *\n> - * \\return True if the YamlObject is empty, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::operator bool()\n> - * \\brief Return whether the YamlObject is a non-empty\n> - *\n> - * \\return False if the YamlObject is empty, true otherwise\n> - */\n> -\n> -/**\n> - * \\brief Retrieve the number of elements in a dictionary or list YamlObject\n> - *\n> - * This function retrieves the size of the YamlObject, defined as the number of\n> - * child elements it contains. Only YamlObject instances of Dictionary or List\n> - * types have a size, calling this function on other types of instances is\n> - * invalid and results in undefined behaviour.\n> - *\n> - * \\return The size of the YamlObject\n> - */\n> -std::size_t YamlObject::size() const\n> -{\n> -\tswitch (type_) {\n> -\tcase Type::Dictionary:\n> -\tcase Type::List:\n> -\t\treturn list_.size();\n> -\tdefault:\n> -\t\treturn 0;\n> -\t}\n> -}\n> -\n> -/**\n> - * \\fn template<typename T> YamlObject::get<T>() const\n> - * \\tparam T Type of the value\n> - * \\brief Parse the YamlObject as a \\a T value\n> - *\n> - * This function parses the value of the YamlObject as a \\a T object, and\n> - * returns the value. If parsing fails (usually because the YamlObject doesn't\n> - * store a \\a T value), std::nullopt is returned.\n> - *\n> - * If the type \\a T is an std::vector, the YamlObject will be parsed as a list\n> - * of values.\n> - *\n> - * \\return The YamlObject value, or std::nullopt if parsing failed\n> - */\n> -\n> -/**\n> - * \\fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const\n> - * \\brief Parse the YamlObject as a \\a T value\n> - * \\param[in] defaultValue The default value when failing to parse\n> - *\n> - * This function parses the value of the YamlObject as a \\a T object, and\n> - * returns the value. If parsing fails (usually because the YamlObject doesn't\n> - * store a \\a T value), the \\a defaultValue is returned.\n> - *\n> - * Unlike the get() function, this overload does not support std::vector for the\n> - * type \\a T.\n> - *\n> - * \\return The YamlObject value, or \\a defaultValue if parsing failed\n> - */\n> -\n> -/**\n> - * \\fn template<typename T> YamlObject::set<T>(T &&value)\n> - * \\brief Set the value of a YamlObject\n> - * \\param[in] value The value\n> - *\n> - * This function sets the value stored in a YamlObject to \\a value. The value is\n> - * converted to a string in an implementation-specific way that guarantees that\n> - * subsequent calls to get<T>() will return the same value.\n> - *\n> - * Values can only be set on YamlObject of Type::Value type or empty YamlObject.\n> - * Attempting to set a value on an object of type Type::Dict or Type::List does\n> - * not modify the YamlObject.\n> - */\n> -\n> -#ifndef __DOXYGEN__\n> -\n> -template<>\n> -std::optional<bool>\n> -YamlObject::Accessor<bool>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tif (obj.value_ == \"true\")\n> -\t\treturn true;\n> -\telse if (obj.value_ == \"false\")\n> -\t\treturn false;\n> -\n> -\treturn std::nullopt;\n> -}\n> -\n> -template<>\n> -void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)\n> -{\n> -\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> -\t\treturn;\n> -\n> -\tobj.type_ = Type::Value;\n> -\tobj.value_ = value ? \"true\" : \"false\";\n> -}\n> -\n> -template<typename T>\n> -struct YamlObject::Accessor<T, std::enable_if_t<\n> -\tstd::is_same_v<int8_t, T> ||\n> -\tstd::is_same_v<uint8_t, T> ||\n> -\tstd::is_same_v<int16_t, T> ||\n> -\tstd::is_same_v<uint16_t, T> ||\n> -\tstd::is_same_v<int32_t, T> ||\n> -\tstd::is_same_v<uint32_t, T>>>\n> -{\n> -\tstd::optional<T> get(const YamlObject &obj) const\n> -\t{\n> -\t\tif (obj.type_ != Type::Value)\n> -\t\t\treturn std::nullopt;\n> -\n> -\t\tconst std::string &str = obj.value_;\n> -\t\tT value;\n> -\n> -\t\tauto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),\n> -\t\t\t\t\t\t value);\n> -\t\tif (ptr != str.data() + str.size() || ec != std::errc())\n> -\t\t\treturn std::nullopt;\n> -\n> -\t\treturn value;\n> -\t}\n> -\n> -\tvoid set(YamlObject &obj, T value)\n> -\t{\n> -\t\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> -\t\t\treturn;\n> -\n> -\t\tobj.type_ = Type::Value;\n> -\t\tobj.value_ = std::to_string(value);\n> -\t}\n> -};\n> -\n> -template struct YamlObject::Accessor<int8_t>;\n> -template struct YamlObject::Accessor<uint8_t>;\n> -template struct YamlObject::Accessor<int16_t>;\n> -template struct YamlObject::Accessor<uint16_t>;\n> -template struct YamlObject::Accessor<int32_t>;\n> -template struct YamlObject::Accessor<uint32_t>;\n> -\n> -template<>\n> -std::optional<float>\n> -YamlObject::Accessor<float>::get(const YamlObject &obj) const\n> -{\n> -\treturn obj.get<double>();\n> -}\n> -\n> -template<>\n> -void YamlObject::Accessor<float>::set(YamlObject &obj, float value)\n> -{\n> -\tobj.set<double>(std::forward<float>(value));\n> -}\n> -\n> -template<>\n> -std::optional<double>\n> -YamlObject::Accessor<double>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tif (obj.value_.empty())\n> -\t\treturn std::nullopt;\n> -\n> -\tchar *end;\n> -\n> -\terrno = 0;\n> -\tdouble value = utils::strtod(obj.value_.c_str(), &end);\n> -\n> -\tif ('\\0' != *end || errno == ERANGE)\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -void YamlObject::Accessor<double>::set(YamlObject &obj, double value)\n> -{\n> -\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> -\t\treturn;\n> -\n> -\tobj.type_ = Type::Value;\n> -\tobj.value_ = std::to_string(value);\n> -}\n> -\n> -template<>\n> -std::optional<std::string>\n> -YamlObject::Accessor<std::string>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\treturn obj.value_;\n> -}\n> -\n> -template<>\n> -void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)\n> -{\n> -\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n> -\t\treturn;\n> -\n> -\tobj.type_ = Type::Value;\n> -\tobj.value_ = std::move(value);\n> -}\n> -\n> -template<typename T>\n> -struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<\n> -\tstd::is_same_v<bool, T> ||\n> -\tstd::is_same_v<float, T> ||\n> -\tstd::is_same_v<double, T> ||\n> -\tstd::is_same_v<int8_t, T> ||\n> -\tstd::is_same_v<uint8_t, T> ||\n> -\tstd::is_same_v<int16_t, T> ||\n> -\tstd::is_same_v<uint16_t, T> ||\n> -\tstd::is_same_v<int32_t, T> ||\n> -\tstd::is_same_v<uint32_t, T> ||\n> -\tstd::is_same_v<std::string, T>>>\n> -{\n> -\tstd::optional<std::vector<T>> get(const YamlObject &obj) const\n> -\t{\n> -\t\tif (obj.type_ != Type::List)\n> -\t\t\treturn std::nullopt;\n> -\n> -\t\tstd::vector<T> values;\n> -\t\tvalues.reserve(obj.list_.size());\n> -\n> -\t\tfor (const YamlObject &entry : obj.asList()) {\n> -\t\t\tconst auto value = entry.get<T>();\n> -\t\t\tif (!value)\n> -\t\t\t\treturn std::nullopt;\n> -\t\t\tvalues.emplace_back(*value);\n> -\t\t}\n> -\n> -\t\treturn values;\n> -\t}\n> -};\n> -\n> -template struct YamlObject::Accessor<std::vector<bool>>;\n> -template struct YamlObject::Accessor<std::vector<float>>;\n> -template struct YamlObject::Accessor<std::vector<double>>;\n> -template struct YamlObject::Accessor<std::vector<int8_t>>;\n> -template struct YamlObject::Accessor<std::vector<uint8_t>>;\n> -template struct YamlObject::Accessor<std::vector<int16_t>>;\n> -template struct YamlObject::Accessor<std::vector<uint16_t>>;\n> -template struct YamlObject::Accessor<std::vector<int32_t>>;\n> -template struct YamlObject::Accessor<std::vector<uint32_t>>;\n> -template struct YamlObject::Accessor<std::vector<std::string>>;\n> -#endif /* __DOXYGEN__ */\n> -\n> -/**\n> - * \\fn YamlObject::asDict() const\n> - * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n> - *\n> - * The YamlObject class doesn't directly implement iterators, as the iterator\n> - * type depends on whether the object is a Dictionary or List. This function\n> - * wraps a YamlObject of Dictionary type into an adapter that exposes\n> - * iterators, as well as begin() and end() functions, allowing usage of\n> - * range-based for loops with YamlObject. As YAML mappings are not ordered, the\n> - * iteration order is not specified.\n> - *\n> - * The iterator's value_type is a\n> - * <em>std::pair<const std::string &, const \\ref YamlObject &></em>.\n> - *\n> - * If the YamlObject is not of Dictionary type, the returned adapter operates\n> - * as an empty container.\n> - *\n> - * \\return An adapter of unspecified type compatible with range-based for loops\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::asList() const\n> - * \\brief Wrap a list YamlObject in an adapter that exposes iterators\n> - *\n> - * The YamlObject class doesn't directly implement iterators, as the iterator\n> - * type depends on whether the object is a Dictionary or List. This function\n> - * wraps a YamlObject of List type into an adapter that exposes iterators, as\n> - * well as begin() and end() functions, allowing usage of range-based for loops\n> - * with YamlObject. As YAML lists are ordered, the iteration order is identical\n> - * to the list order in the YAML data.\n> - *\n> - * The iterator's value_type is a <em>const YamlObject &</em>.\n> - *\n> - * If the YamlObject is not of List type, the returned adapter operates as an\n> - * empty container.\n> - *\n> - * \\return An adapter of unspecified type compatible with range-based for loops\n> - */\n> -\n> -/**\n> - * \\fn YamlObject::operator[](std::size_t index) const\n> - * \\brief Retrieve the element from list YamlObject by index\n> - *\n> - * This function retrieves an element of the YamlObject. Only YamlObject\n> - * instances of List type associate elements with index, calling this function\n> - * on other types of instances or with an invalid index results in an empty\n> - * object.\n> - *\n> - * \\return The YamlObject as an element of the list\n> - */\n> -const YamlObject &YamlObject::operator[](std::size_t index) const\n> -{\n> -\tif (type_ != Type::List || index >= size())\n> -\t\treturn empty;\n> -\n> -\treturn *list_[index].value;\n> -}\n> -\n> -/**\n> - * \\fn YamlObject::contains()\n> - * \\brief Check if an element of a dictionary exists\n> - *\n> - * This function check if the YamlObject contains an element. Only YamlObject\n> - * instances of Dictionary type associate elements with names, calling this\n> - * function on other types of instances is invalid and results in undefined\n> - * behaviour.\n> - *\n> - * \\return True if an element exists, false otherwise\n> - */\n> -bool YamlObject::contains(std::string_view key) const\n> -{\n> -\treturn dictionary_.find(key) != dictionary_.end();\n> -}\n> -\n> -/**\n> - * \\fn YamlObject::operator[](std::string_view key) const\n> - * \\brief Retrieve a member by name from the dictionary\n> - *\n> - * This function retrieve a member of a YamlObject by name. Only YamlObject\n> - * instances of Dictionary type associate elements with names, calling this\n> - * function on other types of instances or with a nonexistent key results in an\n> - * empty object.\n> - *\n> - * \\return The YamlObject corresponding to the \\a key member\n> - */\n> -const YamlObject &YamlObject::operator[](std::string_view key) const\n> -{\n> -\tif (type_ != Type::Dictionary)\n> -\t\treturn empty;\n> -\n> -\tauto iter = dictionary_.find(key);\n> -\tif (iter == dictionary_.end())\n> -\t\treturn empty;\n> -\n> -\treturn *iter->second;\n> -}\n> -\n> -/**\n> - * \\brief Add a child object to a list\n> - * \\param[in] child The child object\n> - *\n> - * Append the \\a child node as the last element of this node's children list.\n> - * This node must be empty, in which case it is converted to the Type::List\n> - * type, or be a list. Otherwise, the \\a child is discarded and the function\n> - * returns a nullptr.\n> - *\n> - * \\return A pointer to the child object if successfully added, nullptr\n> - * otherwise\n> - */\n> -YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> -{\n> -\tif (type_ == Type::Empty)\n> -\t\ttype_ = Type::List;\n> -\n> -\tif (type_ != Type::List)\n> -\t\treturn nullptr;\n> -\n> -\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> -\treturn elem.value.get();\n> -}\n> -\n> -/**\n> - * \\brief Add a child object to a dictionary\n> - * \\param[in] key The dictionary key\n> - * \\param[in] child The child object\n> - *\n> - * Add the \\a child node with the given \\a key to this node's children. This\n> - * node must be empty, in which case it is converted to the Type::Dictionary\n> - * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> - * function returns a nullptr.\n> - *\n> - * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> - * is discarded and the function returns a nullptr.\n> - *\n> - * \\return A pointer to the child object if successfully added, nullptr\n> - * otherwise\n> - */\n> -YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> -{\n> -\tif (type_ == Type::Empty)\n> -\t\ttype_ = Type::Dictionary;\n> -\n> -\tif (type_ != Type::Dictionary)\n> -\t\treturn nullptr;\n> -\n> -\tif (dictionary_.find(key) != dictionary_.end())\n> -\t\treturn nullptr;\n> -\n> -\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> -\tdictionary_.emplace(elem.key, elem.value.get());\n> -\treturn elem.value.get();\n> -}\n> -\n>   #ifndef __DOXYGEN__\n>   \n>   class YamlParserContext","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 9422FC3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 14 Jan 2026 11:18:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8C15561FC3;\n\tWed, 14 Jan 2026 12:18:07 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2166061F9F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 14 Jan 2026 12:18:06 +0100 (CET)","from [192.168.33.18] (185.221.143.114.nat.pool.zt.hu\n\t[185.221.143.114])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BF89C24A;\n\tWed, 14 Jan 2026 12:17:37 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"nGF8tBMm\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768389459;\n\tbh=+d99/NvVG8zTCJ/dSOErps75RU5J6cVd/6wx5IAbJZM=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=nGF8tBMmVxalfh11WQbEezUANIHzMTb279JpKoVWs3igVig08VihQGOs88N0V9OpZ\n\t0nRRbAu+gTD62iifbbuHuscuuddofISK575mpsNlqKWXn55YThTKxjbJqr7yocQQW/\n\tbzWBg6NUYsMHioFxIN//FN8y2zIUIhd9OUshRUHs=","Message-ID":"<dca5917c-45cb-453d-9355-ffcce276aecd@ideasonboard.com>","Date":"Wed, 14 Jan 2026 12:17:59 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 17/36] libcamera: yaml_parser: Split YamlObject from\n\tYamlParser","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<20260113000808.15395-18-laurent.pinchart@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260113000808.15395-18-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]