libcamera: yaml_parser: Split YamlObject from YamlParser

The YamlObject class was designed to represent data parsed from YAML
files. It is outgrowing the initial needs. Move it to a separate file to
prepare for a rename.

Most files that include yaml_parser.h only need access to a YamlObject.
Switch them to yaml_object.h. pipeline_base.cpp was including
yaml_parser.h indirectly through pipeline_base.h, include it directly
now.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>
This commit is contained in:
Laurent Pinchart
2026-04-24 18:08:16 +03:00
parent 31a817cda2
commit 7133680dd7
33 changed files with 729 additions and 683 deletions
@@ -14,7 +14,7 @@
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1 -1
View File
@@ -14,7 +14,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/span.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1
View File
@@ -49,6 +49,7 @@ libcamera_internal_headers = files([
'v4l2_subdevice.h',
'v4l2_videodevice.h',
'vector.h',
'yaml_object.h',
'yaml_parser.h',
])
+1 -1
View File
@@ -19,7 +19,7 @@
#include <libcamera/base/span.h>
#include "libcamera/internal/matrix.h"
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+227
View File
@@ -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 <iterator>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
#include <libcamera/base/class.h>
namespace libcamera {
class YamlObject
{
private:
struct Value {
Value(std::string k, std::unique_ptr<YamlObject> &&v)
: key(std::move(k)), value(std::move(v))
{
}
std::string key;
std::unique_ptr<YamlObject> value;
};
using ValueContainer = std::vector<Value>;
public:
#ifndef __DOXYGEN__
template<typename Derived>
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<Derived *>(this);
}
Derived operator++(int)
{
Derived it = *static_cast<Derived *>(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<typename Iterator>
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<ListIterator>
{
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<DictIterator>
{
public:
using value_type = std::pair<const std::string &, const YamlObject &>;
using pointer = value_type *;
using reference = value_type &;
value_type operator*() const
{
return { it_->key, *it_->value.get() };
}
};
class DictAdapter : public Adapter<DictIterator>
{
public:
using key_type = std::string;
};
class ListAdapter : public Adapter<ListIterator>
{
};
#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<typename T>
std::optional<T> get() const
{
return Accessor<T>{}.get(*this);
}
template<typename T, typename U>
T get(U &&defaultValue) const
{
return get<T>().value_or(std::forward<U>(defaultValue));
}
template<typename T>
void set(T &&value)
{
return Accessor<std::remove_cv_t<std::remove_reference_t<T>>>{}
.set(*this, std::forward<T>(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<YamlObject> &&child);
YamlObject *add(std::string key, std::unique_ptr<YamlObject> &&child);
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
template<typename T>
friend struct Accessor;
enum class Type {
Dictionary,
List,
Value,
Empty,
};
template<typename T, typename Enable = void>
struct Accessor {
std::optional<T> get(const YamlObject &obj) const;
void set(YamlObject &obj, T value);
};
Type type_;
std::string value_;
ValueContainer list_;
std::map<std::string, YamlObject *, std::less<>> dictionary_;
};
} /* namespace libcamera */
+2 -211
View File
@@ -7,222 +7,13 @@
#pragma once
#include <iterator>
#include <map>
#include <optional>
#include <stdint.h>
#include <string>
#include <string_view>
#include <vector>
#include <memory>
#include <libcamera/base/class.h>
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
class File;
class YamlParserContext;
class YamlObject
{
private:
struct Value {
Value(std::string k, std::unique_ptr<YamlObject> &&v)
: key(std::move(k)), value(std::move(v))
{
}
std::string key;
std::unique_ptr<YamlObject> value;
};
using ValueContainer = std::vector<Value>;
public:
#ifndef __DOXYGEN__
template<typename Derived>
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<Derived *>(this);
}
Derived operator++(int)
{
Derived it = *static_cast<Derived *>(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<typename Iterator>
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<ListIterator>
{
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<DictIterator>
{
public:
using value_type = std::pair<const std::string &, const YamlObject &>;
using pointer = value_type *;
using reference = value_type &;
value_type operator*() const
{
return { it_->key, *it_->value.get() };
}
};
class DictAdapter : public Adapter<DictIterator>
{
public:
using key_type = std::string;
};
class ListAdapter : public Adapter<ListIterator>
{
};
#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<typename T>
std::optional<T> get() const
{
return Accessor<T>{}.get(*this);
}
template<typename T, typename U>
T get(U &&defaultValue) const
{
return get<T>().value_or(std::forward<U>(defaultValue));
}
template<typename T>
void set(T &&value)
{
return Accessor<std::remove_cv_t<std::remove_reference_t<T>>>{}
.set(*this, std::forward<T>(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<YamlObject> &&child);
YamlObject *add(std::string key, std::unique_ptr<YamlObject> &&child);
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
template<typename T>
friend struct Accessor;
enum class Type {
Dictionary,
List,
Value,
Empty,
};
template<typename T, typename Enable = void>
struct Accessor {
std::optional<T> get(const YamlObject &obj) const;
void set(YamlObject &obj, T value);
};
Type type_;
std::string value_;
ValueContainer list_;
std::map<std::string, YamlObject *, std::less<>> dictionary_;
};
class YamlParser final
{
+1 -1
View File
@@ -16,7 +16,7 @@
#include <libcamera/controls.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "exposure_mode_helper.h"
#include "histogram.h"
+1 -1
View File
@@ -14,7 +14,7 @@
#include <libcamera/controls.h>
#include "libcamera/internal/vector.h"
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <libcamera/controls.h>
#include "libcamera/internal/vector.h"
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "awb.h"
#include "interpolator.h"
+1 -1
View File
@@ -15,7 +15,7 @@
#include <libcamera/base/log.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1 -1
View File
@@ -16,7 +16,7 @@
#include <libcamera/geometry.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1 -1
View File
@@ -12,7 +12,7 @@
#include <libcamera/base/log.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "histogram.h"
+1 -1
View File
@@ -15,7 +15,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "algorithm.h"
+1 -1
View File
@@ -10,7 +10,7 @@
#include <libcamera/base/log.h>
#include <libcamera/control_ids.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
/**
* \file blc.h
+1 -1
View File
@@ -7,7 +7,7 @@
#include "lsc.h"
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
namespace libcamera {
+1 -1
View File
@@ -19,7 +19,7 @@
#include <libcamera/control_ids.h>
#include <libcamera/ipa/core_ipa_interface.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "libipa/histogram.h"
+1 -1
View File
@@ -13,7 +13,7 @@
#include <libcamera/control_ids.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
/**
* \file blc.h
+1 -1
View File
@@ -16,7 +16,7 @@
#include <libcamera/ipa/core_ipa_interface.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "libipa/fixedpoint.h"
#include "libipa/interpolator.h"
+1 -1
View File
@@ -9,7 +9,7 @@
#include <libcamera/base/log.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "linux/rkisp1-config.h"
+1 -1
View File
@@ -13,7 +13,7 @@
#include <libcamera/control_ids.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "linux/rkisp1-config.h"
+1 -1
View File
@@ -10,7 +10,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "linux/rkisp1-config.h"
+1 -1
View File
@@ -14,7 +14,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "libipa/lsc_polynomial.h"
#include "linux/rkisp1-config.h"
+1 -1
View File
@@ -10,7 +10,7 @@
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include <libipa/agc_mean_luminance.h>
#include <libipa/histogram.h>
+1 -1
View File
@@ -15,7 +15,7 @@
#include <memory>
#include <map>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "controller.h"
+1 -1
View File
@@ -16,7 +16,7 @@
#include <string>
#include <libcamera/base/utils.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "camera_mode.h"
#include "device_status.h"
+1 -1
View File
@@ -12,7 +12,7 @@
#include <libcamera/base/log.h>
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
/**
* \file geometry.h
+1
View File
@@ -58,6 +58,7 @@ libcamera_internal_sources = files([
'v4l2_subdevice.cpp',
'v4l2_videodevice.cpp',
'vector.cpp',
'yaml_object.cpp',
'yaml_parser.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;
@@ -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 <libcamera/ipa/raspberrypi_ipa_interface.h>
#include <libcamera/ipa/raspberrypi_ipa_proxy.h>
@@ -13,7 +13,7 @@
#include <libcamera/base/file.h>
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/yaml_parser.h"
#include "libcamera/internal/yaml_object.h"
#include "virtual.h"
+1 -1
View File
@@ -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"
+469
View File
@@ -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 <charconv>
#include <errno.h>
#include <string>
#include <vector>
#include <libcamera/base/utils.h>
/**
* \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<typename T> YamlObject::get<T>() 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<typename T, typename U> YamlObject::get<T>(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<typename T> YamlObject::set<T>(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<T>() 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<bool>
YamlObject::Accessor<bool>::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<bool>::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<typename T>
struct YamlObject::Accessor<T, std::enable_if_t<
std::is_same_v<int8_t, T> ||
std::is_same_v<uint8_t, T> ||
std::is_same_v<int16_t, T> ||
std::is_same_v<uint16_t, T> ||
std::is_same_v<int32_t, T> ||
std::is_same_v<uint32_t, T>>>
{
std::optional<T> 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<int8_t>;
template struct YamlObject::Accessor<uint8_t>;
template struct YamlObject::Accessor<int16_t>;
template struct YamlObject::Accessor<uint16_t>;
template struct YamlObject::Accessor<int32_t>;
template struct YamlObject::Accessor<uint32_t>;
template<>
std::optional<float>
YamlObject::Accessor<float>::get(const YamlObject &obj) const
{
return obj.get<double>();
}
template<>
void YamlObject::Accessor<float>::set(YamlObject &obj, float value)
{
obj.set<double>(value);
}
template<>
std::optional<double>
YamlObject::Accessor<double>::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<double>::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<std::string>
YamlObject::Accessor<std::string>::get(const YamlObject &obj) const
{
if (obj.type_ != Type::Value)
return std::nullopt;
return obj.value_;
}
template<>
void YamlObject::Accessor<std::string>::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<typename T>
struct YamlObject::Accessor<std::vector<T>> {
std::optional<std::vector<T>> get(const YamlObject &obj) const
{
if (obj.type_ != Type::List)
return std::nullopt;
std::vector<T> values;
values.reserve(obj.list_.size());
for (const YamlObject &entry : obj.asList()) {
auto value = entry.get<T>();
if (!value)
return std::nullopt;
values.emplace_back(std::move(*value));
}
return values;
}
};
template struct YamlObject::Accessor<std::vector<bool>>;
template struct YamlObject::Accessor<std::vector<float>>;
template struct YamlObject::Accessor<std::vector<double>>;
template struct YamlObject::Accessor<std::vector<int8_t>>;
template struct YamlObject::Accessor<std::vector<uint8_t>>;
template struct YamlObject::Accessor<std::vector<int16_t>>;
template struct YamlObject::Accessor<std::vector<uint16_t>>;
template struct YamlObject::Accessor<std::vector<int32_t>>;
template struct YamlObject::Accessor<std::vector<uint32_t>>;
template struct YamlObject::Accessor<std::vector<std::string>>;
#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
* <em>std::pair<const std::string &, const \ref YamlObject &></em>.
*
* 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 <em>const YamlObject &</em>.
*
* 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<YamlObject> &&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<YamlObject> &&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 */
+2 -446
View File
@@ -7,11 +7,10 @@
#include "libcamera/internal/yaml_parser.h"
#include <charconv>
#include <errno.h>
#include <functional>
#include <limits>
#include <stdlib.h>
#include <memory>
#include <string>
#include <libcamera/base/file.h>
#include <libcamera/base/log.h>
@@ -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<typename T> YamlObject::get<T>() 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<typename T, typename U> YamlObject::get<T>(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<typename T> YamlObject::set<T>(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<T>() 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<bool>
YamlObject::Accessor<bool>::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<bool>::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<typename T>
struct YamlObject::Accessor<T, std::enable_if_t<
std::is_same_v<int8_t, T> ||
std::is_same_v<uint8_t, T> ||
std::is_same_v<int16_t, T> ||
std::is_same_v<uint16_t, T> ||
std::is_same_v<int32_t, T> ||
std::is_same_v<uint32_t, T>>>
{
std::optional<T> 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<int8_t>;
template struct YamlObject::Accessor<uint8_t>;
template struct YamlObject::Accessor<int16_t>;
template struct YamlObject::Accessor<uint16_t>;
template struct YamlObject::Accessor<int32_t>;
template struct YamlObject::Accessor<uint32_t>;
template<>
std::optional<float>
YamlObject::Accessor<float>::get(const YamlObject &obj) const
{
return obj.get<double>();
}
template<>
void YamlObject::Accessor<float>::set(YamlObject &obj, float value)
{
obj.set<double>(value);
}
template<>
std::optional<double>
YamlObject::Accessor<double>::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<double>::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<std::string>
YamlObject::Accessor<std::string>::get(const YamlObject &obj) const
{
if (obj.type_ != Type::Value)
return std::nullopt;
return obj.value_;
}
template<>
void YamlObject::Accessor<std::string>::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<typename T>
struct YamlObject::Accessor<std::vector<T>> {
std::optional<std::vector<T>> get(const YamlObject &obj) const
{
if (obj.type_ != Type::List)
return std::nullopt;
std::vector<T> values;
values.reserve(obj.list_.size());
for (const YamlObject &entry : obj.asList()) {
auto value = entry.get<T>();
if (!value)
return std::nullopt;
values.emplace_back(std::move(*value));
}
return values;
}
};
template struct YamlObject::Accessor<std::vector<bool>>;
template struct YamlObject::Accessor<std::vector<float>>;
template struct YamlObject::Accessor<std::vector<double>>;
template struct YamlObject::Accessor<std::vector<int8_t>>;
template struct YamlObject::Accessor<std::vector<uint8_t>>;
template struct YamlObject::Accessor<std::vector<int16_t>>;
template struct YamlObject::Accessor<std::vector<uint16_t>>;
template struct YamlObject::Accessor<std::vector<int32_t>>;
template struct YamlObject::Accessor<std::vector<uint32_t>>;
template struct YamlObject::Accessor<std::vector<std::string>>;
#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
* <em>std::pair<const std::string &, const \ref YamlObject &></em>.
*
* 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 <em>const YamlObject &</em>.
*
* 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<YamlObject> &&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<YamlObject> &&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