Patch Detail
Show a patch.
GET /api/patches/15686/?format=api
{ "id": 15686, "url": "https://patchwork.libcamera.org/api/patches/15686/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15686/", "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": "<20220420134119.3306759-1-hanlinchen@chromium.org>", "date": "2022-04-20T13:41:18", "name": "[libcamera-devel,v4,1/2] libcamera: Introduce YamlParser as a helper to parse yaml files", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "45c0f451f5a4b2e55c9e88c4613bea3e9d093111", "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/15686/mbox/", "series": [ { "id": 3059, "url": "https://patchwork.libcamera.org/api/series/3059/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3059", "date": "2022-04-20T13:41:18", "name": "[libcamera-devel,v4,1/2] libcamera: Introduce YamlParser as a helper to parse yaml files", "version": 4, "mbox": "https://patchwork.libcamera.org/series/3059/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15686/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15686/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 9E2C2C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 20 Apr 2022 13:41:26 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BFB2B65643;\n\tWed, 20 Apr 2022 15:41:25 +0200 (CEST)", "from mail-pf1-x429.google.com (mail-pf1-x429.google.com\n\t[IPv6:2607:f8b0:4864:20::429])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 660D1604AB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 20 Apr 2022 15:41:24 +0200 (CEST)", "by mail-pf1-x429.google.com with SMTP id p3so623021pfw.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 20 Apr 2022 06:41:24 -0700 (PDT)", "from localhost ([2401:fa00:1:17:e17d:5346:b70d:ea81])\n\tby smtp.gmail.com with UTF8SMTPSA id\n\ta38-20020a056a001d2600b004fae885424dsm21323904pfx.72.2022.04.20.06.41.20\n\tfor <libcamera-devel@lists.libcamera.org>\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tWed, 20 Apr 2022 06:41:21 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1650462085;\n\tbh=4eDj4N2dIYRjUbJy5N+7e+VAb1+dS0RHgQVzHLXa6N0=;\n\th=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post:\n\tList-Help:List-Subscribe:From:Reply-To:From;\n\tb=olCXFbQBzmXFXcialY1eyupHYuesLiCSEct+LEaxK4moTSsRMoz/g2K1a7JUzTr7H\n\tg0HLfWdvEgCsgg9U16vUotHhYXiu3WqGxMUlNlLtkQ4u7pdXImalicX7vbBBjrkfQV\n\tqBJuuYKgpgYR/SS1xBsjX18JMdYNURhmY1frauymwDdJ8/MKtyFUFAFyRAvQ92LZMX\n\tmt7oTI8pu2xOrvCLvPDxNNLbZgojedoA5Pzphn9Qce21jsdm1+y1/9sIzUvXKlHAi8\n\tPnQLWNUtE/M5nmpZZCwZBeghPNth7NVyccCoH1MxwORFIJkxAnyxBBkYQwba3bv+Nq\n\tcNMLifoujmQoQ==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; h=from:to:subject:date:message-id:mime-version\n\t:content-transfer-encoding;\n\tbh=u7a8kzI9uC2kZKlVWuygbSCH8UokxqKHYOvsM1IbR28=;\n\tb=MH200V4BIzhNhbO2CkN8Qtp9qaY14282+s11J3gANWOLMDi4u0wSU8rJ8djyS2cNXo\n\tT3IpzGXhZTgJDrDB5lMRGnwsncmpRPPm8piS/HhcbfAKiHIYOFDppGhCeDegg7sCG2f0\n\tkskXcc2Zv0ihN4n14W4wrCgvj+aIZ+YFc9M3M=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"MH200V4B\"; \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:subject:date:message-id:mime-version\n\t:content-transfer-encoding;\n\tbh=u7a8kzI9uC2kZKlVWuygbSCH8UokxqKHYOvsM1IbR28=;\n\tb=QgiygjIvlPzDYJsV4+VQ2uWtyBr90JCGA33Z+K41TzubA8z4hJbtAFmtYLfHUKCnCX\n\tU763j/mPLB+5Da4EWUA+QUJHw0haUMd+fnRiAqSdhAk5pQobJ6NE2BUWo+7Mg6Xlc9eG\n\tdRRGJu11f0wacHv8l+lCCSIncitb+RADVRIT1Zgjrso4OProac+eKMdqtz5fpJ/YL3vD\n\t2w+x1yr7XvDQtL3q8JiTyo6MQ07XkSm5RQEZZDD3iz1irJxxBhJu3Fy4UAgU4rQhFBs3\n\tpSLWgzpf9Psbj5RLs2/OinWNvgyVT6H3Ta0a1o4HnOokU6/BtVwuqIowx1uBaX/KDBEc\n\topWQ==", "X-Gm-Message-State": "AOAM530o5n9pjx/dPRRwneu0XA1pAuogeKWNVChIBfHx7TW0wnKq9JuE\n\tRwddPC2Wo6ju5fF4BO8XlmwMad4iktZkYw==", "X-Google-Smtp-Source": "ABdhPJxbqO32UGwLSrHeCyLgJ/ymHggprmljar3oITWu/26S4f3KQ5kHDpvDN/4wkVY2vxEvdDX9Tw==", "X-Received": "by 2002:a63:78ca:0:b0:398:ae5:6515 with SMTP id\n\tt193-20020a6378ca000000b003980ae56515mr19497821pgc.345.1650462082000; \n\tWed, 20 Apr 2022 06:41:22 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 20 Apr 2022 21:41:18 +0800", "Message-Id": "<20220420134119.3306759-1-hanlinchen@chromium.org>", "X-Mailer": "git-send-email 2.36.0.rc0.470.gd361397f0d-goog", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v4 1/2] 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 | 794 +++++++++++++++++++++++\n 5 files changed, 887 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..abc0c6c3\n--- /dev/null\n+++ b/src/libcamera/yaml_parser.cpp\n@@ -0,0 +1,794 @@\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 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() = default;\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+\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+\tassert(type_ == List);\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+\tassert(type_ == List);\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+\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 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+\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::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+\tassert(type_ == Dictionary);\n+\tassert(contains(key));\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+\tusing ItemParser = const std::function<int()> &;\n+\tusing RecordParser = const std::function<int(const std::string &)> &;\n+\n+\tint init(std::FILE *fh);\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+\tint parseNextYamlObject(YamlObject &yamlObject);\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 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: nextEventLoaded_(false), parserValid_(false)\n+{\n+}\n+\n+/**\n+ * \\class YamlParserContext\n+ * \\brief Destructor of YamlParserContext\n+ */\n+YamlParserContext::~YamlParserContext()\n+{\n+\tif (nextEventLoaded_) {\n+\t\tyaml_event_delete(&nextEvent_);\n+\t\tnextEventLoaded_ = false;\n+\t}\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::consume()\n+ * \\brief Consume the next event with an expected event type\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 \\a type doesn't match the next event\n+ */\n+int YamlParserContext::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 YamlParserContext::ItemParser\n+ * \\brief The functor to handle an item in a YAML list\n+ */\n+\n+/**\n+ * \\typedef YamlParserContext::RecordParser\n+ * \\brief The functor to handle an item in a YAML dictionary\n+ */\n+\n+/**\n+ * \\fn YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::parseList(ItemParser parseItem)\n+{\n+\treturn parseDictionaryOrList(false, parseItem);\n+}\n+\n+/**\n+ * \\fn YamlParserContext::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 YamlParserContext::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 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()> &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 YamlParserContext::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 YamlParserContext::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 when it succeeds */\n+\tif (1 != yaml_parser_parse(&parser_, &nextEvent_))\n+\t\treturn -EINVAL;\n+\n+\tnextEventLoaded_ = true;\n+\tevent = nextEvent_;\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+ *\n+ * 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 YamlParserContext::parseNextYamlObject(YamlObject &yamlObject)\n+{\n+\tyaml_event_t event;\n+\n+\tif (nextEvent(event))\n+\t\treturn -EINVAL;\n+\n+\tswitch (event.type) {\n+\tcase YAML_SCALAR_EVENT:\n+\t\tyamlObject.type_ = YamlObject::Value;\n+\t\tparseString(yamlObject.value_);\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]() {\n+\t\t\tlist.emplace_back(new YamlObject());\n+\t\t\treturn parseNextYamlObject(*list.back());\n+\t\t};\n+\t\treturn parseList(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](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 parseDictionary(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.consumeDocumentStart())\n+\t\treturn nullptr;\n+\n+\tif (context.parseNextYamlObject(*root))\n+\t\treturn nullptr;\n+\n+\tif (context.consumeDocumentEnd())\n+\t\treturn nullptr;\n+\n+\treturn root;\n+}\n+\n+} /* namespace libcamera */\n", "prefixes": [ "libcamera-devel", "v4", "1/2" ] }