Patch Detail
Show a patch.
GET /api/patches/15681/?format=api
{ "id": 15681, "url": "https://patchwork.libcamera.org/api/patches/15681/?format=api", "web_url": "https://patchwork.libcamera.org/patch/15681/", "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": "<20220418120923.453131-2-hanlinchen@chromium.org>", "date": "2022-04-18T12:09:22", "name": "[libcamera-devel,v3,1/2] libcamera: Introduce YamlParser as a helper to parse yaml files", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "d0239a0e59cce09aa81508d30105068c16085e49", "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/15681/mbox/", "series": [ { "id": 3056, "url": "https://patchwork.libcamera.org/api/series/3056/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3056", "date": "2022-04-18T12:09:21", "name": "Introduce YamlParser YAML files", "version": 3, "mbox": "https://patchwork.libcamera.org/series/3056/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/15681/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/15681/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 3F7D2C3260\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 18 Apr 2022 12:09:33 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E282565641;\n\tMon, 18 Apr 2022 14:09:32 +0200 (CEST)", "from mail-pg1-x52b.google.com (mail-pg1-x52b.google.com\n\t[IPv6:2607:f8b0:4864:20::52b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AA9646563F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 18 Apr 2022 14:09:30 +0200 (CEST)", "by mail-pg1-x52b.google.com with SMTP id k62so12459951pgd.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 18 Apr 2022 05:09:30 -0700 (PDT)", "from localhost ([2401:fa00:1:17:9775:a6e9:7655:6686])\n\tby smtp.gmail.com with UTF8SMTPSA id\n\te10-20020a17090a630a00b001c685cfd9d1sm12694221pjj.20.2022.04.18.05.09.27\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tMon, 18 Apr 2022 05:09:28 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1650283772;\n\tbh=9SumQPraztHwJBtMzpLYdRMoyk9ehK/y/zuJU7jih3w=;\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=xzjabpB6Fcpadlay6+59w/9z2lwthfdAkSSs2fLsyv4NvXSDRWTjsPk0834bM11O9\n\tPfCKLAp5/eYCNkiBJ71mavrRZhVrDgwWgzVdhhjtT5bR6JlW0loNbdKQr7MBpilTdS\n\ti+U2cI7yxXxE0AGKoZdBTIDd35B8Q+QNUBi/dar8U1V+DVf59njf6YcIRzyRJ/xSDI\n\tWd0OjfgQxR+nimSbEOjY8ik8JKSJlE7mdnsndZC8p4OWsOrt8z/5FdoTIkqCMIf1SE\n\tipcnqTOGDo1kRfVS/4LBq6+rguqEqBQ1DRNnSkiGSHkoqAw+aEIksza0lZs37LJWUz\n\tFC0bPRP81/fbA==", "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=kpfqAAVfvb1Zvp92zy4nJ+BdYuF6NZBRz/jeIKHRUTs=;\n\tb=hS/1hM4ahGpO/aoFI/VZ/ZwjRJ0AgNKKJXZL3iMTsnIA74NLB9A5QR6W2cs4SqhLVK\n\ttVYvUhJVZvfXpsUDul5e+yoodrEybdFpB4xA88Kw8iJGAAZcs/zgoDp1ric1jqKP1MJN\n\tb/BDuBW1EG1HxgX4V9bqiLqCGrUd2yCfg/+wM=" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=chromium.org\n\theader.i=@chromium.org header.b=\"hS/1hM4a\"; \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=kpfqAAVfvb1Zvp92zy4nJ+BdYuF6NZBRz/jeIKHRUTs=;\n\tb=znVkMxs8pSimwxDo7MnBlapAaTFyvbXoRJXagkbnkn0g1TWXPHWp2IlbBkSI2zxXdl\n\tnMqP4fa9m9hy4ow25bwodnD25D5jo+as09u+GB/qNDTBBxMM2t5eGfdfPkqoJL+867Fc\n\tyLGjFrm7p/8TJ/34IXcBo5cA62twLFygFBIFaZ4DevEL2eJNAu8zy8yV9T7ZmFYyrLCv\n\txAM6dGLHxVpTMni9ruv8BSpl/BZS0hPSbQ5hjgItGlNzd1OuDhYj5ey0caLS9aUsDcOo\n\tNp4EMZ/3W/pE3N03CBZUBXgfJBXTl0xxIm1xUy9rXe+J41bcIWpK3Ax4ud89pNFjFvMe\n\tqTEg==", "X-Gm-Message-State": "AOAM530R3RUJL6zCSqw42CR6heUAv/nHAFjhuqlrdOjGUq8Es2hXGPx3\n\t4WoF53b2iVUR1Djmk4oHFZbEdinMIK0+Ow==", "X-Google-Smtp-Source": "ABdhPJzL2jO13cCk8+A9xJR7tglupv11H+iAJ81w+YhXXIqas+hjprgcOM2evDvURO3acblZECPUUA==", "X-Received": "by 2002:a65:5b4b:0:b0:3a3:d8fb:6926 with SMTP id\n\ty11-20020a655b4b000000b003a3d8fb6926mr9699349pgr.76.1650283768584; \n\tMon, 18 Apr 2022 05:09:28 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Mon, 18 Apr 2022 20:09:22 +0800", "Message-Id": "<20220418120923.453131-2-hanlinchen@chromium.org>", "X-Mailer": "git-send-email 2.36.0.rc0.470.gd361397f0d-goog", "In-Reply-To": "<20220418120923.453131-1-hanlinchen@chromium.org>", "References": "<20220418120923.453131-1-hanlinchen@chromium.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v3 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 | 86 +++\n src/libcamera/meson.build | 3 +\n src/libcamera/yaml_parser.cpp | 802 +++++++++++++++++++++++\n 5 files changed, 894 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..e00ac25d\n--- /dev/null\n+++ b/include/libcamera/internal/yaml_parser.h\n@@ -0,0 +1,86 @@\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 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(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..f4072bb0\n--- /dev/null\n+++ b/src/libcamera/yaml_parser.cpp\n@@ -0,0 +1,802 @@\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+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 boolean value is returned, and \\a ok is set\n+ * 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 diemension 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 associates 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 associates 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 associates elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\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 memberof a YamlObject by name. Only YamlObject\n+ * instances of dictionary type associates elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\n+ *\n+ * \\return A YamlObject as a 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+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 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+\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+\trelease();\n+}\n+\n+/**\n+ * \\fn YamlParserContext::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() must 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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::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 YamlParserContext::consume()\n+ * \\brief Consume 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 failed to initialize\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+\t}\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+\tif (event.type == YAML_SCALAR_EVENT) {\n+\t\tyamlObject.type_ = YamlObject::Value;\n+\t\tparseString(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 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 parseDictionary(handler);\n+\t}\n+\n+\tLOG(YamlParser, Error) << \"Invalid YAML file\";\n+\treturn -EINVAL;\n+}\n+\n+/**\n+ * \\class YamlParser\n+ * \\brief A helper class for parsing YAML files.\n+ *\n+ * The YamlParser class provides an easy interface to parse the contents of a\n+ * YAML file int a tree fo YamlObject instances.\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+ *\tstd::unique_ptr<YamlObject> root = YamlParser::parse(fh);\n+ *\tif (!root)\n+ *\t\treturn;\n+ *\n+ *\tif (!root->isDictionary())\n+ *\t\treturn;\n+ *\n+ *\tstd::string name = (*root)[\"name\"];\n+ *\tstd::cout << name.get<std::string>(\"\");\n+ *\n+ *\tconst YamlObject &numbers = (*root)[\"numbers\"];\n+ *\tif (!numbers.isList())\n+ *\t\treturn;\n+ *\n+ *\tfor (int i = 0; i < numbers.size; i++)\n+ *\t\tstd::cout << numbers[i].get<int32_t>(0);\n+ *\n+ * \\endcode\n+ *\n+ * The YamlParser::parse() function accepts an open FILE and initializes an\n+ * internal parse.\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+ * Prior to parsing the YAML content, the function accepts an opened FILE to\n+ * create an internal parser.\n+ *\n+ * \\return Pointer to result YamlObject on success or nullptr otherwise\n+ */\n+std::unique_ptr<YamlObject> YamlParser::parse(FILE *fh)\n+{\n+\tYamlParserContext context;\n+\n+\tif (context.initParser(fh))\n+\t\treturn nullptr;\n+\n+\tstd::unique_ptr<YamlObject> root(new YamlObject());\n+\n+\tif (context.consumeDocumentStart())\n+\t\tgoto error;\n+\n+\tif (context.parseNextYamlObject(*root))\n+\t\tgoto error;\n+\n+\tif (context.consumeDocumentEnd())\n+\t\tgoto error;\n+\n+\tcontext.release();\n+\treturn root;\n+\n+error:\n+\troot.release();\n+\tcontext.release();\n+\treturn nullptr;\n+}\n+\n+} /* namespace libcamera */\n", "prefixes": [ "libcamera-devel", "v3", "1/2" ] }