[libcamera-devel] [PATCH v6 1/3] libcamera: Introduce YamlParser as a helper to parse yaml files
Kieran Bingham
kieran.bingham at ideasonboard.com
Mon May 9 22:51:46 CEST 2022
Quoting Han-Lin Chen via libcamera-devel (2022-04-27 15:09:27)
> Introduce YamlParser as a helper to convert contents of a yaml file to
> a tree based structure for easier reading, and to avoid writing parser
> with raw yaml tokens. The class is based on libyaml, and only support
> reading but not writing a yaml file.
>
> The interface is inspired by Json::Value class from jsoncpp:
> http://jsoncpp.sourceforge.net/class_json_1_1_value.html
>
> Signed-off-by: Han-Lin Chen <hanlinchen at chromium.org>
This causes compile failures on GCC-8. (I haven't yet tested further
than that, as it's the first failure that hits).
FAILED: src/libcamera/libcamera.so.0.0.0.p/yaml_parser.cpp.o
g++-8 -Isrc/libcamera/libcamera.so.0.0.0.p -Isrc/libcamera -I../../../src/libcamera/src/libcamera -Iinclude -I../../../src/libcamera/include -Iinclude/libcamera -Iinclude/libcamera/ipa -Iinclude/libcamera/internal -Isrc/libcamera/proxy -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -Wextra -Werror -std=c++17 -g -Wl,--start-group -lstdc++fs -Wl,--end-group -Wshadow -include config.h -fPIC -DLIBCAMERA_BASE_PRIVATE -MD -MQ src/libcamera/libcamera.so.0.0.0.p/yaml_parser.cpp.o -MF src/libcamera/libcamera.so.0.0.0.p/yaml_parser.cpp.o.d -o src/libcamera/libcamera.so.0.0.0.p/yaml_parser.cpp.o -c ../../../src/libcamera/src/libcamera/yaml_parser.cpp
../../../src/libcamera/src/libcamera/yaml_parser.cpp:360:18: error: ‘function’ in namespace ‘std’ does not name a template type
const std::function<int(EventPtr event)> &parseItem);
^~~~~~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:360:13: note: ‘std::function’ is defined in header ‘<functional>’; did you forget to ‘#include <functional>’?
../../../src/libcamera/src/libcamera/yaml_parser.cpp:16:1:
+#include <functional>
../../../src/libcamera/src/libcamera/yaml_parser.cpp:360:13:
const std::function<int(EventPtr event)> &parseItem);
^~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:360:26: error: expected ‘,’ or ‘...’ before ‘<’ token
const std::function<int(EventPtr event)> &parseItem);
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp:509:22: error: ‘function’ in namespace ‘std’ does not name a template type
const std::function<int(EventPtr event)> &parseItem)
^~~~~~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:509:17: note: ‘std::function’ is defined in header ‘<functional>’; did you forget to ‘#include <functional>’?
const std::function<int(EventPtr event)> &parseItem)
^~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:509:30: error: expected ‘,’ or ‘...’ before ‘<’ token
const std::function<int(EventPtr event)> &parseItem)
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp: In member function ‘int libcamera::YamlParserContext::parseDictionaryOrList(libcamera::YamlObject::Type, int)’:
../../../src/libcamera/src/libcamera/yaml_parser.cpp:527:13: error: ‘parseItem’ was not declared in this scope
int ret = parseItem(std::move(evt));
^~~~~~~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:527:13: note: suggested alternative: ‘parser_’
int ret = parseItem(std::move(evt));
^~~~~~~~~
parser_
../../../src/libcamera/src/libcamera/yaml_parser.cpp: In member function ‘int libcamera::YamlParserContext::parseNextYamlObject(libcamera::YamlObject&, libcamera::YamlParserContext::EventPtr)’:
../../../src/libcamera/src/libcamera/yaml_parser.cpp:567:57: error: no matching function for call to ‘libcamera::YamlParserContext::parseDictionaryOrList(libcamera::YamlObject::Type, libcamera::YamlParserContext::parseNextYamlObject(libcamera::YamlObject&, libcamera::YamlParserContext::EventPtr)::<lambda(libcamera::YamlParserContext::EventPtr)>&)’
return parseDictionaryOrList(YamlObject::List, handler);
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp:508:5: note: candidate: ‘int libcamera::YamlParserContext::parseDictionaryOrList(libcamera::YamlObject::Type, int)’
int YamlParserContext::parseDictionaryOrList(YamlObject::Type type,
^~~~~~~~~~~~~~~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:508:5: note: no known conversion for argument 2 from ‘libcamera::YamlParserContext::parseNextYamlObject(libcamera::YamlObject&, libcamera::YamlParserContext::EventPtr)::<lambda(libcamera::YamlParserContext::EventPtr)>’ to ‘int’
../../../src/libcamera/src/libcamera/yaml_parser.cpp:594:63: error: no matching function for call to ‘libcamera::YamlParserContext::parseDictionaryOrList(libcamera::YamlObject::Type, libcamera::YamlParserContext::parseNextYamlObject(libcamera::YamlObject&, libcamera::YamlParserContext::EventPtr)::<lambda(libcamera::YamlParserContext::EventPtr)>&)’
return parseDictionaryOrList(YamlObject::Dictionary, handler);
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp:508:5: note: candidate: ‘int libcamera::YamlParserContext::parseDictionaryOrList(libcamera::YamlObject::Type, int)’
int YamlParserContext::parseDictionaryOrList(YamlObject::Type type,
^~~~~~~~~~~~~~~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:508:5: note: no known conversion for argument 2 from ‘libcamera::YamlParserContext::parseNextYamlObject(libcamera::YamlObject&, libcamera::YamlParserContext::EventPtr)::<lambda(libcamera::YamlParserContext::EventPtr)>’ to ‘int’
../../../src/libcamera/src/libcamera/yaml_parser.cpp:567:58: error: this statement may fall through [-Werror=implicit-fallthrough=]
return parseDictionaryOrList(YamlObject::List, handler);
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp:570:2: note: here
case YAML_MAPPING_START_EVENT: {
^~~~
../../../src/libcamera/src/libcamera/yaml_parser.cpp:594:64: error: this statement may fall through [-Werror=implicit-fallthrough=]
return parseDictionaryOrList(YamlObject::Dictionary, handler);
^
../../../src/libcamera/src/libcamera/yaml_parser.cpp:597:2: note: here
default:
^~~~~~~
cc1plus: all warnings being treated as errors
Are we still supporting gcc-8 ?
--
Kieran
> ---
> README.rst | 4 +-
> include/libcamera/internal/meson.build | 1 +
> include/libcamera/internal/yaml_parser.h | 87 +++
> src/libcamera/meson.build | 3 +
> src/libcamera/yaml_parser.cpp | 679 +++++++++++++++++++++++
> 5 files changed, 772 insertions(+), 2 deletions(-)
> create mode 100644 include/libcamera/internal/yaml_parser.h
> create mode 100644 src/libcamera/yaml_parser.cpp
>
> diff --git a/README.rst b/README.rst
> index aae6b79f..b5a2d448 100644
> --- a/README.rst
> +++ b/README.rst
> @@ -60,7 +60,7 @@ Meson Build system: [required]
> pip3 install --user --upgrade meson
>
> for the libcamera core: [required]
> - python3-yaml python3-ply python3-jinja2
> + libyaml-dev python3-yaml python3-ply python3-jinja2
>
> for IPA module signing: [required]
> libgnutls28-dev openssl
> @@ -98,7 +98,7 @@ for tracing with lttng: [optional]
> liblttng-ust-dev python3-jinja2 lttng-tools
>
> for android: [optional]
> - libexif-dev libjpeg-dev libyaml-dev
> + libexif-dev libjpeg-dev
>
> for lc-compliance: [optional]
> libevent-dev
> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
> index c9e055d4..7a780d48 100644
> --- a/include/libcamera/internal/meson.build
> +++ b/include/libcamera/internal/meson.build
> @@ -42,4 +42,5 @@ libcamera_internal_headers = files([
> 'v4l2_pixelformat.h',
> 'v4l2_subdevice.h',
> 'v4l2_videodevice.h',
> + 'yaml_parser.h',
> ])
> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> new file mode 100644
> index 00000000..3a4f3052
> --- /dev/null
> +++ b/include/libcamera/internal/yaml_parser.h
> @@ -0,0 +1,87 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2022, Google Inc.
> + *
> + * yaml_parser.h - libcamera YAML parsing helper
> + */
> +
> +#pragma once
> +
> +#include <cstdio>
> +#include <map>
> +#include <string>
> +#include <vector>
> +
> +#include <libcamera/base/class.h>
> +
> +#include <libcamera/geometry.h>
> +
> +namespace libcamera {
> +
> +class YamlParserContext;
> +
> +class YamlObject
> +{
> +public:
> + YamlObject();
> + ~YamlObject();
> +
> + bool isValue() const
> + {
> + return type_ == Value;
> + }
> + bool isList() const
> + {
> + return type_ == List;
> + }
> + bool isDictionary() const
> + {
> + return type_ == Dictionary;
> + }
> +
> +#ifndef __DOXYGEN__
> + template<typename T,
> + typename std::enable_if_t<
> + std::is_same<bool, T>::value ||
> + std::is_same<double, T>::value ||
> + std::is_same<int32_t, T>::value ||
> + std::is_same<uint32_t, T>::value ||
> + std::is_same<std::string, T>::value ||
> + std::is_same<Size, T>::value> * = nullptr>
> +#else
> + template<typename T>
> +#endif
> + T get(const T &defaultValue, bool *ok = nullptr) const;
> +
> + std::size_t size() const;
> + const YamlObject &operator[](std::size_t index) const;
> +
> + bool contains(const std::string &key) const;
> + const YamlObject &operator[](const std::string &key) const;
> + std::vector<std::string> memberNames() const;
> +
> +private:
> + LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
> +
> + friend class YamlParserContext;
> +
> + enum Type {
> + Dictionary,
> + List,
> + Value,
> + };
> +
> + Type type_;
> +
> + std::string value_;
> + std::vector<std::unique_ptr<YamlObject>> list_;
> + std::map<const std::string, std::unique_ptr<YamlObject>> dictionary_;
> +};
> +
> +class YamlParser final
> +{
> +public:
> + static std::unique_ptr<YamlObject> parse(std::FILE *fh);
> +};
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 26912ca1..f8e18e03 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -46,6 +46,7 @@ libcamera_sources = files([
> 'v4l2_pixelformat.cpp',
> 'v4l2_subdevice.cpp',
> 'v4l2_videodevice.cpp',
> + 'yaml_parser.cpp',
> ])
>
> libcamera_sources += libcamera_public_headers
> @@ -66,6 +67,7 @@ subdir('proxy')
> libdl = cc.find_library('dl')
> libgnutls = cc.find_library('gnutls', required : true)
> libudev = dependency('libudev', required : false)
> +libyaml = dependency('yaml-0.1', required : true)
>
> if libgnutls.found()
> config_h.set('HAVE_GNUTLS', 1)
> @@ -126,6 +128,7 @@ libcamera_deps = [
> libgnutls,
> liblttng,
> libudev,
> + libyaml,
> ]
>
> # We add '/' to the build_rpath as a 'safe' path to act as a boolean flag.
> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> new file mode 100644
> index 00000000..4a047494
> --- /dev/null
> +++ b/src/libcamera/yaml_parser.cpp
> @@ -0,0 +1,679 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2022, Google Inc.
> + *
> + * yaml_parser.cpp - libcamera YAML parsing helper
> + */
> +
> +#include "libcamera/internal/yaml_parser.h"
> +
> +#include <cerrno>
> +#include <cstdlib>
> +
> +#include <libcamera/base/log.h>
> +
> +#include <yaml.h>
> +
> +/**
> + * \file libcamera/internal/yaml_parser.h
> + * \brief A YAML parser helper
> + */
> +
> +namespace libcamera {
> +
> +LOG_DEFINE_CATEGORY(YamlParser)
> +
> +namespace {
> +
> +/* Empty static YamlObject as a safe result for invalid operations */
> +static const YamlObject empty;
> +
> +void setOk(bool *ok, bool result)
> +{
> + if (ok)
> + *ok = result;
> +}
> +
> +} /* 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 a dictionary or list of YamlObjects or a value if a tree
> + * leaf.
> + */
> +
> +YamlObject::YamlObject()
> + : type_(Value)
> +{
> +}
> +
> +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 template<typename T> YamlObject::get<T>(
> + * const T &defaultValue, bool *ok) const
> + * \brief Parse the YamlObject as a \a T value
> + * \param[in] defaultValue The default value when failing to parse
> + * \param[out] ok The result of whether the parse succeeded
> + *
> + * 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, and \a ok is set to
> + * false. Otherwise, the YamlObject value is returned, and \a ok is set to true.
> + *
> + * The \a ok pointer is optional and can be a nullptr if the caller doesn't
> + * need to know if parsing succeeded.
> + *
> + * \return Value as a bool type
> + */
> +
> +#ifndef __DOXYGEN__
> +
> +template<>
> +bool YamlObject::get(const bool &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != Value)
> + return defaultValue;
> +
> + if (value_ == "true") {
> + setOk(ok, true);
> + return true;
> + } else if (value_ == "false") {
> + setOk(ok, true);
> + return false;
> + }
> +
> + return defaultValue;
> +}
> +
> +template<>
> +int32_t YamlObject::get(const int32_t &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != Value)
> + return defaultValue;
> +
> + if (value_ == "")
> + return defaultValue;
> +
> + char *end;
> +
> + errno = 0;
> + int32_t value = std::strtol(value_.c_str(), &end, 10);
> +
> + if ('\0' != *end || errno == ERANGE)
> + return defaultValue;
> +
> + setOk(ok, true);
> + return value;
> +}
> +
> +template<>
> +uint32_t YamlObject::get(const uint32_t &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != Value)
> + return defaultValue;
> +
> + if (value_ == "")
> + return defaultValue;
> +
> + /*
> + * libyaml parses all scalar values as strings. When a string has
> + * leading spaces before a minus sign, for example " -10", strtoul
> + * skips leading spaces, accepts the leading minus sign, and the
> + * calculated digits are negated as if by unary minus. Rule it out in
> + * case the user gets a large number when the value is negative.
> + */
> + std::size_t found = value_.find_first_not_of(" \t");
> + if (found != std::string::npos && value_[found] == '-')
> + return defaultValue;
> +
> + char *end;
> +
> + errno = 0;
> + uint32_t value = std::strtoul(value_.c_str(), &end, 10);
> +
> + if ('\0' != *end || errno == ERANGE)
> + return defaultValue;
> +
> + setOk(ok, true);
> + return value;
> +}
> +
> +template<>
> +double YamlObject::get(const double &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != Value)
> + return defaultValue;
> +
> + if (value_ == "")
> + return defaultValue;
> +
> + char *end;
> +
> + errno = 0;
> + double value = std::strtod(value_.c_str(), &end);
> +
> + if ('\0' != *end || errno == ERANGE)
> + return defaultValue;
> +
> + setOk(ok, true);
> + return value;
> +}
> +
> +template<>
> +std::string YamlObject::get(const std::string &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != Value)
> + return defaultValue;
> +
> + setOk(ok, true);
> + return value_;
> +}
> +
> +template<>
> +Size YamlObject::get(const Size &defaultValue, bool *ok) const
> +{
> + setOk(ok, false);
> +
> + if (type_ != List)
> + return defaultValue;
> +
> + if (list_.size() != 2)
> + return defaultValue;
> +
> + /*
> + * Add a local variable to validate each dimension in case
> + * that ok == nullptr.
> + */
> + bool valid;
> + uint32_t width = list_[0]->get<uint32_t>(0, &valid);
> + if (!valid)
> + return defaultValue;
> +
> + uint32_t height = list_[1]->get<uint32_t>(0, &valid);
> + if (!valid)
> + return defaultValue;
> +
> + setOk(ok, true);
> + return Size(width, height);
> +}
> +
> +#endif /* __DOXYGEN__ */
> +
> +/**
> + * \fn YamlObject::size()
> + * \brief Retrieve the number of elements in a list YamlObject
> + *
> + * This function retrieves the size of the YamlObject, defined as the number of
> + * child elements it contains. Only YamlObject instances of List type 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
> +{
> + if (type_ != List)
> + return 0;
> +
> + return list_.size();
> +}
> +
> +/**
> + * \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 is invalid and results in undefined behaviour.
> + *
> + * \return The YamlObject as an element of the list
> + */
> +const YamlObject &YamlObject::operator[](std::size_t index) const
> +{
> + if (type_ != List || index >= size())
> + return empty;
> +
> + return *list_[index];
> +}
> +
> +/**
> + * \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(const std::string &key) const
> +{
> + if (dictionary_.find(key) == dictionary_.end())
> + return false;
> +
> + return true;
> +}
> +
> +/**
> + * \fn YamlObject::memberNames()
> + * \brief Retrieve all member names of the dictionary
> + *
> + * This function retrieve member names of a YamlObject. 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.
> + *
> + * \todo Replace this function with an iterator-based API
> + *
> + * \return A vector of string as the member names
> + */
> +std::vector<std::string> YamlObject::memberNames() const
> +{
> + std::vector<std::string> memberNames;
> + for (auto &[key, _] : dictionary_)
> + memberNames.push_back(key);
> +
> + return memberNames;
> +}
> +
> +/**
> + * \fn YamlObject::operator[](const std::string &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 is invalid and results in undefined
> + * behaviour.
> + *
> + * \return The YamlObject corresponding to the \a key member
> + */
> +const YamlObject &YamlObject::operator[](const std::string &key) const
> +{
> + if (type_ != Dictionary || !contains(key))
> + return empty;
> +
> + auto iter = dictionary_.find(key);
> + return *iter->second;
> +}
> +
> +#ifndef __DOXYGEN__
> +
> +class YamlParserContext
> +{
> +public:
> + YamlParserContext();
> + ~YamlParserContext();
> +
> + int init(std::FILE *fh);
> + int parseContent(YamlObject &yamlObject);
> +
> +private:
> + struct EventDeleter {
> + void operator()(yaml_event_t *event) const
> + {
> + yaml_event_delete(event);
> + delete event;
> + }
> + };
> + using EventPtr = std::unique_ptr<yaml_event_t, EventDeleter>;
> +
> + EventPtr nextEvent();
> +
> + void readValue(std::string &value, EventPtr event);
> + int parseDictionaryOrList(YamlObject::Type type,
> + const std::function<int(EventPtr event)> &parseItem);
> + int parseNextYamlObject(YamlObject &yamlObject, EventPtr event);
> +
> + bool parserValid_;
> + yaml_parser_t parser_;
> +};
> +
> +/**
> + * \class YamlParserContext
> + * \brief Class for YamlParser parsing and context data
> + *
> + * The YamlParserContext class stores the internal yaml_parser_t and provides
> + * helper functions to do event-based parsing for YAML files.
> + */
> +YamlParserContext::YamlParserContext()
> + : parserValid_(false)
> +{
> +}
> +
> +/**
> + * \class YamlParserContext
> + * \brief Destructor of YamlParserContext
> + */
> +YamlParserContext::~YamlParserContext()
> +{
> + if (parserValid_) {
> + yaml_parser_delete(&parser_);
> + parserValid_ = false;
> + }
> +}
> +
> +/**
> + * \fn YamlParserContext::init()
> + * \brief Initialize a parser with an opened file for parsing
> + * \param[in] fh The YAML file to parse
> + *
> + * Prior to parsing the YAML content, the YamlParserContext must be initialized
> + * with an opened FILE to create an internal parser. The FILE needs to stay
> + * valid during the process.
> + *
> + * \return 0 on success or a negative error code otherwise
> + * \retval -EINVAL The parser has failed to initialize
> + */
> +int YamlParserContext::init(std::FILE *fh)
> +{
> + /* yaml_parser_initialize returns 1 when it succeededs */
> + if (!yaml_parser_initialize(&parser_)) {
> + LOG(YamlParser, Error) << "Failed to initialize YAML parser";
> + return -EINVAL;
> + }
> + parserValid_ = true;
> + yaml_parser_set_input_file(&parser_, fh);
> +
> + return 0;
> +}
> +
> +/**
> + * \fn YamlParserContext::nextEvent()
> + * \brief Get the next event
> + *
> + * Get the next event in the current YAML event stream, and return nullptr when
> + * there is no more event.
> + *
> + * \return The next event on success or nullptr otherwise
> + */
> +YamlParserContext::EventPtr YamlParserContext::nextEvent()
> +{
> + EventPtr event(new yaml_event_t);
> +
> + /* yaml_parser_parse returns 1 when it succeeds */
> + if (!yaml_parser_parse(&parser_, event.get()))
> + return nullptr;
> +
> + return event;
> +}
> +
> +/**
> + * \fn YamlParserContext::parseContent()
> + * \brief Parse the content of a YAML document
> + * \param[in] yamlObject The result of YamlObject
> + *
> + * Check YAML start and end events of a YAML document, and parse the root object
> + * of the YAML document into a YamlObject.
> + *
> + * \return 0 on success or a negative error code otherwise
> + * \retval -EINVAL The parser has failed to validate end of a YAML file
> + */
> +int YamlParserContext::parseContent(YamlObject &yamlObject)
> +{
> + /* Check start of the YAML file. */
> + EventPtr event = nextEvent();
> + if (!event || event->type != YAML_STREAM_START_EVENT)
> + return -EINVAL;
> +
> + event = nextEvent();
> + if (!event || event->type != YAML_DOCUMENT_START_EVENT)
> + return -EINVAL;
> +
> + /* Parse the root object. */
> + event = nextEvent();
> + if (parseNextYamlObject(yamlObject, std::move(event)))
> + return -EINVAL;
> +
> + /* Check end of the YAML file. */
> + event = nextEvent();
> + if (!event || event->type != YAML_DOCUMENT_END_EVENT)
> + return -EINVAL;
> +
> + event = nextEvent();
> + if (!event || event->type != YAML_STREAM_END_EVENT)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +/**
> + * \fn YamlParserContext::readValue()
> + * \brief Parse event scalar and fill its content into a string
> + * \param[in] value The string reference to fill value
> + *
> + * A helper function to parse a scalar event as string. The caller needs to
> + * guarantee the event is of scaler type.
> + */
> +void YamlParserContext::readValue(std::string &value, EventPtr event)
> +{
> + value.assign(reinterpret_cast<char *>(event->data.scalar.value),
> + event->data.scalar.length);
> +}
> +
> +/**
> + * \fn YamlParserContext::parseDictionaryOrList()
> + * \brief A helper function to abstract the common part of parsing dictionary or list
> + *
> + * \param[in] isDictionary True for parsing a dictionary, and false for a list
> + * \param[in] parseItem The callback to handle an item
> + *
> + * A helper function to abstract parsing an item from a dictionary or a list.
> + * The differences of them in a YAML event stream are:
> + *
> + * 1. The start and end event types are different
> + * 2. There is a leading scalar string as key in the items of a dictionary
> + *
> + * The caller should handle the leading key string in its callback parseItem
> + * when it's a dictionary.
> + *
> + * \return 0 on success or a negative error code otherwise
> + * \retval -EINVAL The parser is failed to initialize
> + */
> +int YamlParserContext::parseDictionaryOrList(YamlObject::Type type,
> + const std::function<int(EventPtr event)> &parseItem)
> +{
> + yaml_event_type_t endEventType = YAML_SEQUENCE_END_EVENT;
> + if (type == YamlObject::Dictionary)
> + endEventType = YAML_MAPPING_END_EVENT;
> +
> + /*
> + * Add a safety counter to make sure we don't loop indefinitely in case
> + * the YAML file is malformed.
> + */
> + for (unsigned int sentinel = 1000; sentinel; sentinel--) {
> + auto evt = nextEvent();
> + if (!evt)
> + return -EINVAL;
> +
> + if (evt->type == endEventType)
> + return 0;
> +
> + int ret = parseItem(std::move(evt));
> + if (ret)
> + return ret;
> + }
> +
> + LOG(YamlParser, Error) << "The YAML file contains a List or Dictionary"
> + " whose size exceeding the parser's limit(1000)";
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * \fn YamlParserContext::parseNextYamlObject()
> + * \brief Parse next YAML event and read it as a YamlObject
> + * \param[in] yamlObject The result of YamlObject
> + * \param[in] event The leading event of the object
> + *
> + * Parse next YAML object separately as a value, list or dictionary.
> + *
> + * \return 0 on success or a negative error code otherwise
> + * \retval -EINVAL Fail to parse the YAML file.
> + */
> +int YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr event)
> +{
> + if (!event)
> + return -EINVAL;
> +
> + switch (event->type) {
> + case YAML_SCALAR_EVENT:
> + yamlObject.type_ = YamlObject::Value;
> + readValue(yamlObject.value_, std::move(event));
> + return 0;
> +
> + case YAML_SEQUENCE_START_EVENT: {
> + yamlObject.type_ = YamlObject::List;
> + auto &list = yamlObject.list_;
> + auto handler = [this, &list](EventPtr evt) {
> + list.emplace_back(new YamlObject());
> + return parseNextYamlObject(*list.back(), std::move(evt));
> + };
> + return parseDictionaryOrList(YamlObject::List, handler);
> + }
> +
> + case YAML_MAPPING_START_EVENT: {
> + yamlObject.type_ = YamlObject::Dictionary;
> + auto &dictionary = yamlObject.dictionary_;
> + auto handler = [this, &dictionary](EventPtr evtKey) {
> + /* Parse key */
> + if (evtKey->type != YAML_SCALAR_EVENT) {
> + LOG(YamlParser, Error) << "Expect key at line: "
> + << evtKey->start_mark.line
> + << " column: "
> + << evtKey->start_mark.column;
> + return -EINVAL;
> + }
> +
> + std::string key;
> + readValue(key, std::move(evtKey));
> +
> + /* Parse value */
> + EventPtr evtValue = nextEvent();
> + if (!evtValue)
> + return -EINVAL;
> +
> + auto elem = dictionary.emplace(key, std::make_unique<YamlObject>());
> + return parseNextYamlObject(*elem.first->second.get(), std::move(evtValue));
> + };
> + return parseDictionaryOrList(YamlObject::Dictionary, handler);
> + }
> + default:
> + LOG(YamlParser, Error) << "Invalid YAML file";
> + return -EINVAL;
> + }
> +}
> +
> +#endif /* __DOXYGEN__ */
> +
> +/**
> + * \class YamlParser
> + * \brief A helper class for parsing a YAML file
> + *
> + * The YamlParser class provides an easy interface to parse the contents of a
> + * YAML file into a tree of YamlObject instances.
> + *
> + * Example usage:
> + *
> + * \code{.unparsed}
> + *
> + * name:
> + * "John"
> + * numbers:
> + * - 1
> + * - 2
> + *
> + * \endcode
> + *
> + * The following code illustrates how to parse the above YAML file:
> + *
> + * \code{.cpp}
> + *
> + * std::unique_ptr<YamlObject> root = YamlParser::parse(fh);
> + * if (!root)
> + * return;
> + *
> + * if (!root->isDictionary())
> + * return;
> + *
> + * const YamlObject &name = (*root)["name"];
> + * std::cout << name.get<std::string>("") << std::endl;
> + *
> + * const YamlObject &numbers = (*root)["numbers"];
> + * if (!numbers.isList())
> + * return;
> + *
> + * for (std::size_t i = 0; i < numbers.size(); i++)
> + * std::cout << numbers[i].get<int32_t>(0) << std::endl;
> + *
> + * \endcode
> + *
> + * The YamlParser::parse() function takes an open FILE, parses its contents, and
> + * returns a pointer to a YamlObject corresponding to the root node of the YAML
> + * document.
> + */
> +
> +/**
> + * \fn YamlParser::parse()
> + * \brief Parse a YAML file as a YamlObject
> + * \param[in] fh The YAML file to parse
> + *
> + * The YamlParser::parse() function takes an open FILE, parses its contents, and
> + * returns a pointer to a YamlObject corresponding to the root node of the YAML
> + * document. The caller is responsible for closing the file.
> + *
> + * \return Pointer to result YamlObject on success or nullptr otherwise
> + */
> +std::unique_ptr<YamlObject> YamlParser::parse(std::FILE *fh)
> +{
> + YamlParserContext context;
> +
> + if (context.init(fh))
> + return nullptr;
> +
> + std::unique_ptr<YamlObject> root(new YamlObject());
> +
> + if (context.parseContent(*root)) {
> + LOG(YamlParser, Error) << "Failed to parse YAML content";
> + return nullptr;
> + }
> +
> + return root;
> +}
> +
> +} /* namespace libcamera */
> --
> 2.36.0.rc2.479.g8af0fa9b8e-goog
>
More information about the libcamera-devel
mailing list