{"id":15347,"url":"https://patchwork.libcamera.org/api/1.1/patches/15347/?format=json","web_url":"https://patchwork.libcamera.org/patch/15347/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20220209071917.559993-4-hanlinchen@chromium.org>","date":"2022-02-09T07:19:11","name":"[libcamera-devel,3/9] libcamera: Introduce YamlParser as a helper to parse yaml files","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"426d0e7b4a8b3299b4dd7a3c0ec89831b2e1bece","submitter":{"id":98,"url":"https://patchwork.libcamera.org/api/1.1/people/98/?format=json","name":"Hanlin Chen","email":"hanlinchen@chromium.org"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/15347/mbox/","series":[{"id":2923,"url":"https://patchwork.libcamera.org/api/1.1/series/2923/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=2923","date":"2022-02-09T07:19:08","name":"Introduce Pipeline configuration preference for IPU3","version":1,"mbox":"https://patchwork.libcamera.org/series/2923/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/15347/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/15347/checks/","tags":{},"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 C69A3C3261\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  9 Feb 2022 07:19:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E2C85610E7;\n\tWed,  9 Feb 2022 08:19:33 +0100 (CET)","from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com\n\t[IPv6:2607:f8b0:4864:20::102d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 846CD60E66\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  9 Feb 2022 08:19:32 +0100 (CET)","by mail-pj1-x102d.google.com with SMTP id\n\tc5-20020a17090a1d0500b001b904a7046dso2743072pjd.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 08 Feb 2022 23:19:32 -0800 (PST)","from localhost ([2401:fa00:1:10:a5cf:9a43:1c6d:824])\n\tby smtp.gmail.com with UTF8SMTPSA id\n\tml19sm5266237pjb.52.2022.02.08.23.19.29\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tTue, 08 Feb 2022 23:19:30 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"RhwLRR3g\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=vSxjdnVBCGYsMUphGRb24V2CxxmnLtecgOxg8GmfZZY=;\n\tb=RhwLRR3gn3AQ3bl4iyHSFE+qRzHUi8G80lOxF9H892aUe92uvNqPhoTjOTKjAapDQO\n\t1bNZz1glpgAoN7bZhW2fSmxppJxZqFSmwMM8IL1VrZoeMkuBZ1EvkolhkihM/Bo6+AJt\n\tc16eKA7q8wsHgo7MFZg50IRYtlb01lpC9R1fk=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=vSxjdnVBCGYsMUphGRb24V2CxxmnLtecgOxg8GmfZZY=;\n\tb=HKgKSb88XytY0YjiFMVbrJHnWgzyN0N0biGzTzqunIji+nWOyZyJUhJGb7RepKZIy9\n\tQiDMUdQ5Z+MCDDkg0DACYU2ciBUH2xHGOr39lHL4SizGQHjDFR+gsWaNairVKGj4ghLP\n\thtPGbu+c2gUOoJJKX1YDwwVuA7Ng/HBaZn4Pme7PEYv3GNY9pwxhHRmQAW7o7e16761N\n\t+Xrb7RFlZOlYUowkrePXZXz/oHVpqi7ndJ7n2gc4gehn9KMfsEd5OIOtPOHzxVLKZrqd\n\tuzDvptQa7fEG6+w6z3t91YfWSCg/8qE+FzqK1U8UaC7iTATbQXKw8hjtj4GDEvlVUbfl\n\thaUA==","X-Gm-Message-State":"AOAM5331ARp+LKqAVDPjKk38fTWMfExDVX5ja6ONQs+57u2Anl2Jp1QI\n\txwsfVaBCyM47A++ySSI8BglaCqOxfsMk4Q==","X-Google-Smtp-Source":"ABdhPJy191/5Cj3Z98Hd1pUCFUGte7aVicEEUsQ9Ov2T3o7JfPcXCcQMMMnVdvEnlkfYO3h9rKPYrg==","X-Received":"by 2002:a17:902:a708:: with SMTP id\n\tw8mr838510plq.101.1644391170462; \n\tTue, 08 Feb 2022 23:19:30 -0800 (PST)","From":"Han-Lin Chen <hanlinchen@chromium.org>","To":"libcamera-devel@lists.libcamera.org","Date":"Wed,  9 Feb 2022 15:19:11 +0800","Message-Id":"<20220209071917.559993-4-hanlinchen@chromium.org>","X-Mailer":"git-send-email 2.35.0.263.gb82422642f-goog","In-Reply-To":"<20220209071917.559993-1-hanlinchen@chromium.org>","References":"<20220209071917.559993-1-hanlinchen@chromium.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH 3/9] libcamera: Introduce YamlParser as a\n\thelper to parse yaml files","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>"},"content":"Introduce YamlParser as a helper to convert contents of a yaml file to\na tree based structure for easier reading, and to avoid writing parser\nwith raw yaml tokens. The class is based on libyaml, and only support\nreading but writing a yaml file.\n\nThe interface is inspired by Json::Value class from jsoncpp:\nhttp://jsoncpp.sourceforge.net/class_json_1_1_value.html\n\nSigned-off-by: Han-Lin Chen <hanlinchen@chromium.org>\n---\n README.rst                               |   2 +-\n include/libcamera/internal/meson.build   |   1 +\n include/libcamera/internal/yaml_parser.h |  82 +++\n src/libcamera/meson.build                |   3 +\n src/libcamera/yaml_parser.cpp            | 796 +++++++++++++++++++++++\n 5 files changed, 883 insertions(+), 1 deletion(-)\n create mode 100644 include/libcamera/internal/yaml_parser.h\n create mode 100644 src/libcamera/yaml_parser.cpp","diff":"diff --git a/README.rst b/README.rst\nindex ca8a97cb..4a2a451a 100644\n--- a/README.rst\n+++ b/README.rst\n@@ -60,7 +60,7 @@ Meson Build system: [required]\n             pip3 install --user --upgrade meson\n \n for the libcamera core: [required]\n-        python3-yaml python3-ply python3-jinja2\n+        libyaml-dev python3-yaml python3-ply python3-jinja2\n \n for IPA module signing: [required]\n         libgnutls28-dev openssl\ndiff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\nindex c9e055d4..7a780d48 100644\n--- a/include/libcamera/internal/meson.build\n+++ b/include/libcamera/internal/meson.build\n@@ -42,4 +42,5 @@ libcamera_internal_headers = files([\n     'v4l2_pixelformat.h',\n     'v4l2_subdevice.h',\n     'v4l2_videodevice.h',\n+    'yaml_parser.h',\n ])\ndiff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\nnew file mode 100644\nindex 00000000..7ba7c52c\n--- /dev/null\n+++ b/include/libcamera/internal/yaml_parser.h\n@@ -0,0 +1,82 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * yaml_parser.h - libcamera yaml parsing helper\n+ */\n+\n+#pragma once\n+\n+#include <map>\n+#include <string>\n+#include <vector>\n+\n+#include <libcamera/base/class.h>\n+\n+#include <libcamera/geometry.h>\n+\n+namespace libcamera {\n+\n+class YamlObject\n+{\n+public:\n+\tYamlObject() = default;\n+\t~YamlObject() = default;\n+\n+\tYamlObject(const YamlObject &) = delete;\n+\tYamlObject &operator=(const YamlObject &) = delete;\n+\n+\tbool isValue() const\n+\t{\n+\t\treturn type_ == VALUE;\n+\t}\n+\tbool isList() const\n+\t{\n+\t\treturn type_ == LIST;\n+\t}\n+\tbool isDictionary() const\n+\t{\n+\t\treturn type_ == DICTIONARY;\n+\t}\n+\n+\tbool asBool(bool defaultValue = false, bool *ok = nullptr) const;\n+\tdouble asDouble(double defaultValue = 0.0, bool *ok = nullptr) const;\n+\tint32_t asInt32(int32_t defaultValue = 0, bool *ok = nullptr) const;\n+\tuint32_t asUint32(uint32_t defaultValue = 0, bool *ok = nullptr) const;\n+\tstd::string asString(std::string defaultValue = \"\", bool *ok = nullptr) const;\n+\tSize asSize(Size defaultValue = Size(), bool *ok = nullptr) const;\n+\n+\tint length() const;\n+\tconst YamlObject &operator[](int index) const;\n+\n+\tbool isMember(const std::string &key) const;\n+\tconst YamlObject &get(const std::string &key) const;\n+\tstd::vector<std::string> getMemberNames() const;\n+\n+private:\n+\tfriend class YamlParser;\n+\n+\tenum PropertyType {\n+\t\tDICTIONARY,\n+\t\tLIST,\n+\t\tVALUE,\n+\t} type_;\n+\n+\tstd::string value_;\n+\tstd::vector<std::unique_ptr<YamlObject>> list_;\n+\tstd::map<const std::string, std::unique_ptr<YamlObject>> dictionary_;\n+};\n+\n+class YamlParser final : public Extensible\n+{\n+\tLIBCAMERA_DECLARE_PRIVATE()\n+\n+public:\n+\tYamlParser();\n+\tint ParseAsYamlObject(FILE *fh, YamlObject &yamlObject);\n+\n+private:\n+\tint ParseNextYamlObject(YamlObject &yamlObject);\n+};\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 26912ca1..f8e18e03 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -46,6 +46,7 @@ libcamera_sources = files([\n     'v4l2_pixelformat.cpp',\n     'v4l2_subdevice.cpp',\n     'v4l2_videodevice.cpp',\n+    'yaml_parser.cpp',\n ])\n \n libcamera_sources += libcamera_public_headers\n@@ -66,6 +67,7 @@ subdir('proxy')\n libdl = cc.find_library('dl')\n libgnutls = cc.find_library('gnutls', required : true)\n libudev = dependency('libudev', required : false)\n+libyaml = dependency('yaml-0.1', required : true)\n \n if libgnutls.found()\n     config_h.set('HAVE_GNUTLS', 1)\n@@ -126,6 +128,7 @@ libcamera_deps = [\n     libgnutls,\n     liblttng,\n     libudev,\n+    libyaml,\n ]\n \n # We add '/' to the build_rpath as a 'safe' path to act as a boolean flag.\ndiff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\nnew file mode 100644\nindex 00000000..203d9cf4\n--- /dev/null\n+++ b/src/libcamera/yaml_parser.cpp\n@@ -0,0 +1,796 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ *\n+ * yaml_parser.cpp - libcamera yaml parsing helper\n+ */\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+#include <assert.h>\n+\n+#include <libcamera/base/log.h>\n+\n+#include <yaml.h>\n+\n+/**\n+ * \\file libcamera/internal/yaml_parser.h\n+ * \\brief A yaml parser helper\n+ */\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(YamlParser)\n+\n+namespace {\n+\n+void setOk(bool *ok, bool result)\n+{\n+\tif (ok)\n+\t\t*ok = result;\n+}\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 a yaml content. A\n+ * YamlObject can be a dictionary or list of YamlObjects or a value if a tree\n+ * leaf.\n+ */\n+\n+/**\n+ * \\fn YamlObject::isValue()\n+ * \\brief Return whether the YamlObject is a value\n+ *\n+ * \\return Whether the YamlObject is a value\n+ */\n+\n+/**\n+ * \\fn YamlObject::isList()\n+ * \\brief Return whether the YamlObject is a list\n+ *\n+ * \\return Whether the YamlObject is a list\n+ */\n+\n+/**\n+ * \\fn YamlObject::isDictionary()\n+ * \\brief Return whether the YamlObject is a dictionary\n+ *\n+ * \\return Whether the YamlObject is a dictionary\n+ */\n+\n+/**\n+ * \\fn YamlObject::asBool()\n+ * \\brief Helper function to parse the YamlObject as a bool value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a bool value.\n+ *\n+ * \\return Value as a bool type\n+ */\n+bool YamlObject::asBool(bool defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != VALUE) {\n+\t\treturn defaultValue;\n+\t}\n+\n+\tif (value_ == \"true\") {\n+\t\tsetOk(ok, true);\n+\t\treturn true;\n+\t} else if (value_ == \"false\") {\n+\t\tsetOk(ok, true);\n+\t\treturn false;\n+\t}\n+\n+\treturn defaultValue;\n+}\n+\n+/**\n+ * \\fn YamlObject::asInt32()\n+ * \\brief Helper function to parse the YamlObject as a int32_t value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a int32_t value.\n+ *\n+ * \\return Value as a int32_t type\n+ */\n+int32_t YamlObject::asInt32(int32_t defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != VALUE)\n+\t\treturn defaultValue;\n+\n+\tint32_t value = std::strtol(value_.c_str(), nullptr, 10);\n+\tif (errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+/**\n+ * \\fn YamlObject::asUint32()\n+ * \\brief Helper function to parse the YamlObject as a uint32_t value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a uint32_t value.\n+ *\n+ * \\return Value as a uint32_t type\n+ */\n+uint32_t YamlObject::asUint32(uint32_t defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != VALUE) {\n+\t\treturn defaultValue;\n+\t}\n+\n+\tuint32_t value = std::strtoul(value_.c_str(), nullptr, 10);\n+\tif (errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+/**\n+ * \\fn YamlObject::asDouble()\n+ * \\brief Helper function to parse the YamlObject as a double value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a double value.\n+ *\n+ * \\return Value as a double type\n+ */\n+double YamlObject::asDouble(double defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\tif (type_ != VALUE)\n+\t\treturn defaultValue;\n+\n+\tdouble value = std::strtod(value_.c_str(), nullptr);\n+\tif (errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+/**\n+ * \\fn YamlObject::asString()\n+ * \\brief Helper function to parse the YamlObject as a string value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a string value.\n+ *\n+ * \\return Value as a string type\n+ */\n+std::string YamlObject::asString(std::string defaultValue, bool *ok) const\n+{\n+\tif (type_ != VALUE) {\n+\t\tsetOk(ok, false);\n+\t\treturn defaultValue;\n+\t}\n+\n+\treturn value_;\n+}\n+\n+/**\n+ * \\fn YamlObject::asSize()\n+ * \\brief Helper function to parse the YamlObject as a Size value\n+ * \\param[in] defaultValue The default value when fail to parse\n+ * \\param[in] ok The result of whether the parse success\n+ *\n+ * Helper function to parse the YamlObject as a Size value. The Size structure\n+ * is represented as a list of two non-negative numbers.\n+ *\n+ * \\return Value as a Size type\n+ */\n+Size YamlObject::asSize(Size defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != LIST)\n+\t\treturn defaultValue;\n+\n+\tif (list_.size() != 2)\n+\t\treturn defaultValue;\n+\n+\tbool isInt = false;\n+\tint width = list_[0]->asUint32(0, &isInt);\n+\tif (!isInt)\n+\t\treturn Size(0, 0);\n+\n+\tint height = list_[1]->asUint32(0, &isInt);\n+\tif (!isInt)\n+\t\treturn Size(0, 0);\n+\n+\tsetOk(ok, true);\n+\treturn Size(width, height);\n+}\n+\n+/**\n+ * \\fn YamlObject::length()\n+ * \\brief Helper function to return length of the list\n+ *\n+ * Helper function to parse the YamlObject as a List and return the length of\n+ * the List.\n+ *\n+ * \\return The length as a list\n+ */\n+int YamlObject::length() const\n+{\n+\tassert(type_ == LIST);\n+\n+\treturn list_.size();\n+}\n+\n+/**\n+ * \\fn YamlObject::operator[](int index)\n+ * \\brief Helper function to return an element of a list YamlObject by index\n+ *\n+ * Helper function to return an element of a list YamlObject by index.\n+ *\n+ * \\return The YamlObject as an element of the list\n+ */\n+const YamlObject &YamlObject::operator[](int index) const\n+{\n+\tassert(type_ == LIST);\n+\n+\treturn *list_[index];\n+}\n+\n+/**\n+ * \\fn YamlObject::isMember()\n+ * \\brief Helper function to check if an element of a dictionary exists\n+ *\n+ * Helper function to return an element of a list YamlObject by index.\n+ *\n+ * \\return Whether an element exists\n+ */\n+bool YamlObject::isMember(const std::string &key) const\n+{\n+\tassert(type_ == DICTIONARY);\n+\n+\tif (dictionary_.find(key) == dictionary_.end())\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+/**\n+ * \\fn YamlObject::getMemberNames()\n+ * \\brief Helper function to get all member names of the dictionary\n+ *\n+ * Helper function to get all member names of the dictionary.\n+ *\n+ * \\return A vector of string as the member names\n+ */\n+std::vector<std::string> YamlObject::getMemberNames() const\n+{\n+\tassert(type_ == DICTIONARY);\n+\n+\tstd::vector<std::string> memberNames;\n+\tfor (auto &[key, _] : dictionary_)\n+\t\tmemberNames.push_back(key);\n+\n+\treturn memberNames;\n+}\n+\n+/**\n+ * \\fn YamlObject::get()\n+ * \\brief Helper function to get a member by name from the dictionary\n+ *\n+ * Helper function to get a member by name from the dictionary\n+ *\n+ * \\return A YamlObject as a member\n+ */\n+const YamlObject &YamlObject::get(const std::string &key) const\n+{\n+\tassert(type_ == DICTIONARY);\n+\tassert(isMember(key));\n+\n+\tauto iter = dictionary_.find(key);\n+\treturn *iter->second;\n+}\n+\n+class YamlParser::Private : public Extensible::Private\n+{\n+\tLIBCAMERA_DECLARE_PUBLIC(YamlParser)\n+\n+public:\n+\tPrivate();\n+\t~Private();\n+\n+\tusing ItemParser = const std::function<int()> &;\n+\tusing RecordParser = const std::function<int(const std::string &)> &;\n+\n+\tint initParser(FILE *fh);\n+\tvoid release();\n+\n+\tint consumeDocumentStart();\n+\tint consumeDocumentEnd();\n+\n+\tint parseString(std::string &value);\n+\tint parseList(ItemParser parseItem);\n+\tint parseDictionary(RecordParser parseRecord);\n+\n+private:\n+\tint nextEvent(yaml_event_t &event);\n+\tint consume(yaml_event_type_t);\n+\n+\tint parseDictionaryOrList(bool isDictionary,\n+\t\t\t\t  const std::function<int()> &parseItem);\n+\n+\tbool nextEventLoaded_;\n+\tyaml_event_t nextEvent_;\n+\n+\tbool parserValid_;\n+\tyaml_parser_t parser_;\n+};\n+\n+/**\n+ * \\class YamlParser::Private\n+ * \\brief A Private class helps control event based parsing for yaml files.\n+ *\n+ * The YamlParser::Private class stores the internal yaml_parser_t and provides\n+ * helper functions to do event based parsing for yaml files.\n+ */\n+YamlParser::Private::Private()\n+\t: nextEventLoaded_(false), parserValid_(false)\n+{\n+}\n+\n+/**\n+ * \\class YamlParser::Private\n+ * \\brief Destructor of YamlParser::Private.\n+ */\n+YamlParser::Private::~Private()\n+{\n+\trelease();\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::initParser()\n+ * \\brief Initialize a parser with an opened file for parsing\n+ * \\param[in] fh The yaml file to parse\n+ *\n+ * Prior to parsing the yaml content, the YamlParser must be initialized with\n+ * an opened FILE to create an internal parser. The FILE need to stay valid\n+ * during the process. The release() should be called after use.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to initialize\n+ */\n+int YamlParser::Private::initParser(FILE *fh)\n+{\n+\t/* yaml_parser_initialize returns 1 when succeeded */\n+\tif (!yaml_parser_initialize(&parser_)) {\n+\t\tLOG(YamlParser, Error) << \"Failed to initialize yaml parser\";\n+\t\treturn -EINVAL;\n+\t}\n+\tparserValid_ = true;\n+\tyaml_parser_set_input_file(&parser_, fh);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::release()\n+ * \\brief Release the internal parser\n+ *\n+ * After parsing the yaml content, The YamlParser::release() should be called\n+ * to release the internal parser.\n+ */\n+void YamlParser::Private::release()\n+{\n+\tif (nextEventLoaded_) {\n+\t\tyaml_event_delete(&nextEvent_);\n+\t\tnextEventLoaded_ = false;\n+\t}\n+\n+\tif (parserValid_) {\n+\t\tparserValid_ = false;\n+\t\tyaml_parser_delete(&parser_);\n+\t}\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::consume()\n+ * \\brief Cosume the next event with an expected event type\n+ *\n+ * \\param[in] type The expected event type to consume\n+ *\n+ * Consume next event and check whether next event has an expected type.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to initialize\n+ */\n+int YamlParser::Private::consume(yaml_event_type_t type)\n+{\n+\tyaml_event_t event;\n+\tint ret = nextEvent(event);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (event.type != type) {\n+\t\tLOG(YamlParser, Error)\n+\t\t\t<< \"Expect event: \" << type\n+\t\t\t<< \" but get \" << event.type\n+\t\t\t<< \" at line: \" << event.start_mark.line\n+\t\t\t<< \" column: \" << event.start_mark.column;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tyaml_event_delete(&event);\n+\tnextEventLoaded_ = false;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\typedef YamlParser::Private::ItemParser\n+ * \\brief The functor to handle an item in a yaml list\n+ */\n+\n+/**\n+ * \\typedef YamlParser::Private::RecordParser\n+ * \\brief The functor to handle an item in a yaml dictionary\n+ */\n+\n+/**\n+ * \\fn YamlParser::Private::consumeDocumentStart()\n+ * \\brief Consume start of a yaml document\n+ *\n+ * Check yaml start of a yaml document. The function should be called and\n+ * checked before parsing any content of the yaml file.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to validate start of a yaml file\n+ */\n+int YamlParser::Private::consumeDocumentStart()\n+{\n+\tif (consume(YAML_STREAM_START_EVENT))\n+\t\treturn -EINVAL;\n+\n+\tif (consume(YAML_DOCUMENT_START_EVENT))\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::consumeDocumentEnd()\n+ * \\brief Consume end of a yaml document\n+ *\n+ * Check yaml end of a yaml document. The function should be called and\n+ * checked after parsing any content of the yaml file.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to validate end of a yaml file\n+ */\n+int YamlParser::Private::consumeDocumentEnd()\n+{\n+\tif (consume(YAML_DOCUMENT_END_EVENT))\n+\t\treturn -EINVAL;\n+\n+\tif (consume(YAML_STREAM_END_EVENT))\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::parseString()\n+ * \\brief Parse scalar and read its content as a string\n+ * \\param[in] value The string reference to fill value\n+ *\n+ * A helper function to peek the next event, check whether it's scalar type\n+ * and read its content as a string.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to initialize\n+ */\n+int YamlParser::Private::parseString(std::string &value)\n+{\n+\tyaml_event_t event;\n+\tint ret = nextEvent(event);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (event.type != YAML_SCALAR_EVENT) {\n+\t\tLOG(YamlParser, Error) << \"Expect scalar at line: \"\n+\t\t\t\t       << event.start_mark.line\n+\t\t\t\t       << \" column: \"\n+\t\t\t\t       << event.start_mark.column;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tvalue.assign(reinterpret_cast<char *>(event.data.scalar.value),\n+\t\t     event.data.scalar.length);\n+\n+\tconsume(YAML_SCALAR_EVENT);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::parseList()\n+ * \\brief Parse a list with an callback function to parse each item in the list\n+ * \\param[in] parseItem The functor to parse a single item in the list\n+ *\n+ * Start to parse a list from the current position and call back to parseItem\n+ * for parsing each item in the list. The parseItem should return 0 on success\n+ * or a negative error code otherwise.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to parse the list\n+ */\n+int YamlParser::Private::parseList(ItemParser parseItem)\n+{\n+\treturn parseDictionaryOrList(false, parseItem);\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::parseDictionary()\n+ * \\brief Parse a dictionary with an callback function to parse each item\n+ * \\param[in] parseRecord The functor to parse a single item in the dictionary\n+ *\n+ * Start to parse a dictionary from the current position and call back to\n+ * parseRecord to parse value by a string argument as the key to the value.\n+ * The parseRecord should return 0 on success or a negative error code\n+ * otherwise.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to parse the dictionary\n+ */\n+int YamlParser::Private::parseDictionary(RecordParser parseRecord)\n+{\n+\tauto parseItem = [this, &parseRecord]() {\n+\t\tstd::string key;\n+\t\tint ret = parseString(key);\n+\t\tif (ret)\n+\t\t\treturn -EINVAL;\n+\n+\t\treturn parseRecord(key);\n+\t};\n+\n+\treturn parseDictionaryOrList(true, parseItem);\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::parseDictionaryOrList()\n+ * \\brief A helper function to abstract common part of parsing dictionary or list\n+ *\n+ * \\param[in] isDictionary True for parsing a dictionary, and false for a list\n+ * \\param[in] parseItem The callback to handle an item\n+ *\n+ * A helper function to abstract parsing a item from a dictionary or a list.\n+ * The differences of them in a yaml event stream are:\n+ *\n+ * 1. The start and end event type are different\n+ * 2. There is a leading scalar string as key in the items of a dictionary\n+ *\n+ * The caller should handle the leading key string in its callback parseItem\n+ * when it's a dictionary.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to initialize\n+ */\n+int YamlParser::Private::parseDictionaryOrList(bool isDictionary,\n+\t\t\t\t\t       const std::function<int()> &parseItem)\n+{\n+\tyaml_event_type_t startEventType = YAML_SEQUENCE_START_EVENT;\n+\tyaml_event_type_t endEventType = YAML_SEQUENCE_END_EVENT;\n+\n+\tif (isDictionary) {\n+\t\tstartEventType = YAML_MAPPING_START_EVENT;\n+\t\tendEventType = YAML_MAPPING_END_EVENT;\n+\t}\n+\n+\tif (consume(startEventType))\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * Add a safety counter to make sure we don't loop indefinitely in case\n+\t * the configuration file is malformed.\n+\t */\n+\tunsigned int sentinel = 1000;\n+\tyaml_event_t event;\n+\tdo {\n+\t\tint ret = nextEvent(event);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (event.type == endEventType)\n+\t\t\treturn consume(endEventType);\n+\n+\t\tret = parseItem();\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\t--sentinel;\n+\t} while (sentinel);\n+\n+\tif (!sentinel)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParser::Private::nextEvent()\n+ * \\brief Peek the next event\n+ *\n+ * \\param[in] event The event reference to fill information\n+ *\n+ * Peek the next event in the current yaml event stream, and return -EINVAL when\n+ * there is no more event.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser is failed to initialize\n+ */\n+int YamlParser::Private::nextEvent(yaml_event_t &event)\n+{\n+\tif (nextEventLoaded_) {\n+\t\tevent = nextEvent_;\n+\t\treturn 0;\n+\t}\n+\n+\t/* yaml_parser_parse returns 1 when succeeded */\n+\tif (1 != yaml_parser_parse(&parser_, &nextEvent_)) {\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tnextEventLoaded_ = true;\n+\tevent = nextEvent_;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\class YamlParser\n+ * \\brief A helper class for parsing yaml files.\n+ *\n+ * The YamlParser class eases handling of parsing a yaml file by providing\n+ * helper function to extract content yaml files into a tree base yaml object.\n+ *\n+ * Example usage 1:\n+ * The following code illustrates how to parse the following yaml file:\n+ *\n+ * name:\n+ * \t\"John\"\n+ * numbers:\n+ * \t- 1\n+ * \t- 2\n+ *\n+ * @code\n+ *\n+ *\tYamlObject root;\n+ *\tYamlParser yamlParser;\n+ *\tif (yamlParser.ParseAsYamlObject(fh, root));\n+ *\t\treturn;\n+ *\n+ *\tif (!root.isDictionary())\n+ *\t\treturn;\n+ *\n+ *\tstd::string name = root.get(\"name\");\n+ *\tcout << name.asString();\n+ *\n+ *\tconst YamlObject &numbers = root.get(\"numbers\");\n+ *\tif (!numbers.isList())\n+ *\t\treturn;\n+ *\n+ *\tfor (int i = 0; i < numbers.size; i++)\n+ *\t\tcout << numbers[i].asInt();\n+ *\n+ * @endcode\n+ *\n+ * Function ParseAsYamlObject(FILE *, YamlObject &) accept an opened FILE and\n+ * initialize an internal parser. The FILE need to stay valid during function\n+ * call.\n+ */\n+\n+/**\n+ * \\brief Construct a YamlParser\n+ */\n+YamlParser::YamlParser()\n+\t: Extensible(std::make_unique<Private>())\n+{\n+}\n+\n+/**\n+ * \\fn YamlParser::ParseAsYamlObject()\n+ * \\brief Parse a yaml file as a YamlObject\n+ * \\param[in] fh The yaml file to parse\n+ * \\param[in] yamlObject The result fo YamlObject\n+ *\n+ * Prior to parsing the yaml content, the function accepts an opened FILE to\n+ * create an internal parser. The FILE need to stay valid during the function\n+ * call. When fails, the contect of the YamlObject is undefined.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL Fail to parse the yaml file.\n+ */\n+int YamlParser::ParseAsYamlObject(FILE *fh, YamlObject &yamlObject)\n+{\n+\tif (_d()->initParser(fh))\n+\t\treturn -EINVAL;\n+\n+\tif (_d()->consumeDocumentStart())\n+\t\tgoto error;\n+\n+\tif (ParseNextYamlObject(yamlObject))\n+\t\tgoto error;\n+\n+\tif (_d()->consumeDocumentEnd())\n+\t\tgoto error;\n+\n+\t_d()->release();\n+\treturn 0;\n+\n+error:\n+\t_d()->release();\n+\treturn -EINVAL;\n+}\n+\n+/**\n+ * \\fn YamlParser::ParseNextEvent()\n+ * \\brief Helper function to parse next yaml event and read it as a YamlObject\n+ * \\param[in] yamlObject The result of YamlObject\n+ *\n+ * A helper function to parse next yaml event by peeking next event and parse\n+ * them separately as a value, list or dictionary.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL Fail to parse the yaml file.\n+ */\n+int YamlParser::ParseNextYamlObject(YamlObject &yamlObject)\n+{\n+\tyaml_event_t event;\n+\n+\tif (_d()->nextEvent(event))\n+\t\treturn -EINVAL;\n+\n+\tif (event.type == YAML_SCALAR_EVENT) {\n+\t\tyamlObject.type_ = YamlObject::VALUE;\n+\t\t_d()->parseString(yamlObject.value_);\n+\t\treturn 0;\n+\t}\n+\n+\tif (event.type == YAML_SEQUENCE_START_EVENT) {\n+\t\tyamlObject.type_ = YamlObject::LIST;\n+\t\tauto &list = yamlObject.list_;\n+\t\tauto handler = [this, &list]() {\n+\t\t\tlist.emplace_back(new YamlObject());\n+\t\t\treturn ParseNextYamlObject(*list.back());\n+\t\t};\n+\t\treturn _d()->parseList(handler);\n+\t}\n+\n+\tif (event.type == YAML_MAPPING_START_EVENT) {\n+\t\tyamlObject.type_ = YamlObject::DICTIONARY;\n+\t\tauto &dictionary = yamlObject.dictionary_;\n+\t\tauto handler = [this, &dictionary](const std::string &key) {\n+\t\t\tdictionary[key].reset(new YamlObject());\n+\t\t\treturn ParseNextYamlObject(*dictionary[key]);\n+\t\t};\n+\t\treturn _d()->parseDictionary(handler);\n+\t}\n+\n+\tLOG(YamlParser, Error) << \"Invalid yaml file\";\n+\treturn -EINVAL;\n+}\n+\n+} /* namespace libcamera */\n","prefixes":["libcamera-devel","3/9"]}