Patch Detail
Show a patch.
GET /api/patches/15705/?format=api
{ "id": 15705, "url": "https://patchwork.libcamera.org/api/patches/15705/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15705/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20220425144617.2549778-2-hanlinchen@chromium.org>", "date": "2022-04-25T14:46:15", "name": "[libcamera-devel,v5,1/3] libcamera: Introduce YamlParser as a helper to parse yaml files", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "5090ef1532f8372aa6fc2cdcf86184f6d602ec5b", "submitter": { "id": 98, "url": "https://patchwork.libcamera.org/api/people/98/?format=api", "name": "Hanlin Chen", "email": "hanlinchen@chromium.org" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/15705/mbox/", "series": [ { "id": 3068, "url": "https://patchwork.libcamera.org/api/series/3068/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3068", "date": "2022-04-25T14:46:14", "name": "Introduce YamlParser for YAML files", "version": 5, "mbox": "https://patchwork.libcamera.org/series/3068/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15705/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15705/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 7872BC3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 25 Apr 2022 14:46:29 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 297DD6564B;\n\tMon, 25 Apr 2022 16:46:29 +0200 (CEST)", "from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com\n\t[IPv6:2607:f8b0:4864:20::1032])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7A6876042F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 25 Apr 2022 16:46:26 +0200 (CEST)", "by mail-pj1-x1032.google.com with SMTP id p6so125612pjm.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 25 Apr 2022 07:46:26 -0700 (PDT)", "from localhost ([2401:fa00:1:17:38a2:b0a:67fa:9b05])\n\tby smtp.gmail.com with UTF8SMTPSA id\n\tf17-20020a17090a4a9100b001cd4989fed3sm15721468pjh.31.2022.04.25.07.46.23\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tMon, 25 Apr 2022 07:46:24 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1650897989;\n\tbh=KtbKn3u8HQLKJMWT5quiykYi3GhI4pmNNXQ/H8Pv5Sc=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=mXrOHkAy4TPxcgSL02++HEf8XN0OzQQKppubhf4svQPc8grGxj/FewGR7zrCTk1t7\n\tBYgRucqvrmQY5ULyiWl/6uyH7gvERbJ/JYxZVvVZ6LaUGNP4JllP4guSu/7wJjrTr4\n\tdDjBsntP9lax6+MoEwbXZhj16wTxdkgbb7VvmFibW78Yu3piX5qb8G0hR4JwF/01sn\n\toN+n+5Np+ZL9GPoGf5UjorX0qzr9hHMMclWpOrVGRF7z//cTNekVr9T7IjADgcB/Yd\n\tEawgOhA3006LSe/YGDmdkaQqPfGehyj0BHFkf3IAnVj5qASUwoYP+UuuTgUHhsvTH+\n\t3JqUpiJzESffg==", "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=LLtWqTGmztrFnijO8aZky7tJrg38aT3QTOa7+y710rs=;\n\tb=lgiHvO23OQv/xmlk4e/fcldjnvxnqU+YCn5nz5EUSEFA1xphOBGnomsmkfoQt2DdBx\n\thB177U3i9l6lCRah3SvFLHcByMem5tddoKKsW1u4s/YAZJ5y/cm6z8ysEu3hj95X9eYX\n\tgTxpoofq7RyoixvXhRo6dn9ou4mT/qFDEL2ds=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"lgiHvO23\"; \n\tdkim-atps=neutral", "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=LLtWqTGmztrFnijO8aZky7tJrg38aT3QTOa7+y710rs=;\n\tb=W+jO0ZBF/2NS9/LZO8u4+0+v4sGBKIfB5/5vSZb9lTgFoZZp2bFnkq0khK1NE1Ti0Y\n\tNyPDS+YTJMtYHllYevmbfRBPheD5k7ugtbtRbjCaezMZr4EBWE6uowO3hCBEVwlJJYhR\n\t3ZPwYq2Vg+5VSrZ7vM96EAtBLysDY57DAySXXpm6sVZmnM67puagvz91KX8n8pb+0e+a\n\tkfA0qHCxMmRvkn0PQohYFwBSMoN6kkTv/OGFtZW8KLHkho+EjhyBuLGlFFbczKiOu/L4\n\tTz6x/8GUCyz6R+Ja8QZj7Iqltsly41i7B+vAPISvU5LiLMiNJFurKo2LyVehuAXoBDBb\n\tg+4w==", "X-Gm-Message-State": "AOAM530KFvrULRfH4iOKvpqJ3Tv5oM74HSkuClgvk2whCVuTYj1nPzvu\n\t3w2ySH9wjSEGBf22X5Q8EMN6hafn2pf1Dw==", "X-Google-Smtp-Source": "ABdhPJxh4YXJDLr6tzLezHbYiciaiIuW3yqhCqkKjOBRlTvhnRm9jiFV0KmbXLlJatiooxesWsJiAw==", "X-Received": "by 2002:a17:903:22d2:b0:15a:805d:3b16 with SMTP id\n\ty18-20020a17090322d200b0015a805d3b16mr18287708plg.173.1650897984429; \n\tMon, 25 Apr 2022 07:46:24 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Mon, 25 Apr 2022 22:46:15 +0800", "Message-Id": "<20220425144617.2549778-2-hanlinchen@chromium.org>", "X-Mailer": "git-send-email 2.36.0.rc2.479.g8af0fa9b8e-goog", "In-Reply-To": "<20220425144617.2549778-1-hanlinchen@chromium.org>", "References": "<20220425144617.2549778-1-hanlinchen@chromium.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v5 1/3] libcamera: Introduce YamlParser as\n\ta helper 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>", "From": "Han-Lin Chen via libcamera-devel <libcamera-devel@lists.libcamera.org>", "Reply-To": "Han-Lin Chen <hanlinchen@chromium.org>", "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 | 4 +-\n include/libcamera/internal/meson.build | 1 +\n include/libcamera/internal/yaml_parser.h | 87 +++\n src/libcamera/meson.build | 3 +\n src/libcamera/yaml_parser.cpp | 679 +++++++++++++++++++++++\n 5 files changed, 772 insertions(+), 2 deletions(-)\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 aae6b79f..b5a2d448 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\n@@ -98,7 +98,7 @@ for tracing with lttng: [optional]\n liblttng-ust-dev python3-jinja2 lttng-tools\n \n for android: [optional]\n- libexif-dev libjpeg-dev libyaml-dev\n+ libexif-dev libjpeg-dev\n \n for lc-compliance: [optional]\n libevent-dev\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..99f9eb17\n--- /dev/null\n+++ b/include/libcamera/internal/yaml_parser.h\n@@ -0,0 +1,87 @@\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 <stdio.h>\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 YamlParserContext;\n+\n+class YamlObject\n+{\n+public:\n+\tYamlObject();\n+\t~YamlObject();\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+#ifndef __DOXYGEN__\n+\ttemplate<typename T,\n+\t\t typename std::enable_if_t<\n+\t\t\t std::is_same<bool, T>::value ||\n+\t\t\t std::is_same<double, T>::value ||\n+\t\t\t std::is_same<int32_t, T>::value ||\n+\t\t\t std::is_same<uint32_t, T>::value ||\n+\t\t\t std::is_same<std::string, T>::value ||\n+\t\t\t std::is_same<Size, T>::value> * = nullptr>\n+#else\n+\ttemplate<typename T>\n+#endif\n+\tT get(const T &defaultValue, bool *ok = nullptr) const;\n+\n+\tstd::size_t size() const;\n+\tconst YamlObject &operator[](std::size_t index) const;\n+\n+\tbool contains(const std::string &key) const;\n+\tconst YamlObject &operator[](const std::string &key) const;\n+\tstd::vector<std::string> getMemberNames() const;\n+\n+private:\n+\tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n+\n+\tfriend class YamlParserContext;\n+\n+\tenum Type {\n+\t\tDictionary,\n+\t\tList,\n+\t\tValue,\n+\t};\n+\n+\tType 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\n+{\n+public:\n+\tstatic std::unique_ptr<YamlObject> parse(std::FILE *fh);\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..43b83b8f\n--- /dev/null\n+++ b/src/libcamera/yaml_parser.cpp\n@@ -0,0 +1,679 @@\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 <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+/* Empty static YamlObject as a safe result for invalid operations */\n+static const YamlObject empty;\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 YAML content. A\n+ * YamlObject can be a dictionary or list of YamlObjects or a value if a tree\n+ * leaf.\n+ */\n+YamlObject::YamlObject()\n+\t: type_(Value)\n+{\n+}\n+\n+/**\n+ * \\class YamlObject\n+ * \\brief Destructor of YamlObject\n+ */\n+YamlObject::~YamlObject() = default;\n+\n+/**\n+ * \\fn YamlObject::isValue()\n+ * \\brief Return whether the YamlObject is a value\n+ *\n+ * \\return True if the YamlObject is a value, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::isList()\n+ * \\brief Return whether the YamlObject is a list\n+ *\n+ * \\return True if the YamlObject is a list, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::isDictionary()\n+ * \\brief Return whether the YamlObject is a dictionary\n+ *\n+ * \\return True if the YamlObject is a dictionary, false otherwise\n+ */\n+\n+/**\n+ * \\fn template<typename T> YamlObject::get<T>(\n+ *\tconst T &defaultValue, bool *ok) const\n+ * \\brief Parse the YamlObject as a \\a T value\n+ * \\param[in] defaultValue The default value when failing to parse\n+ * \\param[out] ok The result of whether the parse succeeded\n+ *\n+ * This function parses the value of the YamlObject as a \\a T object, and\n+ * returns the value. If parsing fails (usually because the YamlObject doesn't\n+ * store a \\a T value), the \\a defaultValue is returned, and \\a ok is set to\n+ * false. Otherwise, the YamlObject value is returned, and \\a ok is set to true.\n+ *\n+ * The \\a ok pointer is optional and can be a nullptr if the caller doesn't\n+ * need to know if parsing succeeded.\n+ *\n+ * \\return Value as a bool type\n+ */\n+\n+#ifndef __DOXYGEN__\n+\n+template<>\n+bool YamlObject::get(const bool &defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != Value)\n+\t\treturn defaultValue;\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+template<>\n+int32_t YamlObject::get(const int32_t &defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != Value)\n+\t\treturn defaultValue;\n+\n+\tif (value_ == \"\")\n+\t\treturn defaultValue;\n+\n+\tchar *end;\n+\n+\terrno = 0;\n+\tint32_t value = std::strtol(value_.c_str(), &end, 10);\n+\n+\tif ('\\0' != *end || errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+template<>\n+uint32_t YamlObject::get(const uint32_t &defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != Value)\n+\t\treturn defaultValue;\n+\n+\tif (value_ == \"\")\n+\t\treturn defaultValue;\n+\n+\t/*\n+\t * strtoul accept a leading minus sign, and the calculated digits\n+\t * is negated as if by unary minus. Rule out the case.\n+\t */\n+\tstd::size_t found = value_.find_first_not_of(\" \\t\");\n+\tif (found != std::string::npos && value_[found] == '-')\n+\t\treturn defaultValue;\n+\n+\tchar *end;\n+\n+\terrno = 0;\n+\tuint32_t value = std::strtoul(value_.c_str(), &end, 10);\n+\n+\tif ('\\0' != *end || errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+template<>\n+double YamlObject::get(const double &defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != Value)\n+\t\treturn defaultValue;\n+\n+\tif (value_ == \"\")\n+\t\treturn defaultValue;\n+\n+\tchar *end;\n+\n+\terrno = 0;\n+\tdouble value = std::strtod(value_.c_str(), &end);\n+\n+\tif ('\\0' != *end || errno == ERANGE)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value;\n+}\n+\n+template<>\n+std::string YamlObject::get(const std::string &defaultValue, bool *ok) const\n+{\n+\tsetOk(ok, false);\n+\n+\tif (type_ != Value)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn value_;\n+}\n+\n+template<>\n+Size YamlObject::get(const 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+\t/*\n+\t * Add a local variable to validate of each dimension in case\n+\t * that ok == nullptr.\n+\t */\n+\tbool valid;\n+\tuint32_t width = list_[0]->get<uint32_t>(0, &valid);\n+\tif (!valid)\n+\t\treturn defaultValue;\n+\n+\tuint32_t height = list_[1]->get<uint32_t>(0, &valid);\n+\tif (!valid)\n+\t\treturn defaultValue;\n+\n+\tsetOk(ok, true);\n+\treturn Size(width, height);\n+}\n+\n+#endif /* __DOXYGEN__ */\n+\n+/**\n+ * \\fn YamlObject::size()\n+ * \\brief Retrieve the number of elements in a list YamlObject\n+ *\n+ * This function retrieves the size of the YamlObject, defined as the number of\n+ * child elements it contains. Only YamlObject instances of list type have a\n+ * size, calling this function on other types of instances is invalid and\n+ * results in undefined behaviour.\n+ *\n+ * \\return The size of the YamlObject\n+ */\n+std::size_t YamlObject::size() const\n+{\n+\tif (type_ != List)\n+\t\treturn 0;\n+\n+\treturn list_.size();\n+}\n+\n+/**\n+ * \\fn YamlObject::operator[](std::size_t index) const\n+ * \\brief Retrieve the element from list YamlObject by index\n+ *\n+ * This function retrieves an element of the YamlObject. Only YamlObject\n+ * instances of list type associate elements with index, calling this function\n+ * on other types of instances is invalid and results in undefined behaviour.\n+ *\n+ * \\return The YamlObject as an element of the list\n+ */\n+const YamlObject &YamlObject::operator[](std::size_t index) const\n+{\n+\tif (type_ != List || index >= size())\n+\t\treturn empty;\n+\n+\treturn *list_[index];\n+}\n+\n+/**\n+ * \\fn YamlObject::contains()\n+ * \\brief Check if an element of a dictionary exists\n+ *\n+ * This function check if the YamlObject contains an element. Only YamlObject\n+ * instances of dictionary type associate elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\n+ *\n+ * \\return True if an element exists, false otherwise\n+ */\n+bool YamlObject::contains(const std::string &key) const\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 Retrieve all member names of the dictionary\n+ *\n+ * This function retrieve member names of a YamlObject. Only YamlObject\n+ * instances of dictionary type associate elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\n+ *\n+ * \\todo Replace this function with an iterator-based API\n+ *\n+ * \\return A vector of string as the member names\n+ */\n+std::vector<std::string> YamlObject::getMemberNames() const\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::operator[](const std::string &key) const\n+ * \\brief Retrieve a member by name from the dictionary\n+ *\n+ * This function retrieve a member of a YamlObject by name. Only YamlObject\n+ * instances of dictionary type associate elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\n+ *\n+ * \\return The YamlObject corresponding to the \\a key member\n+ */\n+const YamlObject &YamlObject::operator[](const std::string &key) const\n+{\n+\tif (type_ != Dictionary || !contains(key))\n+\t\treturn empty;\n+\n+\tauto iter = dictionary_.find(key);\n+\treturn *iter->second;\n+}\n+\n+#ifndef __DOXYGEN__\n+\n+class YamlParserContext\n+{\n+public:\n+\tYamlParserContext();\n+\t~YamlParserContext();\n+\n+\tint init(std::FILE *fh);\n+\tint parseContent(YamlObject &yamlObject);\n+\n+private:\n+\tstruct EventDeleter {\n+\t\tvoid operator()(yaml_event_t *event) const\n+\t\t{\n+\t\t\tyaml_event_delete(event);\n+\t\t\tdelete event;\n+\t\t}\n+\t};\n+\tusing EventPtr = std::unique_ptr<yaml_event_t, EventDeleter>;\n+\n+\tEventPtr getEvent();\n+\n+\tvoid readValue(std::string &value, EventPtr event);\n+\tint parseDictionaryOrList(bool isDictionary,\n+\t\t\t\t const std::function<int(EventPtr event)> &parseItem);\n+\tint parseNextYamlObject(YamlObject &yamlObject, EventPtr event);\n+\n+\tbool parserValid_;\n+\tyaml_parser_t parser_;\n+};\n+\n+/**\n+ * \\class YamlParserContext\n+ * \\brief Class for YamlParser parsing and context data\n+ *\n+ * The YamlParserContext class stores the internal yaml_parser_t and provides\n+ * helper functions to do event-based parsing for YAML files.\n+ */\n+YamlParserContext::YamlParserContext()\n+\t: parserValid_(false)\n+{\n+}\n+\n+/**\n+ * \\class YamlParserContext\n+ * \\brief Destructor of YamlParserContext\n+ */\n+YamlParserContext::~YamlParserContext()\n+{\n+\tif (parserValid_) {\n+\t\tyaml_parser_delete(&parser_);\n+\t\tparserValid_ = false;\n+\t}\n+}\n+\n+/**\n+ * \\fn YamlParserContext::init()\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 YamlParserContext must be initialized\n+ * with an opened FILE to create an internal parser. The FILE needs to stay\n+ * valid during the process.\n+ *\n+ * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EINVAL The parser has failed to initialize\n+ */\n+int YamlParserContext::init(std::FILE *fh)\n+{\n+\t/* yaml_parser_initialize returns 1 when it succeededs */\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 YamlParserContext::getEvent()\n+ * \\brief Get the next event\n+ *\n+ * Get the next event in the current YAML event stream, and return nullptr when\n+ * there is no more event.\n+ *\n+ * \\return EventPtr on success or nullptr otherwise\n+ */\n+YamlParserContext::EventPtr YamlParserContext::getEvent()\n+{\n+\tstatic yaml_event_t event;\n+\n+\t/* yaml_parser_parse returns when it succeeds */\n+\tif (1 != yaml_parser_parse(&parser_, &event)) {\n+\t\treturn nullptr;\n+\t}\n+\n+\treturn EventPtr(new yaml_event_t(event));\n+}\n+\n+/**\n+ * \\fn YamlParserContext::parseContent()\n+ * \\brief Parse the content of a YAML document\n+ * \\param[in] yamlObject The result of YamlObject\n+ *\n+ * Check YAML start and end events of a YAML document, and parse the root object\n+ * of the YAML document into a YamlObject.\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 YamlParserContext::parseContent(YamlObject &yamlObject)\n+{\n+\t/* Check start of the YAML file */\n+\tEventPtr event = getEvent();\n+\tif (!event || event->type != YAML_STREAM_START_EVENT)\n+\t\treturn -EINVAL;\n+\n+\tevent = getEvent();\n+\tif (!event || event->type != YAML_DOCUMENT_START_EVENT)\n+\t\treturn -EINVAL;\n+\n+\t/* Parse the root object */\n+\tevent = getEvent();\n+\tif (parseNextYamlObject(yamlObject, std::move(event)))\n+\t\treturn -EINVAL;\n+\n+\t/* Check end of the YAML file */\n+\tevent = getEvent();\n+\tif (!event || event->type != YAML_DOCUMENT_END_EVENT)\n+\t\treturn -EINVAL;\n+\n+\tevent = getEvent();\n+\tif (!event || event->type != YAML_STREAM_END_EVENT)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn YamlParserContext::readValue()\n+ * \\brief Parse event scalar and fill its content into a string\n+ * \\param[in] value The string reference to fill value\n+ *\n+ * A helper function to parse a scalar event as string. The caller need to\n+ * guarantee the event is of scaler type.\n+ */\n+void YamlParserContext::readValue(std::string &value, EventPtr event)\n+{\n+\tvalue.assign(reinterpret_cast<char *>(event->data.scalar.value),\n+\t\t event->data.scalar.length);\n+}\n+\n+/**\n+ * \\fn YamlParserContext::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 YamlParserContext::parseDictionaryOrList(bool isDictionary,\n+\t\t\t\t\t const std::function<int(EventPtr event)> &parseItem)\n+{\n+\tyaml_event_type_t endEventType = YAML_SEQUENCE_END_EVENT;\n+\tif (isDictionary)\n+\t\tendEventType = YAML_MAPPING_END_EVENT;\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+\tdo {\n+\t\tauto evt = getEvent();\n+\t\tif (!evt)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (evt->type == endEventType)\n+\t\t\treturn 0;\n+\n+\t\tint ret = parseItem(std::move(evt));\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 YamlParserContext::parseNextYamlObject()\n+ * \\brief Parse next YAML event and read it as a YamlObject\n+ * \\param[in] yamlObject The result of YamlObject\n+ * \\param[in] event The leading event of the object\n+ *\n+ * Parse next YAML object 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 YamlParserContext::parseNextYamlObject(YamlObject &yamlObject, EventPtr event)\n+{\n+\tif (!event)\n+\t\treturn -EINVAL;\n+\n+\tswitch (event->type) {\n+\tcase YAML_SCALAR_EVENT:\n+\t\tyamlObject.type_ = YamlObject::Value;\n+\t\treadValue(yamlObject.value_, std::move(event));\n+\t\treturn 0;\n+\n+\tcase YAML_SEQUENCE_START_EVENT: {\n+\t\tyamlObject.type_ = YamlObject::List;\n+\t\tauto &list = yamlObject.list_;\n+\t\tauto handler = [this, &list](EventPtr evt) {\n+\t\t\tlist.emplace_back(new YamlObject());\n+\t\t\treturn parseNextYamlObject(*list.back(), std::move(evt));\n+\t\t};\n+\t\treturn parseDictionaryOrList(false, handler);\n+\t}\n+\n+\tcase YAML_MAPPING_START_EVENT: {\n+\t\tyamlObject.type_ = YamlObject::Dictionary;\n+\t\tauto &dictionary = yamlObject.dictionary_;\n+\t\tauto handler = [this, &dictionary](EventPtr evtKey) {\n+\t\t\t/* Parse key */\n+\t\t\tif (evtKey->type != YAML_SCALAR_EVENT) {\n+\t\t\t\tLOG(YamlParser, Error) << \"Expect key at line: \"\n+\t\t\t\t\t\t << evtKey->start_mark.line\n+\t\t\t\t\t\t << \" column: \"\n+\t\t\t\t\t\t << evtKey->start_mark.column;\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\n+\t\t\tstd::string key;\n+\t\t\treadValue(key, std::move(evtKey));\n+\n+\t\t\t/* Parse value */\n+\t\t\tEventPtr evtValue = getEvent();\n+\t\t\tif (!evtValue)\n+\t\t\t\treturn -EINVAL;\n+\n+\t\t\tdictionary[key].reset(new YamlObject());\n+\t\t\treturn parseNextYamlObject(*dictionary[key], std::move(evtValue));\n+\t\t};\n+\t\treturn parseDictionaryOrList(true, handler);\n+\t}\n+\tdefault:\n+\t\tLOG(YamlParser, Error) << \"Invalid YAML file\";\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+#endif /* __DOXYGEN__ */\n+\n+/**\n+ * \\class YamlParser\n+ * \\brief A helper class for parsing YAML file\n+ *\n+ * The YamlParser class provides an easy interface to parse the contents of a\n+ * YAML file int a tree of YamlObject instances.\n+ *\n+ * Example usage:\n+ *\n+ * \\code{.unparsed}\n+ *\n+ * name:\n+ * \t\"John\"\n+ * numbers:\n+ * \t- 1\n+ * \t- 2\n+ *\n+ * \\endcode\n+ *\n+ * The following code illustrates how to parse the above YAML file:\n+ *\n+ * \\code{.cpp}\n+ *\n+ * std::unique_ptr<YamlObject> root = YamlParser::parse(fh);\n+ * if (!root)\n+ * return;\n+ *\n+ * if (!root->isDictionary())\n+ * return;\n+ *\n+ * const YamlObject &name = (*root)[\"name\"];\n+ * std::cout << name.get<std::string>(\"\");\n+ *\n+ * const YamlObject &numbers = (*root)[\"numbers\"];\n+ * if (!numbers.isList())\n+ * return;\n+ *\n+ * for (std::size_t i = 0; i < numbers.size(); i++)\n+ * std::cout << numbers[i].get<int32_t>(0);\n+ *\n+ * \\endcode\n+ *\n+ * The YamlParser::parse() function takes open FILE, parses its contents, and\n+ * returns a pointer to a YamlObject corresponding to the root node of the YAML\n+ * document.\n+ */\n+\n+/**\n+ * \\fn YamlParser::parse()\n+ * \\brief Parse a YAML file as a YamlObject\n+ * \\param[in] fh The YAML file to parse\n+ *\n+ * The YamlParser::parse() function takes open FILE, parses its contents, and\n+ * returns a pointer to a YamlObject corresponding to the root node of the YAML\n+ * document.\n+ *\n+ * \\return Pointer to result YamlObject on success or nullptr otherwise\n+ */\n+std::unique_ptr<YamlObject> YamlParser::parse(std::FILE *fh)\n+{\n+\tYamlParserContext context;\n+\n+\tif (context.init(fh))\n+\t\treturn nullptr;\n+\n+\tstd::unique_ptr<YamlObject> root(new YamlObject());\n+\n+\tif (context.parseContent(*root))\n+\t\treturn nullptr;\n+\n+\treturn root;\n+}\n+\n+} /* namespace libcamera */\n", "prefixes": [ "libcamera-devel", "v5", "1/3" ] }