[{"id":29385,"web_url":"https://patchwork.libcamera.org/comment/29385/","msgid":"<v6te6ogk7do3fhpyt4zk25bvpu62ikecx7n7gc42ehlnpc7fxv@f3yus777de7e>","date":"2024-05-02T10:27:09","subject":"Re: [PATCH v3] treewide: Query list of cameras just once","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"content":"Hi Barnabás\n\nOn Mon, Apr 29, 2024 at 02:24:09PM GMT, Barnabás Pőcze wrote:\n> This is more efficient since only a single vector will be constructed,\n> and furthermore, it prevents the TOCTOU issue that might arise when\n> the list of cameras changes between the two queries.\n>\n> Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>\n> ---\n>\n> changes in v3\n>   * drop std::move() from this change\n>   * extend note in documentation\n>   * limit scope of `cameras` in `CameraSession::CameraSession()`\n>\n> changes in v2\n>   * fix code block in documentation\n>   * add comment noting that the camera may disappear\n>\n> ---\n>  Documentation/guides/application-developer.rst | 12 +++++++-----\n>  src/apps/cam/camera_session.cpp                | 11 ++++++++---\n>  src/gstreamer/gstlibcamerasrc.cpp              |  5 +++--\n>  3 files changed, 18 insertions(+), 10 deletions(-)\n>\n> diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst\n> index 9a9905b1..92e2a373 100644\n> --- a/Documentation/guides/application-developer.rst\n> +++ b/Documentation/guides/application-developer.rst\n> @@ -116,19 +116,21 @@ available.\n>\n>  .. code:: cpp\n>\n> -   if (cm->cameras().empty()) {\n> +   auto cameras = cm->cameras();\n> +   if (cameras.empty()) {\n>         std::cout << \"No cameras were identified on the system.\"\n>                   << std::endl;\n>         cm->stop();\n>         return EXIT_FAILURE;\n>     }\n>\n> -   std::string cameraId = cm->cameras()[0]->id();\n> -   camera = cm->get(cameraId);\n> +   std::string cameraId = cameras[0]->id();\n>\n> +   auto camera = cm->get(cameraId);\n>     /*\n> -    * Note that is equivalent to:\n> -    * camera = cm->cameras()[0];\n> +    * Note that `camera` may not compare equal to `cameras[0]`.\n> +    * In fact, it might simply be a `nullptr`, as the particular\n> +    * device might have disappeared (and reappeared) in the meantime.\n>      */\n>\n>  Once a camera has been selected an application needs to acquire an exclusive\n> diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp\n> index 8447f932..334d2ed8 100644\n> --- a/src/apps/cam/camera_session.cpp\n> +++ b/src/apps/cam/camera_session.cpp\n> @@ -39,9 +39,14 @@ CameraSession::CameraSession(CameraManager *cm,\n>  {\n>  \tchar *endptr;\n>  \tunsigned long index = strtoul(cameraId.c_str(), &endptr, 10);\n> -\tif (*endptr == '\\0' && index > 0 && index <= cm->cameras().size())\n> -\t\tcamera_ = cm->cameras()[index - 1];\n> -\telse\n> +\n> +\tif (*endptr == '\\0' && index > 0) {\n> +\t\tauto cameras = cm->cameras();\n> +\t\tif (index <= cameras.size())\n> +\t\t\tcamera_ = cameras[index - 1];\n> +\t}\n> +\n> +\tif (!camera_)\n>  \t\tcamera_ = cm->get(cameraId);\n\nI guess this is ok even if the window between the two accesses to\ncameras() is really tiny and the optimization is relatively minor\n\n>\n>  \tif (!camera_) {\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index f015c6d2..4eddfa3e 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -385,13 +385,14 @@ gst_libcamera_src_open(GstLibcameraSrc *self)\n>  \t\t\treturn false;\n>  \t\t}\n>  \t} else {\n> -\t\tif (cm->cameras().empty()) {\n> +\t\tauto cameras = cm->cameras();\n> +\t\tif (cameras.empty()) {\n>  \t\t\tGST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND,\n>  \t\t\t\t\t  (\"Could not find any supported camera on this system.\"),\n>  \t\t\t\t\t  (\"libcamera::CameraMananger::cameras() is empty\"));\n>  \t\t\treturn false;\n>  \t\t}\n> -\t\tcam = cm->cameras()[0];\n> +\t\tcam = cameras[0];\n\nack!\n\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\n>  \t}\n>\n>  \tGST_INFO_OBJECT(self, \"Using camera '%s'\", cam->id().c_str());\n>\n> base-commit: fb74bb7df66b96dbe28702155cddfc96a1b30f78\n> --\n> 2.44.0\n>","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 34CD3C3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  2 May 2024 10:27:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4B2FE63419;\n\tThu,  2 May 2024 12:27:13 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 50A7F6340B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  2 May 2024 12:27:12 +0200 (CEST)","from ideasonboard.com (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B2D6D3A3;\n\tThu,  2 May 2024 12:26:14 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"JwCoh89q\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1714645574;\n\tbh=+Rm/Z9Chz+/bn7Cb03NdMOluOErjxPsz0x/Z8rIHuBk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=JwCoh89q/vr02oehBvKLTD7HNL/29Kk3HEAGNvD3c/7AJGQgB/D6Q5wWLi5zTv72F\n\tEsHdbaBy/rIktidx1Jd4vkpF+X/nlv5n7iybTW/GCcUS+XrTJomIREhAgU/m1/9CGu\n\tKPCR4tsRlbgLIFF0jHYhr90I86TxYu1MNozoOstE=","Date":"Thu, 2 May 2024 12:27:09 +0200","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v3] treewide: Query list of cameras just once","Message-ID":"<v6te6ogk7do3fhpyt4zk25bvpu62ikecx7n7gc42ehlnpc7fxv@f3yus777de7e>","References":"<20240429142406.59765-1-pobrn@protonmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20240429142406.59765-1-pobrn@protonmail.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":29434,"web_url":"https://patchwork.libcamera.org/comment/29434/","msgid":"<171509796175.1857112.2635291919652405354@ping.linuxembedded.co.uk>","date":"2024-05-07T16:06:01","subject":"Re: [PATCH v3] treewide: Query list of cameras just once","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2024-04-29 15:24:09)\n> This is more efficient since only a single vector will be constructed,\n> and furthermore, it prevents the TOCTOU issue that might arise when\n> the list of cameras changes between the two queries.\n> \n> Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>\n> ---\n> \n> changes in v3\n>   * drop std::move() from this change\n>   * extend note in documentation\n>   * limit scope of `cameras` in `CameraSession::CameraSession()`\n> \n> changes in v2\n>   * fix code block in documentation\n>   * add comment noting that the camera may disappear\n> \n> ---\n>  Documentation/guides/application-developer.rst | 12 +++++++-----\n>  src/apps/cam/camera_session.cpp                | 11 ++++++++---\n>  src/gstreamer/gstlibcamerasrc.cpp              |  5 +++--\n>  3 files changed, 18 insertions(+), 10 deletions(-)\n> \n> diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst\n> index 9a9905b1..92e2a373 100644\n> --- a/Documentation/guides/application-developer.rst\n> +++ b/Documentation/guides/application-developer.rst\n> @@ -116,19 +116,21 @@ available.\n> \n>  .. code:: cpp\n> \n> -   if (cm->cameras().empty()) {\n> +   auto cameras = cm->cameras();\n> +   if (cameras.empty()) {\n>         std::cout << \"No cameras were identified on the system.\"\n>                   << std::endl;\n>         cm->stop();\n>         return EXIT_FAILURE;\n>     }\n> \n> -   std::string cameraId = cm->cameras()[0]->id();\n> -   camera = cm->get(cameraId);\n> +   std::string cameraId = cameras[0]->id();\n> \n> +   auto camera = cm->get(cameraId);\n>     /*\n> -    * Note that is equivalent to:\n> -    * camera = cm->cameras()[0];\n> +    * Note that `camera` may not compare equal to `cameras[0]`.\n> +    * In fact, it might simply be a `nullptr`, as the particular\n> +    * device might have disappeared (and reappeared) in the meantime.\n>      */\n\nWe should probably do the same corresponding change to simple-cam which\n'implements' this application-developer guide too when this is merged.\n\nBut as that's a separate repository - it's definitely a separate patch.\n\n>  Once a camera has been selected an application needs to acquire an exclusive\n> diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp\n> index 8447f932..334d2ed8 100644\n> --- a/src/apps/cam/camera_session.cpp\n> +++ b/src/apps/cam/camera_session.cpp\n> @@ -39,9 +39,14 @@ CameraSession::CameraSession(CameraManager *cm,\n>  {\n>         char *endptr;\n>         unsigned long index = strtoul(cameraId.c_str(), &endptr, 10);\n> -       if (*endptr == '\\0' && index > 0 && index <= cm->cameras().size())\n> -               camera_ = cm->cameras()[index - 1];\n> -       else\n> +\n> +       if (*endptr == '\\0' && index > 0) {\n> +               auto cameras = cm->cameras();\n> +               if (index <= cameras.size())\n> +                       camera_ = cameras[index - 1];\n> +       }\n> +\n> +       if (!camera_)\n\nIs camera_ already initialised to false/0/null by default? Checking...\n\nIt's a shared_ptr, so I think we're good here.\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n>                 camera_ = cm->get(cameraId);\n> \n>         if (!camera_) {\n> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp\n> index f015c6d2..4eddfa3e 100644\n> --- a/src/gstreamer/gstlibcamerasrc.cpp\n> +++ b/src/gstreamer/gstlibcamerasrc.cpp\n> @@ -385,13 +385,14 @@ gst_libcamera_src_open(GstLibcameraSrc *self)\n>                         return false;\n>                 }\n>         } else {\n> -               if (cm->cameras().empty()) {\n> +               auto cameras = cm->cameras();\n> +               if (cameras.empty()) {\n>                         GST_ELEMENT_ERROR(self, RESOURCE, NOT_FOUND,\n>                                           (\"Could not find any supported camera on this system.\"),\n>                                           (\"libcamera::CameraMananger::cameras() is empty\"));\n>                         return false;\n>                 }\n> -               cam = cm->cameras()[0];\n> +               cam = cameras[0];\n>         }\n> \n>         GST_INFO_OBJECT(self, \"Using camera '%s'\", cam->id().c_str());\n> \n> base-commit: fb74bb7df66b96dbe28702155cddfc96a1b30f78\n> --\n> 2.44.0\n>","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 C6370C3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  7 May 2024 16:06:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BC4DA6342D;\n\tTue,  7 May 2024 18:06:05 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C8C80633E4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  7 May 2024 18:06:04 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5A8FB3D5;\n\tTue,  7 May 2024 18:06:02 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"KP2XbdEk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1715097962;\n\tbh=sTwQ8XDwb8f8XJiO6YLUCQyNKL1qwG5nDKa1TVwV+tY=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=KP2XbdEkE+gRjLF7XUik+A4R1uAV1WA8JGhE0tIZtUiz8YT2FHe9EWVsI8s2do7oa\n\tLE6XCWaB3RpgT+UebD0ALDoS0XWvK5i6gsAUaoi+uWXL12z1Kdna9VITZ4/ZGaVq+f\n\tLV34oCQyzcUBzK9u3U6fNsxZsiTCywPgN4A7m3iU=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20240429142406.59765-1-pobrn@protonmail.com>","References":"<20240429142406.59765-1-pobrn@protonmail.com>","Subject":"Re: [PATCH v3] treewide: Query list of cameras just once","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 07 May 2024 17:06:01 +0100","Message-ID":"<171509796175.1857112.2635291919652405354@ping.linuxembedded.co.uk>","User-Agent":"alot/0.10","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":29538,"web_url":"https://patchwork.libcamera.org/comment/29538/","msgid":"<-K0V4eL1IHMBdwMXHE_12thVBKf1B_j5eScJEYh8RkYdpN8XrbeztNbAMToFYHZhlg2rQn272QhBh3mGF74aPHh83sEOHf1CObYZUeoZhPU=@protonmail.com>","date":"2024-05-14T21:57:19","subject":"Re: [PATCH v3] treewide: Query list of cameras just once","submitter":{"id":133,"url":"https://patchwork.libcamera.org/api/people/133/","name":"Pőcze Barnabás","email":"pobrn@protonmail.com"},"content":"Hi\n\n\n2024. április 29., hétfő 16:24 keltezéssel, Barnabás Pőcze <pobrn@protonmail.com> írta:\n\n> This is more efficient since only a single vector will be constructed,\n> and furthermore, it prevents the TOCTOU issue that might arise when\n> the list of cameras changes between the two queries.\n> \n> [...]\n\n\nAre there any additional changes I should make?\n\n\nRegards,\nBarnabás Pőcze","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 234C7BDE6B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 14 May 2024 21:57:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2DDFC63480;\n\tTue, 14 May 2024 23:57:25 +0200 (CEST)","from mail-4316.protonmail.ch (mail-4316.protonmail.ch\n\t[185.70.43.16])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1F6EE63469\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 14 May 2024 23:57:24 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=protonmail.com header.i=@protonmail.com\n\theader.b=\"wWThQ/tD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com;\n\ts=protonmail3; t=1715723843; x=1715983043;\n\tbh=dIL1CoL72LlnZFdNeiiWJlPyknw6TeLoKKTBLqQg0Gk=;\n\th=Date:To:From:Subject:Message-ID:In-Reply-To:References:\n\tFeedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID:\n\tMessage-ID:BIMI-Selector;\n\tb=wWThQ/tDntsn4E+0Ka8nMBBV/MEga4BN97ZvDIQDv5wNYoXf+Z1zZBWcIq6MIgfkB\n\t4+dO+R1bTbRc01+TDE/4r/Pj3JOQHd38xeN9tpvwDttVBjjum0EPhh3M5A/tqH2F1g\n\tW4/sKMXlMVDNhuGARID1R5qXSxkAsPtdZnR0mZZYGF9WGITluz9yo7HLD7zkRPgKN3\n\tvPk3gg1wNYZdOeyK5kTMWZDPkbtvY4aNSdFei0SmRWoxAXdFkDnefcLWK77ImnZzvp\n\tye40TaI1eY/pCoZPy4PVKPmv5k97XZ8Egm55LI+EPkemEkNbdnmpNtdf7TsYilzrom\n\tuPcftqLLyGWig==","Date":"Tue, 14 May 2024 21:57:19 +0000","To":"libcamera-devel@lists.libcamera.org","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <pobrn@protonmail.com>","Subject":"Re: [PATCH v3] treewide: Query list of cameras just once","Message-ID":"<-K0V4eL1IHMBdwMXHE_12thVBKf1B_j5eScJEYh8RkYdpN8XrbeztNbAMToFYHZhlg2rQn272QhBh3mGF74aPHh83sEOHf1CObYZUeoZhPU=@protonmail.com>","In-Reply-To":"<20240429142406.59765-1-pobrn@protonmail.com>","References":"<20240429142406.59765-1-pobrn@protonmail.com>","Feedback-ID":"20568564:user:proton","X-Pm-Message-ID":"b577004e474a05f2b733f432e1f941de7cd78807","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]