[libcamera-devel] [PATCH v6 1/3] libcamera: Introduce YamlParser as a helper to parse yaml files

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon May 9 19:10:28 CEST 2022


Hi Han-Lin,

Thank you for the patch.

On Wed, Apr 27, 2022 at 10:09:27PM +0800, Han-Lin Chen via libcamera-devel wrote:
> 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>
> ---
>  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

[snip]

> 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 @@

[snip]

> +/**
> + * \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)";

s/exceeding/exceeds/

> +
> +	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);
> +	}

Missing blank line.

I'll fix these when applying.

> +	default:
> +		LOG(YamlParser, Error) << "Invalid YAML file";
> +		return -EINVAL;
> +	}
> +}

[snip]

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list