[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:13:53 CEST 2022


On Mon, May 09, 2022 at 08:10:28PM +0300, Laurent Pinchart via libcamera-devel wrote:
> 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.

And

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

:-)

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

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list