aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorsnaury <snaury@yandex-team.com>2024-10-16 12:16:48 +0300
committersnaury <snaury@yandex-team.com>2024-10-16 12:32:13 +0300
commite0fb25470a47f0c243091ed28bf54a186f732f6a (patch)
treee85dfe628401f4f21749ab95b9d711242e3b49cd /contrib
parentb3b4a0b9681eb0981f9958a426c95a53f79169a7 (diff)
downloadydb-e0fb25470a47f0c243091ed28bf54a186f732f6a.tar.gz
ydblib: add jinja2cpp
commit_hash:f3563041f6f6f7443e75fc99acd2c967d0debb04
Diffstat (limited to 'contrib')
-rw-r--r--contrib/libs/jinja2cpp/.yandex_meta/devtools.copyrights.report52
-rw-r--r--contrib/libs/jinja2cpp/.yandex_meta/devtools.licenses.report125
-rw-r--r--contrib/libs/jinja2cpp/.yandex_meta/licenses.list.txt818
-rw-r--r--contrib/libs/jinja2cpp/LICENSE373
-rw-r--r--contrib/libs/jinja2cpp/README.md384
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/binding/rapid_json.h201
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/config.h27
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/error_info.h185
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/filesystem_handler.h201
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/generic_list.h287
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/generic_list_iterator.h104
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/polymorphic_value.h447
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/reflected_value.h601
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/string_helpers.h296
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/template.h305
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/template_env.h320
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/utils/i_comparable.h16
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/value.h763
-rw-r--r--contrib/libs/jinja2cpp/include/jinja2cpp/value_ptr.h29
-rw-r--r--contrib/libs/jinja2cpp/src/ast_visitor.h117
-rw-r--r--contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.cpp127
-rw-r--r--contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.h50
-rw-r--r--contrib/libs/jinja2cpp/src/error_handling.h63
-rw-r--r--contrib/libs/jinja2cpp/src/error_info.cpp289
-rw-r--r--contrib/libs/jinja2cpp/src/expression_evaluator.cpp602
-rw-r--r--contrib/libs/jinja2cpp/src/expression_evaluator.h619
-rw-r--r--contrib/libs/jinja2cpp/src/expression_parser.cpp606
-rw-r--r--contrib/libs/jinja2cpp/src/expression_parser.h49
-rw-r--r--contrib/libs/jinja2cpp/src/filesystem_handler.cpp163
-rw-r--r--contrib/libs/jinja2cpp/src/filters.cpp1086
-rw-r--r--contrib/libs/jinja2cpp/src/filters.h474
-rw-r--r--contrib/libs/jinja2cpp/src/function_base.h49
-rw-r--r--contrib/libs/jinja2cpp/src/generic_adapters.h215
-rw-r--r--contrib/libs/jinja2cpp/src/generic_list.cpp38
-rw-r--r--contrib/libs/jinja2cpp/src/helpers.h119
-rw-r--r--contrib/libs/jinja2cpp/src/internal_value.cpp1053
-rw-r--r--contrib/libs/jinja2cpp/src/internal_value.h673
-rw-r--r--contrib/libs/jinja2cpp/src/lexer.cpp122
-rw-r--r--contrib/libs/jinja2cpp/src/lexer.h375
-rw-r--r--contrib/libs/jinja2cpp/src/lexertk.h1842
-rw-r--r--contrib/libs/jinja2cpp/src/out_stream.h43
-rw-r--r--contrib/libs/jinja2cpp/src/render_context.h182
-rw-r--r--contrib/libs/jinja2cpp/src/renderer.h131
-rw-r--r--contrib/libs/jinja2cpp/src/robin_hood.h2544
-rw-r--r--contrib/libs/jinja2cpp/src/serialize_filters.cpp442
-rw-r--r--contrib/libs/jinja2cpp/src/statements.cpp839
-rw-r--r--contrib/libs/jinja2cpp/src/statements.h661
-rw-r--r--contrib/libs/jinja2cpp/src/string_converter_filter.cpp395
-rw-r--r--contrib/libs/jinja2cpp/src/template.cpp190
-rw-r--r--contrib/libs/jinja2cpp/src/template_env.cpp95
-rw-r--r--contrib/libs/jinja2cpp/src/template_impl.h487
-rw-r--r--contrib/libs/jinja2cpp/src/template_parser.cpp972
-rw-r--r--contrib/libs/jinja2cpp/src/template_parser.h1081
-rw-r--r--contrib/libs/jinja2cpp/src/testers.cpp379
-rw-r--r--contrib/libs/jinja2cpp/src/testers.h116
-rw-r--r--contrib/libs/jinja2cpp/src/value.cpp143
-rw-r--r--contrib/libs/jinja2cpp/src/value_helpers.h167
-rw-r--r--contrib/libs/jinja2cpp/src/value_visitors.h1151
-rw-r--r--contrib/libs/jinja2cpp/ya.make68
-rw-r--r--contrib/restricted/boost/filesystem/.yandex_meta/devtools.copyrights.report503
-rw-r--r--contrib/restricted/boost/filesystem/.yandex_meta/devtools.licenses.report179
-rw-r--r--contrib/restricted/boost/filesystem/.yandex_meta/licenses.list.txt175
-rw-r--r--contrib/restricted/boost/filesystem/README.md28
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem.hpp22
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/config.hpp153
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/cstdio.hpp87
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/footer.hpp23
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/header.hpp54
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/path_traits.hpp734
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/conjunction.hpp49
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/disjunction.hpp49
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/negation.hpp49
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/detail/utf8_codecvt_facet.hpp33
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/directory.hpp1039
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/exception.hpp92
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/file_status.hpp252
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/fstream.hpp257
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/operations.hpp724
-rw-r--r--contrib/restricted/boost/filesystem/include/boost/filesystem/path.hpp1710
-rw-r--r--contrib/restricted/boost/filesystem/src/atomic_ref.hpp32
-rw-r--r--contrib/restricted/boost/filesystem/src/atomic_tools.hpp69
-rw-r--r--contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp120
-rw-r--r--contrib/restricted/boost/filesystem/src/directory.cpp1772
-rw-r--r--contrib/restricted/boost/filesystem/src/error_handling.hpp237
-rw-r--r--contrib/restricted/boost/filesystem/src/exception.cpp188
-rw-r--r--contrib/restricted/boost/filesystem/src/operations.cpp5202
-rw-r--r--contrib/restricted/boost/filesystem/src/path.cpp1719
-rw-r--r--contrib/restricted/boost/filesystem/src/path_traits.cpp187
-rw-r--r--contrib/restricted/boost/filesystem/src/platform_config.hpp83
-rw-r--r--contrib/restricted/boost/filesystem/src/portability.cpp83
-rw-r--r--contrib/restricted/boost/filesystem/src/posix_tools.hpp78
-rw-r--r--contrib/restricted/boost/filesystem/src/private_config.hpp74
-rw-r--r--contrib/restricted/boost/filesystem/src/unique_path.cpp351
-rw-r--r--contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp29
-rw-r--r--contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp72
-rw-r--r--contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp72
-rw-r--r--contrib/restricted/boost/filesystem/src/windows_tools.hpp310
-rw-r--r--contrib/restricted/boost/filesystem/ya.make68
-rw-r--r--contrib/restricted/boost/scope/.yandex_meta/devtools.copyrights.report91
-rw-r--r--contrib/restricted/boost/scope/.yandex_meta/devtools.licenses.report108
-rw-r--r--contrib/restricted/boost/scope/.yandex_meta/licenses.list.txt48
-rw-r--r--contrib/restricted/boost/scope/LICENSE23
-rw-r--r--contrib/restricted/boost/scope/README.md28
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/defer.hpp180
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/compact_storage.hpp102
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/config.hpp50
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/footer.hpp22
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/header.hpp49
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/is_nonnull_default_constructible.hpp66
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/is_not_like.hpp49
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_assign_ref.hpp52
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_construct_ref.hpp52
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/conjunction.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/disjunction.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_final.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_invocable.hpp63
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp69
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_swappable.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/negation.hpp53
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/error_code_checker.hpp103
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/exception_checker.hpp106
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/fd_deleter.hpp82
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/fd_resource_traits.hpp49
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/scope_exit.hpp560
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/scope_fail.hpp265
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/scope_success.hpp309
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/unique_fd.hpp38
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/unique_resource.hpp1642
-rw-r--r--contrib/restricted/boost/scope/include/boost/scope/unique_resource_fwd.hpp46
-rw-r--r--contrib/restricted/boost/scope/ya.make27
-rw-r--r--contrib/restricted/expected-lite/.yandex_meta/devtools.copyrights.report41
-rw-r--r--contrib/restricted/expected-lite/.yandex_meta/devtools.licenses.report79
-rw-r--r--contrib/restricted/expected-lite/.yandex_meta/licenses.list.txt62
-rw-r--r--contrib/restricted/expected-lite/CHANGES.txt5
-rw-r--r--contrib/restricted/expected-lite/LICENSE.txt23
-rw-r--r--contrib/restricted/expected-lite/README.md506
-rw-r--r--contrib/restricted/expected-lite/include/nonstd/expected.hpp3555
-rw-r--r--contrib/restricted/expected-lite/ya.make13
139 files changed, 50190 insertions, 0 deletions
diff --git a/contrib/libs/jinja2cpp/.yandex_meta/devtools.copyrights.report b/contrib/libs/jinja2cpp/.yandex_meta/devtools.copyrights.report
new file mode 100644
index 0000000000..f1ac2b9dae
--- /dev/null
+++ b/contrib/libs/jinja2cpp/.yandex_meta/devtools.copyrights.report
@@ -0,0 +1,52 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP COPYRIGHT_SERVICE_LABEL 097cfbf1153a4f7ed38e64fd7e2e3993
+BELONGS ya.make
+ License text:
+ Copyright (c) 2016 Jonathan B. Coe
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/jinja2cpp/polymorphic_value.h [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL b64d8f4e77f6cb007626437192f93d68
+BELONGS ya.make
+ License text:
+ // Copyright (c) 2018-2021 Martin Ankerl <http://martin.ankerl.com>
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/robin_hood.h [13:13]
diff --git a/contrib/libs/jinja2cpp/.yandex_meta/devtools.licenses.report b/contrib/libs/jinja2cpp/.yandex_meta/devtools.licenses.report
new file mode 100644
index 0000000000..c548c28d7b
--- /dev/null
+++ b/contrib/libs/jinja2cpp/.yandex_meta/devtools.licenses.report
@@ -0,0 +1,125 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP MIT 4dfb1f9f7c68ff4156bccff8f3de1b60
+BELONGS ya.make
+ License text:
+ [![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/jinja2cpp/Jinja2Cpp/master/LICENSE)
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 85.71
+ Match type : TAG
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ README.md [12:12]
+
+KEEP MIT 6b45d159c11839114dfda208e6140eda
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 100.00
+ Match type : TEXT
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ src/robin_hood.h [15:31]
+
+KEEP MPL-2.0 81bec2692a2a04ebc459d6dca672caf4
+BELONGS ya.make
+FILE_INCLUDE LICENSE found in files: LICENSE at line 363
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: MPL-2.0
+ Score : 100.00
+ Match type : TEXT
+ Links : http://mpl.mozilla.org/2012/01/03/announcing-mpl-2-0/, http://www.mozilla.com/MPL/2.0/, https://spdx.org/licenses/MPL-2.0
+ Files with this license:
+ LICENSE [1:373]
+
+KEEP MIT 8384d75c38c570f3edb87cf9f64f2ec2
+BELONGS ya.make
+ License text:
+ // SPDX-License-Identifier: MIT
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 100.00
+ Match type : TAG
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ src/robin_hood.h [12:12]
+
+KEEP MIT a6e9f2d79eb73e6e422759b53da6152a
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 100.00
+ Match type : TEXT
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ include/jinja2cpp/polymorphic_value.h [5:20]
+
+KEEP MIT b6657f1432f3f3405667101097117109
+BELONGS ya.make
+ License text:
+ // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ src/robin_hood.h [11:11]
+
+KEEP MPL-1.1 bbcc868f743e472bc5bc01c0a6b359a5
+BELONGS ya.make
+ License text:
+ [![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/jinja2cpp/Jinja2Cpp/master/LICENSE)
+ Scancode info:
+ Original SPDX id: MPL-1.1
+ Score : 90.00
+ Match type : TAG
+ Links : http://www.mozilla.com/MPL/1.1/index.html, http://www.mozilla.org/MPL/MPL-1.1.html, https://spdx.org/licenses/MPL-1.1
+ Files with this license:
+ README.md [12:12]
+
+KEEP MIT e7fc849ec7d035354afc7a26365b817d
+BELONGS ya.make
+ License text:
+ * under the guidelines and in accordance with the MIT License. *
+ * http://www.opensource.org/licenses/MIT *
+ Scancode info:
+ Original SPDX id: MIT
+ Score : 81.82
+ Match type : REFERENCE
+ Links : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT
+ Files with this license:
+ src/lexertk.h [11:12]
diff --git a/contrib/libs/jinja2cpp/.yandex_meta/licenses.list.txt b/contrib/libs/jinja2cpp/.yandex_meta/licenses.list.txt
new file mode 100644
index 0000000000..c054605553
--- /dev/null
+++ b/contrib/libs/jinja2cpp/.yandex_meta/licenses.list.txt
@@ -0,0 +1,818 @@
+====================COPYRIGHT====================
+// Copyright (c) 2018-2021 Martin Ankerl <http://martin.ankerl.com>
+
+
+====================COPYRIGHT====================
+Copyright (c) 2016 Jonathan B. Coe
+
+
+====================File: LICENSE====================
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
+
+
+====================MIT====================
+ * under the guidelines and in accordance with the MIT License. *
+ * http://www.opensource.org/licenses/MIT *
+
+
+====================MIT====================
+// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+
+
+====================MIT====================
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+
+====================MIT====================
+// SPDX-License-Identifier: MIT
+
+
+====================MIT====================
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+====================MIT====================
+[![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/jinja2cpp/Jinja2Cpp/master/LICENSE)
+
+
+====================MPL-1.1====================
+[![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/jinja2cpp/Jinja2Cpp/master/LICENSE)
+
+
+====================MPL-2.0====================
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0. \ No newline at end of file
diff --git a/contrib/libs/jinja2cpp/LICENSE b/contrib/libs/jinja2cpp/LICENSE
new file mode 100644
index 0000000000..a612ad9813
--- /dev/null
+++ b/contrib/libs/jinja2cpp/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/contrib/libs/jinja2cpp/README.md b/contrib/libs/jinja2cpp/README.md
new file mode 100644
index 0000000000..2fc58e46ac
--- /dev/null
+++ b/contrib/libs/jinja2cpp/README.md
@@ -0,0 +1,384 @@
+<div align="center"><img width="200" src="https://avatars0.githubusercontent.com/u/49841676?s=200&v=4"></div>
+
+# Jinja2С++
+
+[![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/)
+[![Standard](https://img.shields.io/badge/c%2B%2B-14-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)
+[![Standard](https://img.shields.io/badge/c%2B%2B-17-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)
+[![Standard](https://img.shields.io/badge/c%2B%2B-20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)
+[![Coverage Status](https://codecov.io/gh/jinja2cpp/Jinja2Cpp/branch/master/graph/badge.svg)](https://codecov.io/gh/jinja2cpp/Jinja2Cpp)
+[![Github Releases](https://img.shields.io/github/release/jinja2cpp/Jinja2Cpp/all.svg)](https://github.com/jinja2cpp/Jinja2Cpp/releases)
+[![Github Issues](https://img.shields.io/github/issues/jinja2cpp/Jinja2Cpp.svg)](http://github.com/jinja2cpp/Jinja2Cpp/issues)
+[![GitHub License](https://img.shields.io/badge/license-Mozilla-blue.svg)](https://raw.githubusercontent.com/jinja2cpp/Jinja2Cpp/master/LICENSE)
+[![conan.io](https://api.bintray.com/packages/conan/conan-center/jinja2cpp%3A_/images/download.svg?version=1.2.1%3A_) ](https://conan.io/center/jinja2cpp)
+[![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Jinja2Cpp/Lobby)
+
+C++ implementation of the Jinja2 Python template engine. This library brings support of powerful Jinja2 template features into the C++ world, reports dynamic HTML pages and source code generation.
+
+## Introduction
+
+Main features of Jinja2C++:
+- Easy-to-use public interface. Just load templates and render them.
+- Conformance to [Jinja2 specification](http://jinja.pocoo.org/docs/2.10/)
+- Full support of narrow- and wide-character strings both for templates and parameters.
+- Built-in reflection for the common C++ types, nlohmann and rapid JSON libraries.
+- Powerful full-featured Jinja2 expressions with filtering (via '|' operator) and 'if'-expressions.
+- Control statements (`set`, `for`, `if`, `filter`, `do`, `with`).
+- Templates extension, including and importing
+- Macros
+- Rich error reporting.
+- Shared template environment with templates cache support
+
+For instance, this simple code:
+
+```c++
+#include <jinja2cpp/template.h>
+
+std::string source = R"(
+{{ ("Hello", 'world') | join }}!!!
+{{ ("Hello", 'world') | join(', ') }}!!!
+{{ ("Hello", 'world') | join(d = '; ') }}!!!
+{{ ("Hello", 'world') | join(d = '; ') | lower }}!!!
+)";
+
+Template tpl;
+tpl.Load(source);
+
+std::string result = tpl.RenderAsString({}).value();
+```
+
+produces the result string:
+
+```
+Helloworld!!!
+Hello, world!!!
+Hello; world!!!
+hello; world!!!
+```
+
+## Getting started
+
+To use Jinja2C++ in your project you have to:
+* Clone the Jinja2C++ repository
+* Build it according to the [instructions](https://jinja2cpp.github.io/docs/build_and_install.html)
+* Link to your project.
+
+Usage of Jinja2C++ in the code is pretty simple:
+1. Declare the jinja2::Template object:
+
+```c++
+jinja2::Template tpl;
+```
+
+2. Populate it with template:
+
+```c++
+tpl.Load("{{ 'Hello World' }}!!!");
+```
+
+3. Render the template:
+
+```c++
+std::cout << tpl.RenderAsString({}).value() << std::endl;
+```
+
+and get:
+
+`
+Hello World!!!
+`
+
+That's all!
+
+More detailed examples and features description can be found in the documentation: [https://jinja2cpp.github.io/docs/usage](https://jinja2cpp.github.io/docs/usage)
+
+## Current Jinja2 support
+Currently, Jinja2C++ supports the limited number of Jinja2 features. By the way, Jinja2C++ is planned to be a fully [jinja2 specification](http://jinja.pocoo.org/docs/2.10/templates/)-conformant. The current support is limited to:
+- expressions. You can use almost every expression style: simple, filtered, conditional, and so on.
+- the big number of filters (**sort, default, first, last, length, max, min, reverse, unique, sum, attr, map, reject, rejectattr, select, selectattr, pprint, dictsort, abs, float, int, list, round, random, trim, title, upper, wordcount, replace, truncate, groupby, urlencode, capitalize, escape, tojson, striptags, center, xmlattr**)
+- the big number of testers (**eq, defined, ge, gt, iterable, le, lt, mapping, ne, number, sequence, string, undefined, in, even, odd, lower, upper**)
+- the number of functions (**range**, **loop.cycle**)
+- 'if' statement (with 'elif' and 'else' branches)
+- 'for' statement (with 'else' branch and 'if' part support)
+- 'include' statement
+- 'import'/'from' statements
+- 'set' statement (both line and block)
+- 'filter' statement
+- 'extends'/'block' statements
+- 'macro'/'call' statements
+- 'with' statement
+- 'do' extension statement
+- recursive loops
+- space control and 'raw'/'endraw' blocks
+
+Full information about Jinja2 specification support and compatibility table can be found here: [https://jinja2cpp.github.io/docs/j2_compatibility.html](https://jinja2cpp.github.io/docs/j2_compatibility.html).
+
+## Supported compilers
+Compilation of Jinja2C++ tested on the following compilers (with C++14 and C++17 enabled features):
+- Linux gcc 5.5 - 9.0
+- Linux clang 5.0 - 9
+- MacOS X-Code 9
+- MacOS X-Code 10
+- MacOS X-Code 11 (C++14 in default build, C++17 with externally-provided boost)
+- Microsoft Visual Studio 2015 - 2019 x86, x64
+- MinGW gcc compiler 7.3
+- MinGW gcc compiler 8.1
+
+**Note:** Support of gcc version >= 9.x or clang version >= 8.0 depends on the version of the Boost library provided.
+
+### Build status
+
+| Compiler | Status |
+|---------|---------:|
+| **MSVC** 2015 (x86, x64), **MinGW** 7 (x64), **MinGW** 8 (x64) | [![Build status](https://ci.appveyor.com/api/projects/status/vu59lw4r67n8jdxl/branch/master?svg=true)](https://ci.appveyor.com/project/flexferrum/jinja2cpp-n5hjm/branch/master) |
+| **X-Code** 9, 10, 11 | [![Build Status](https://travis-ci.org/jinja2cpp/Jinja2Cpp.svg?branch=master)](https://travis-ci.org/jinja2cpp/Jinja2Cpp) |
+| **MSVC** 2017 (x86, x64), **MSVC** 2019 (x86, x64), C++14/C++17 | [![](https://github.com/jinja2cpp/Jinja2Cpp/workflows/CI-windows-build/badge.svg)](https://github.com/jinja2cpp/Jinja2Cpp/actions?query=workflow%3ACI-windows-build) |
+| **g++** 5, 6, 7, 8, 9, 10, 11 **clang** 5, 6, 7, 8, 9, 10, 11, 12 C++14/C++17/C++20 | [![](https://github.com/jinja2cpp/Jinja2Cpp/workflows/CI-linux-build/badge.svg)](https://github.com/jinja2cpp/Jinja2Cpp/actions?query=workflow%3ACI-linux-build) |
+
+## Build and install
+Jinja2C++ has several external dependencies:
+- `boost` library (at least version 1.65)
+- `nonstd::expected-lite` [https://github.com/martinmoene/expected-lite](https://github.com/martinmoene/expected-lite)
+- `nonstd::variant-lite` [https://github.com/martinmoene/variant-lite](https://github.com/martinmoene/variant-lite)
+- `nonstd::optional-lite` [https://github.com/martinmoene/optional-lite](https://github.com/martinmoene/optional-lite)
+- `nonstd::string-view-lite` [https://github.com/martinmoene/string-view-lite](https://github.com/martinmoene/string-view-lite)
+- `fmtlib::fmt` [https://github.com/fmtlib/fmt](https://github.com/fmtlib/fmt)
+
+Examples of build scripts and different build configurations could be found here: [https://github.com/jinja2cpp/examples-build](https://github.com/jinja2cpp/examples-build)
+
+In simplest case to compile Jinja2C++ you need:
+
+1. Install CMake build system (at least version 3.0)
+2. Clone jinja2cpp repository:
+
+```
+> git clone https://github.com/flexferrum/Jinja2Cpp.git
+```
+
+3. Create build directory:
+
+```
+> cd Jinja2Cpp
+> mkdir build
+```
+
+4. Run CMake and build the library:
+
+```
+> cd build
+> cmake .. -DCMAKE_INSTALL_PREFIX=<path to install folder>
+> cmake --build . --target all
+```
+"Path to install folder" here is a path to the folder where you want to install Jinja2C++ lib.
+
+5. Install library:
+
+```
+> cmake --build . --target install
+```
+
+In this case, Jinja2C++ will be built with internally-shipped dependencies and install them respectively. But Jinja2C++ supports builds with externally-provided deps.
+### Usage with conan.io dependency manager
+Jinja2C++ can be used as conan.io package. In this case, you should do the following steps:
+
+1. Install conan.io according to the documentation ( https://docs.conan.io/en/latest/installation.html )
+2. Add a reference to Jinja2C++ package (`jinja2cpp/1.2.1`) to your conanfile.txt, conanfile.py or CMakeLists.txt. For instance, with the usage of `conan-cmake` integration it could be written this way:
+
+```cmake
+
+cmake_minimum_required(VERSION 3.24)
+project(Jinja2CppSampleConan CXX)
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
+list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})
+
+add_definitions("-std=c++14")
+
+if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
+ message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
+ file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/0.18.1/conan.cmake"
+ "${CMAKE_BINARY_DIR}/conan.cmake"
+ TLS_VERIFY ON)
+endif()
+include(${CMAKE_BINARY_DIR}/conan.cmake)
+
+conan_cmake_autodetect(settings)
+conan_cmake_run(REQUIRES
+ jinja2cpp/1.1.0
+ gtest/1.14.0
+ BASIC_SETUP
+ ${CONAN_SETTINGS}
+ OPTIONS
+ jinja2cpp/*:shared=False
+ gtest/*:shared=False
+ BUILD missing)
+
+set (TARGET_NAME jinja2cpp_build_test)
+
+add_executable (${TARGET_NAME} main.cpp)
+
+target_link_libraries (${TARGET_NAME} ${CONAN_LIBS})
+set_target_properties (${TARGET_NAME} PROPERTIES
+ CXX_STANDARD 14
+ CXX_STANDARD_REQUIRED ON)
+
+```
+
+
+### Additional CMake build flags
+You can define (via -D command-line CMake option) the following build flags:
+
+- **JINJA2CPP_BUILD_TESTS** (default TRUE) - to build or not to Jinja2C++ tests.
+- **JINJA2CPP_STRICT_WARNINGS** (default TRUE) - Enable strict mode compile-warnings(-Wall -Werror, etc).
+- **JINJA2CPP_MSVC_RUNTIME_TYPE** (default /MD) - MSVC runtime type to link with (if you use Microsoft Visual Studio compiler).
+- **JINJA2CPP_DEPS_MODE** (default "internal") - modes for dependency handling. Following values possible:
+ - `internal` In this mode Jinja2C++ build script uses dependencies (include `boost`) shipped as subprojects. Nothing needs to be provided externally.
+ - `external-boost` In this mode Jinja2C++ build script uses only `boost` as an externally-provided dependency. All other dependencies are taken from subprojects.
+ - `external` In this mode all dependencies should be provided externally. Paths to `boost`, `nonstd-*` libs, etc. should be specified via standard CMake variables (like `CMAKE_PREFIX_PATH` or libname_DIR)
+ - `conan-build` Special mode for building Jinja2C++ via conan recipe.
+
+
+### Build with C++17 standard enabled
+Jinja2C++ tries to use standard versions of `std::variant`, `std::string_view` and `std::optional` if possible.
+
+## Acknowledgments
+Thanks to **@flexferrum** for creating this library, for being one of the brightest minds in software engineering community. Rest in peace, friend.
+
+Thanks to **@manu343726** for CMake scripts improvement, bug hunting, and fixing and conan.io packaging.
+
+Thanks to **@martinmoene** for the perfectly implemented xxx-lite libraries.
+
+Thanks to **@vitaut** for the amazing text formatting library.
+
+Thanks to **@martinus** for the fast hash maps implementation.
+
+
+## Changelog
+
+
+### Version 1.3.1
+
+#### Changes and improvements
+- bump deps versions
+- add new json binding - boost::json
+- speedup regex parsing by switching to boost::regex(std::regex extremely slow)
+ - templates are now loading faster
+
+#### Fixed bugs
+- small fixes across code base
+
+#### Breaking changes
+- internal deps now used through cmake fetch_content
+- default json serializer/deserializer is switched to boost::json
+
+### Version 1.2.1
+
+#### Changes and improvements
+- bump deps versions
+- support modern compilers(up to Clang 12) and standards(C++20)
+- tiny code style cleanup
+
+#### Fixed bugs
+- small fixes across code base
+
+#### Breaking changes
+- internal deps point to make based boost build
+
+### Version 1.1.0
+#### Changes and improvements
+- `batch` filter added
+- `slice` filter added
+- `format` filter added
+- `tojson` filter added
+- `striptags` filter added
+- `center` filter added
+- `xmlattr` filter added
+- `raw`/`endraw` tags added
+- repeat string operator added (e. g. `'a' * 5` will produce `'aaaaa'`)
+- support for templates metadata (`meta`/`endmeta` tags) added
+- `-fPIC` flag added to Linux build configuration
+
+#### Fixed bugs
+- Fix behavior of lstripblock/trimblocks global settings. Now it fully corresponds to the origina jinja2
+- Fix bug with rendering parent `block` content if child doesn't override this block
+- Fix compilation issues with user-defined callables with number of arguments more than 2
+- Fix access to global Jinja2 functions from included/extended templates
+- Fix point of evaluation of macro params
+- Fix looping over the strings
+- Cleanup warnings
+
+#### Breaking changes
+- From now with C++17 standard enabled Jinja2C++ uses standard versions of types `variant`, `string_view` and `optional`
+
+### Version 1.0.0
+#### Changes and improvements
+- `default` attribute added to the `map` filter (#48)
+- escape sequences support added to the string literals (#49)
+- arbitrary ranges, generated sequences, input iterators, etc. now can be used with `GenericList` type (#66)
+- nonstd::string_view is now one of the possible types for the `Value`
+- `filter` tag support added to the template parser (#44)
+- `escape` filter support added to the template parser (#140)
+- `capitalize` filter support added to the template parser (#137)
+- the multiline version of `set` tag added to the parser (#45)
+- added built-in reflection for nlohmann JSON and RapidJSON libraries (#78)
+- `loop.depth` and `loop.depth0` variables support added
+- {fmt} is now used as a formatting library instead of iostreams
+- robin hood hash map is now used for internal value storage
+- rendering performance improvements
+- template cache implemented in `TemplateEnv`
+- user-defined callables now can accept global context via `*context` special param
+- MinGW, clang >= 7.0, XCode >= 9, gcc >= 7.0 are now officially supported as a target compilers (#79)
+
+#### Fixed bugs
+- Fixed pipe (`|`) operator precedence (#47)
+- Fixed bug in internal char <-> wchar_t converter on Windows
+- Fixed crash in parsing `endblock` tag
+- Fixed scope control for `include` and `for` tags
+- Fixed bug with macros call within expression context
+
+#### Breaking changes
+- MSVC runtime type is now defined by `JINJA2CPP_MSVC_RUNTIME_TYPE` CMake variable
+
+### Version 0.9.2
+#### Major changes
+- User-defined callables implemented. Now you can define your own callable objects, pass them as input parameters and use them inside templates as regular (global) functions, filters or testers. See details here: https://jinja2cpp.github.io/docs/usage/ud_callables.html
+- Now you can define global (template environment-wide) parameters that are accessible for all templates bound to this environment.
+- `include`, `import` and `from` statements implemented. Now it's possible to include other templates and use macros from other templates.
+- `with` statement implemented
+- `do` statement implemented
+- Sample build projects for various Jinja2C++ usage variants created: https://github.com/jinja2cpp/examples-build](https://github.com/jinja2cpp/examples-build)
+- Documentation site created for Jinja2C++: https://jinja2cpp.github.io
+
+#### Minor changes
+- Render-time error handling added
+- Dependency management mode added to the build script
+- Fix bugs with error reporting during the parse time
+- Upgraded versions of external dependencies
+
+#### Breaking changes
+- `RenderAsString` method now returns `nonstd::expected` instead of regular `std::string`
+- Templates with `import`, `extends` and `include` generate errors if parsed without `TemplateEnv` set
+- Release bundles (archives) are configured with `external` dependency management mode by default
+
+### Version 0.9.1
+- `applymacro` filter added which allows applying arbitrary macro as a filter
+- dependencies to boost removed from the public interface
+- CMake scripts improved
+- Various bugs fixed
+- Improve reflection
+- Warnings cleanup
+
+### Version 0.9
+- Support of 'extents'/'block' statements
+- Support of 'macro'/'call' statements
+- Rich error reporting
+- Support for recursive loops
+- Support for space control before and after control blocks
+- Improve reflection
+
+### Version 0.6
+- A lot of filters have been implemented. Full set of supported filters listed here: [https://github.com/flexferrum/Jinja2Cpp/issues/7](https://github.com/flexferrum/Jinja2Cpp/issues/7)
+- A lot of testers have been implemented. Full set of supported testers listed here: [https://github.com/flexferrum/Jinja2Cpp/issues/8](https://github.com/flexferrum/Jinja2Cpp/issues/8)
+- 'Concatenate as string' operator ('~') has been implemented
+- For-loop with 'if' condition has been implemented
+- Fixed some bugs in parser
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/binding/rapid_json.h b/contrib/libs/jinja2cpp/include/jinja2cpp/binding/rapid_json.h
new file mode 100644
index 0000000000..2d9bc47c1b
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/binding/rapid_json.h
@@ -0,0 +1,201 @@
+#ifndef JINJA2CPP_BINDING_RAPID_JSON_H
+#define JINJA2CPP_BINDING_RAPID_JSON_H
+
+#include <jinja2cpp/reflected_value.h>
+#include <jinja2cpp/string_helpers.h>
+
+#include <rapidjson/document.h>
+#include <rapidjson/rapidjson.h>
+
+namespace jinja2
+{
+namespace detail
+{
+
+template<typename CharT>
+struct RapidJsonNameConverter;
+
+template<>
+struct RapidJsonNameConverter<char>
+{
+ static const std::string& GetName(const std::string& str) { return str; }
+};
+
+template<>
+struct RapidJsonNameConverter<wchar_t>
+{
+ static std::wstring GetName(const std::string& str) { return ConvertString<std::wstring>(str); }
+};
+
+template<typename T>
+class RapidJsonObjectAccessor : public IMapItemAccessor, public ReflectedDataHolder<T, false>
+{
+public:
+ using ReflectedDataHolder<T, false>::ReflectedDataHolder;
+ using NameCvt = RapidJsonNameConverter<typename T::Ch>;
+ using ThisType = RapidJsonObjectAccessor<T>;
+ ~RapidJsonObjectAccessor() override = default;
+
+ size_t GetSize() const override
+ {
+ auto j = this->GetValue();
+ return j ? j->MemberCount() : 0ULL;
+ }
+
+ bool HasValue(const std::string& name) const override
+ {
+ auto j = this->GetValue();
+ return j ? j->HasMember(NameCvt::GetName(name).c_str()) : false;
+ }
+
+ Value GetValueByName(const std::string& nameOrig) const override
+ {
+ auto j = this->GetValue();
+ const auto& name = NameCvt::GetName(nameOrig);
+ if (!j || !j->HasMember(name.c_str()))
+ return Value();
+
+ return Reflect(&(*j)[name.c_str()]);
+ }
+
+ std::vector<std::string> GetKeys() const override
+ {
+ auto j = this->GetValue();
+ if (!j)
+ return {};
+
+ std::vector<std::string> result;
+ result.reserve(j->MemberCount());
+ for (auto it = j->MemberBegin(); it != j->MemberEnd(); ++ it)
+ {
+ result.emplace_back(ConvertString<std::string>(std::basic_string_view<typename T::Ch>(it->name.GetString())));
+ }
+ return result;
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ auto enumerator = this->GetValue();
+ auto otherEnum = val->GetValue();
+ if (enumerator && otherEnum && enumerator != otherEnum)
+ return false;
+ if ((enumerator && !otherEnum) || (!enumerator && otherEnum))
+ return false;
+ return true;
+ }
+};
+
+template<typename Enc>
+struct RapidJsonArrayAccessor
+ : IListItemAccessor
+ , IIndexBasedAccessor
+ , ReflectedDataHolder<rapidjson::GenericValue<Enc>, false>
+{
+ using ReflectedDataHolder<rapidjson::GenericValue<Enc>, false>::ReflectedDataHolder;
+ using ThisType = RapidJsonArrayAccessor<Enc>;
+
+ std::optional<size_t> GetSize() const override
+ {
+ auto j = this->GetValue();
+ return j ? j->Size() : std::optional<size_t>();
+ }
+
+ const IIndexBasedAccessor* GetIndexer() const override
+ {
+ return this;
+ }
+
+ ListEnumeratorPtr CreateEnumerator() const override
+ {
+ using Enum = Enumerator<typename rapidjson::GenericValue<Enc>::ConstValueIterator>;
+ auto j = this->GetValue();
+ if (!j)
+ return jinja2::ListEnumeratorPtr();
+
+ return jinja2::ListEnumeratorPtr(new Enum(j->Begin(), j->End()));
+ }
+
+ Value GetItemByIndex(int64_t idx) const override
+ {
+ auto j = this->GetValue();
+ if (!j)
+ return Value();
+
+ return Reflect((*j)[static_cast<rapidjson::SizeType>(idx)]);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ auto enumerator = this->GetValue();
+ auto otherEnum = val->GetValue();
+ if (enumerator && otherEnum && enumerator != otherEnum)
+ return false;
+ if ((enumerator && !otherEnum) || (!enumerator && otherEnum))
+ return false;
+ return true;
+ }
+};
+
+template<typename Enc>
+struct Reflector<rapidjson::GenericValue<Enc>>
+{
+ static Value CreateFromPtr(const rapidjson::GenericValue<Enc>* val)
+ {
+ Value result;
+ switch (val->GetType())
+ {
+ case rapidjson::kNullType:
+ break;
+ case rapidjson::kFalseType:
+ result = Value(false);
+ break;
+ case rapidjson::kTrueType:
+ result = Value(true);
+ break;
+ case rapidjson::kObjectType:
+ result = GenericMap([accessor = RapidJsonObjectAccessor<rapidjson::GenericValue<Enc>>(val)]() { return &accessor; });
+ break;
+ case rapidjson::kArrayType:
+ result = GenericList([accessor = RapidJsonArrayAccessor<Enc>(val)]() { return &accessor; });
+ break;
+ case rapidjson::kStringType:
+ result = std::basic_string<typename Enc::Ch>(val->GetString(), val->GetStringLength());
+ break;
+ case rapidjson::kNumberType:
+ if (val->IsInt64() || val->IsUint64())
+ result = val->GetInt64();
+ else if (val->IsInt() || val->IsUint())
+ result = val->GetInt();
+ else
+ result = val->GetDouble();
+ break;
+ }
+ return result;
+ }
+
+};
+
+template<typename Enc>
+struct Reflector<rapidjson::GenericDocument<Enc>>
+{
+ static Value Create(const rapidjson::GenericDocument<Enc>& val)
+ {
+ return GenericMap([accessor = RapidJsonObjectAccessor<rapidjson::GenericDocument<Enc>>(&val)]() { return &accessor; });
+ }
+
+ static Value CreateFromPtr(const rapidjson::GenericDocument<Enc>* val)
+ {
+ return GenericMap([accessor = RapidJsonObjectAccessor<rapidjson::GenericDocument<Enc>>(val)]() { return &accessor; });
+ }
+
+};
+} // namespace detail
+} // namespace jinja2
+
+#endif // JINJA2CPP_BINDING_RAPID_JSON_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/config.h b/contrib/libs/jinja2cpp/include/jinja2cpp/config.h
new file mode 100644
index 0000000000..23d53ab33d
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/config.h
@@ -0,0 +1,27 @@
+#ifndef JINJA2CPP_CONFIG_H
+#define JINJA2CPP_CONFIG_H
+
+// The Jinja2C++ library version in the form major * 10000 + minor * 100 + patch.
+#define JINJA2CPP_VERSION 10100
+
+#ifdef _WIN32
+#define JINJA2_DECLSPEC(S) __declspec(S)
+#if _MSC_VER
+#pragma warning(disable : 4251)
+#endif
+#else
+#define JINJA2_DECLSPEC(S)
+#endif
+
+#ifdef JINJA2CPP_BUILD_AS_SHARED
+#define JINJA2CPP_EXPORT JINJA2_DECLSPEC(dllexport)
+#define JINJA2CPP_SHARED_LIB
+#elif JINJA2CPP_LINK_AS_SHARED
+#define JINJA2CPP_EXPORT JINJA2_DECLSPEC(dllimport)
+#define JINJA2CPP_SHARED_LIB
+#else
+#define JINJA2CPP_EXPORT
+#define JINJA2CPP_STATIC_LIB
+#endif
+
+#endif // JINJA2CPP_CONFIG_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/error_info.h b/contrib/libs/jinja2cpp/include/jinja2cpp/error_info.h
new file mode 100644
index 0000000000..d4c2f88d96
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/error_info.h
@@ -0,0 +1,185 @@
+#ifndef JINJA2CPP_ERROR_INFO_H
+#define JINJA2CPP_ERROR_INFO_H
+
+#include "config.h"
+#include "value.h"
+
+#include <iostream>
+#include <type_traits>
+#include <vector>
+
+namespace jinja2
+{
+/*!
+ * \brief Type of the error
+ */
+enum class ErrorCode
+{
+ Unspecified = 0, //!< Error is unspecified
+ UnexpectedException = 1, //!< Generic exception occurred during template parsing or execution. ExtraParams[0] contains `what()` string of the exception
+ YetUnsupported, //!< Feature of the jinja2 specification which yet not supported
+ FileNotFound, //!< Requested file was not found. ExtraParams[0] contains name of the file
+ ExtensionDisabled, //!< Particular jinja2 extension disabled in the settings
+ TemplateEnvAbsent, //!< Template uses `extend`, `import`, `from` or `include` features but it's loaded without the template environment set
+ TemplateNotFound, //!< Template with the specified name was not found. ExtraParams[0] contains name of the file
+ TemplateNotParsed, //!< Template was not parsed
+ InvalidValueType, //!< Invalid type of the value in the particular context
+ InvalidTemplateName, //!< Invalid name of the template. ExtraParams[0] contains the name
+ MetadataParseError, //!< Invalid name of the template. ExtraParams[0] contains the name
+ ExpectedStringLiteral = 1001, //!< String literal expected
+ ExpectedIdentifier, //!< Identifier expected
+ ExpectedSquareBracket, //!< ']' expected
+ ExpectedRoundBracket, //!< ')' expected
+ ExpectedCurlyBracket, //!< '}' expected
+ ExpectedToken, //!< Specific token(s) expected. ExtraParams[0] contains the actual token, rest of ExtraParams contain set of expected tokens
+ ExpectedExpression, //!< Expression expected
+ ExpectedEndOfStatement, //!< End of statement expected. ExtraParams[0] contains the expected end of statement tag
+ ExpectedRawEnd, //!< {% endraw %} expected
+ ExpectedMetaEnd, //!< {% endmeta %} expected
+ UnexpectedToken, //!< Unexpected token. ExtraParams[0] contains the invalid token
+ UnexpectedStatement, //!< Unexpected statement. ExtraParams[0] contains the invalid statement tag
+ UnexpectedCommentBegin, //!< Unexpected comment block begin (`{#`)
+ UnexpectedCommentEnd, //!< Unexpected comment block end (`#}`)
+ UnexpectedExprBegin, //!< Unexpected expression block begin (`{{`)
+ UnexpectedExprEnd, //!< Unexpected expression block end (`}}`)
+ UnexpectedStmtBegin, //!< Unexpected statement block begin (`{%`)
+ UnexpectedStmtEnd, //!< Unexpected statement block end (`%}`)
+ UnexpectedRawBegin, //!< Unexpected raw block begin {% raw %}
+ UnexpectedRawEnd, //!< Unexpected raw block end {% endraw %}
+ UnexpectedMetaBegin, //!< Unexpected meta block begin {% meta %}
+ UnexpectedMetaEnd, //!< Unexpected meta block end {% endmeta %}
+};
+
+/*!
+ * \brief Information about the source location of the error
+ */
+struct SourceLocation
+{
+ //! Name of the file
+ std::string fileName;
+ //! Line number (1-based)
+ unsigned line = 0;
+ //! Column number (1-based)
+ unsigned col = 0;
+};
+
+template<typename CharT>
+/*!
+ * \brief Detailed information about the parse-time or render-time error
+ *
+ * If template parsing or rendering fails the detailed error information is provided. Exact specialization of ErrorInfoTpl is an object which contains
+ * this information. Type of specialization depends on type of the template object: \ref ErrorInfo for \ref Template and \ref ErrorInfoW for \ref TemplateW.
+ *
+ * Detailed information about an error contains:
+ * - Error code
+ * - Error location
+ * - Other locations related to the error
+ * - Description of the location
+ * - Extra parameters of the error
+ *
+ * @tparam CharT Character type which was used in template parser
+ */
+class ErrorInfoTpl
+{
+public:
+ struct Data
+ {
+ ErrorCode code = ErrorCode::Unspecified;
+ SourceLocation srcLoc;
+ std::vector<SourceLocation> relatedLocs;
+ std::vector<Value> extraParams;
+ std::basic_string<CharT> locationDescr;
+ };
+
+ //! Default constructor
+ ErrorInfoTpl() = default;
+ //! Initializing constructor from error description
+ explicit ErrorInfoTpl(Data data)
+ : m_errorData(std::move(data))
+ {}
+
+ //! Copy constructor
+ ErrorInfoTpl(const ErrorInfoTpl<CharT>&) = default;
+ //! Move constructor
+ ErrorInfoTpl(ErrorInfoTpl<CharT>&& val) noexcept
+ : m_errorData(std::move(val.m_errorData))
+ { }
+
+ //! Destructor
+ ~ErrorInfoTpl() noexcept = default;
+
+ //! Copy-assignment operator
+ ErrorInfoTpl& operator =(const ErrorInfoTpl<CharT>&) = default;
+ //! Move-assignment operator
+ ErrorInfoTpl& operator =(ErrorInfoTpl<CharT>&& val) noexcept
+ {
+ if (this == &val)
+ return *this;
+
+ std::swap(m_errorData.code, val.m_errorData.code);
+ std::swap(m_errorData.srcLoc, val.m_errorData.srcLoc);
+ std::swap(m_errorData.relatedLocs, val.m_errorData.relatedLocs);
+ std::swap(m_errorData.extraParams, val.m_errorData.extraParams);
+ std::swap(m_errorData.locationDescr, val.m_errorData.locationDescr);
+
+ return *this;
+ }
+
+ //! Return code of the error
+ ErrorCode GetCode() const
+ {
+ return m_errorData.code;
+ }
+
+ //! Return error location in the template file
+ auto& GetErrorLocation() const
+ {
+ return m_errorData.srcLoc;
+ }
+
+ //! Return locations, related to the main error location
+ auto& GetRelatedLocations() const
+ {
+ return m_errorData.relatedLocs;
+ }
+
+ /*!
+ * \brief Return location description
+ *
+ * Return string of two lines. First line is contents of the line with error. Second highlight the exact position within line. For instance:
+ * ```
+ * {% for i in range(10) endfor%}
+ * ---^-------
+ * ```
+ *
+ * @return Location description
+ */
+ const std::basic_string<CharT>& GetLocationDescr() const
+ {
+ return m_errorData.locationDescr;
+ }
+
+ /*!
+ * \brief Return extra params of the error
+ *
+ * Extra params is a additional details assiciated with the error. For instance, name of the file which wasn't opened
+ *
+ * @return Vector with extra error params
+ */
+ auto& GetExtraParams() const { return m_errorData.extraParams; }
+
+ //! Convert error to the detailed string representation
+ JINJA2CPP_EXPORT std::basic_string<CharT> ToString() const;
+
+private:
+ Data m_errorData;
+};
+
+using ErrorInfo = ErrorInfoTpl<char>;
+using ErrorInfoW = ErrorInfoTpl<wchar_t>;
+
+JINJA2CPP_EXPORT std::ostream& operator<<(std::ostream& os, const ErrorInfo& res);
+JINJA2CPP_EXPORT std::wostream& operator<<(std::wostream& os, const ErrorInfoW& res);
+} // namespace jinja2
+
+#endif // JINJA2CPP_ERROR_INFO_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/filesystem_handler.h b/contrib/libs/jinja2cpp/include/jinja2cpp/filesystem_handler.h
new file mode 100644
index 0000000000..c33b4c9121
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/filesystem_handler.h
@@ -0,0 +1,201 @@
+#ifndef JINJA2CPP_FILESYSTEM_HANDLER_H
+#define JINJA2CPP_FILESYSTEM_HANDLER_H
+
+#include "config.h"
+
+#include <jinja2cpp/utils/i_comparable.h>
+
+#include <optional>
+#include <variant>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace jinja2
+{
+
+template<typename CharT>
+using FileStreamPtr = std::unique_ptr<std::basic_istream<CharT>, void (*)(std::basic_istream<CharT>*)>;
+using CharFileStreamPtr = FileStreamPtr<char>;
+using WCharFileStreamPtr = FileStreamPtr<wchar_t>;
+
+/*!
+ * \brief Generic interface to filesystem handlers (loaders)
+ *
+ * This interface should be implemented in order to provide custom file system handler. Interface provides most-common methods which are called by
+ * the template environment to load the particular template. `OpenStream` methods return the unique pointer to the generic `istream` object implementation.
+ * So, the exact type (ex. `ifstream`, `istringstream` etc.) of input stream is unspecified. In order to delete stream object correctly returned pointer
+ * provide the custom deleter which should properly delete the stream object.
+ */
+class JINJA2CPP_EXPORT IFilesystemHandler : public IComparable
+{
+public:
+ //! Destructor
+ virtual ~IFilesystemHandler() = default;
+
+ /*!
+ * \brief Method is called to open the file with the specified name in 'narrow-char' mode.
+ *
+ * Method should return unique pointer to the std::istream object with custom deleter (\ref CharFileStreamPtr) . Deleter should properly delete pointee
+ * stream object.
+ *
+ * @param name Name of the file to open
+ * @return Opened stream object or empty pointer in case of any error
+ */
+ virtual CharFileStreamPtr OpenStream(const std::string& name) const = 0;
+ /*!
+ * \brief Method is called to open the file with the specified name in 'wide-char' mode.
+ *
+ * Method should return unique pointer to the std::wistream object with custom deleter (\ref WCharFileStreamPtr) . Deleter should properly delete pointee
+ * stream object.
+ *
+ * @param name Name of the file to open
+ * @return Opened stream object or empty pointer in case of any error
+ */
+ virtual WCharFileStreamPtr OpenWStream(const std::string& name) const = 0;
+ /*!
+ * \brief Method is called to obtain the modification date of the specified file (if applicable)
+ *
+ * If the underlaying filesystem supports retrival of the last modification date of the file this method should return this date when called. In other
+ * case it should return the empty optional object. Main purpose of this method is to help templates loader to determine the necessity of cached template
+ * reload
+ *
+ * @param name Name of the file to get the last modification date
+ * @return Last modification date (if applicable) or empty optional object otherwise
+ */
+ virtual std::optional<std::chrono::system_clock::time_point> GetLastModificationDate(const std::string& name) const = 0;
+};
+
+using FilesystemHandlerPtr = std::shared_ptr<IFilesystemHandler>;
+
+/*!
+ * \brief Filesystem handler for files stored in memory
+ *
+ * This filesystem handler implements the simple dictionary object which maps name of the file to it's content. New files can be added by \ref AddFile
+ * methods. Content of the files automatically converted to narrow/wide strings representation if necessary.
+ */
+class JINJA2CPP_EXPORT MemoryFileSystem : public IFilesystemHandler
+{
+public:
+ /*!
+ * \brief Add new narrow-char "file" to the filesystem handler
+ *
+ * Adds new file entry to the internal dictionary object or overwrite the existing one. New entry contains the specified content of the file
+ *
+ * @param fileName Name of the file to add
+ * @param fileContent Content of the file to add
+ */
+ void AddFile(std::string fileName, std::string fileContent);
+ /*!
+ * \brief Add new wide-char "file" to the filesystem handler
+ *
+ * Adds new file entry to the internal dictionary object or overwrite the existing one. New entry contains the specified content of the file
+ *
+ * @param fileName Name of the file to add
+ * @param fileContent Content of the file to add
+ */
+ void AddFile(std::string fileName, std::wstring fileContent);
+
+ CharFileStreamPtr OpenStream(const std::string& name) const override;
+ WCharFileStreamPtr OpenWStream(const std::string& name) const override;
+ std::optional<std::chrono::system_clock::time_point> GetLastModificationDate(const std::string& name) const override;
+
+ /*!
+ * \brief Compares to an object of the same type
+ *
+ * return true if equal
+ */
+ bool IsEqual(const IComparable& other) const override;
+private:
+ struct FileContent
+ {
+ std::optional<std::string> narrowContent;
+ std::optional<std::wstring> wideContent;
+ bool operator==(const FileContent& other) const
+ {
+ if (narrowContent != other.narrowContent)
+ return false;
+ return wideContent == other.wideContent;
+ }
+ bool operator!=(const FileContent& other) const
+ {
+ return !(*this == other);
+ }
+ };
+ mutable std::unordered_map<std::string, FileContent> m_filesMap;
+};
+
+/*!
+ * \brief Filesystem handler for files stored on the filesystem
+ *
+ * This filesystem handler is an interface to the real file system. Root directory for file name mapping provided as a constructor argument. Each name (path) of
+ * the file to open is appended to the root directory path and then passed to the stream open methods.
+ */
+class JINJA2CPP_EXPORT RealFileSystem : public IFilesystemHandler
+{
+public:
+ /*!
+ * \brief Initializing/default constructor
+ *
+ * @param rootFolder Path to the root folder. This path is used as a root for every opened file
+ */
+ explicit RealFileSystem(std::string rootFolder = ".");
+
+ /*!
+ * \brief Reset path to the root folder to the new value
+ *
+ * @param newRoot New path to the root folder
+ */
+ void SetRootFolder(std::string newRoot)
+ {
+ m_rootFolder = std::move(newRoot);
+ }
+
+ /*!
+ * \brief Get path to the current root folder
+ *
+ * @return
+ */
+ const std::string& GetRootFolder() const
+ {
+ return m_rootFolder;
+ }
+ /*!
+ * \brief Get full path to the specified file.
+ *
+ * Appends specified name of the file to the current root folder and returns it
+ *
+ * @param name Name of the file to get path to
+ * @return Full path to the file
+ */
+ std::string GetFullFilePath(const std::string& name) const;
+
+ CharFileStreamPtr OpenStream(const std::string& name) const override;
+ WCharFileStreamPtr OpenWStream(const std::string& name) const override;
+ /*!
+ * \brief Open the specified file as a binary stream
+ *
+ * Opens the specified file in the binary mode (instead of text).
+ *
+ * @param name Name of the file to get the last modification date
+ * @return Last modification date (if applicable) or empty optional object otherwise
+ */
+ CharFileStreamPtr OpenByteStream(const std::string& name) const;
+ std::optional<std::chrono::system_clock::time_point> GetLastModificationDate(const std::string& name) const override;
+
+ /*!
+ * \brief Compares to an object of the same type
+ *
+ * return true if equal
+ */
+ bool IsEqual(const IComparable& other) const override;
+
+private:
+ std::string m_rootFolder;
+};
+} // namespace jinja2
+
+#endif // JINJA2CPP_FILESYSTEM_HANDLER_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list.h b/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list.h
new file mode 100644
index 0000000000..ae70aa8e3e
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list.h
@@ -0,0 +1,287 @@
+#ifndef JINJA2CPP_GENERIC_LIST_H
+#define JINJA2CPP_GENERIC_LIST_H
+
+#include <jinja2cpp/utils/i_comparable.h>
+#include <jinja2cpp/value_ptr.h>
+
+#include <optional>
+
+#include <functional>
+#include <iterator>
+#include <memory>
+
+namespace jinja2
+{
+class Value;
+
+/*!
+ * \brief Interface for accessing items in list by the indexes
+ *
+ * This interface should provided by the particular list implementation in case of support index-based access to the items.
+ */
+struct IIndexBasedAccessor : virtual IComparable
+{
+ virtual ~IIndexBasedAccessor() = default;
+ /*!
+ * \brief This method is called to get the item by the specified index
+ *
+ * @param idx Index of item to get
+ *
+ * @return requested item
+ */
+ virtual Value GetItemByIndex(int64_t idx) const = 0;
+};
+
+struct IListEnumerator;
+using ListEnumeratorPtr = types::ValuePtr<IListEnumerator>;
+
+inline auto MakeEmptyListEnumeratorPtr()
+{
+ return ListEnumeratorPtr();
+}
+
+/*!
+ * \brief Generic list enumerator interface
+ *
+ * This interface should be implemented by the lists of any type. Interface is used to enumerate the list contents item by item.
+ *
+ * Implementation notes: Initial state of the enumerator should be "before the first item". So, the first call of \ref MoveNext method either moves the
+ * enumerator to the first element end returns `true` or moves enumerator to the end and returns `false` in case of empty list. Each call of \ref GetCurrent
+ * method should return the current enumerable item.
+ */
+struct IListEnumerator : virtual IComparable
+{
+ //! Destructor
+ virtual ~IListEnumerator() = default;
+
+ /*!
+ * \brief Method is called to reset enumerator to the initial state ('before the first element') if applicable.
+ *
+ * For the sequences which allow multi-pass iteration this method should reset enumerator to the initial state. For the single-pass sequences method
+ * can do nothing.
+ */
+ virtual void Reset() = 0;
+
+ /*!
+ * \brief Method is called to move the enumerator to the next item (if any)
+ *
+ * @return `true` if enumerator successfully moved and `false` if enumerator reaches the end of the sequence.
+ */
+ virtual bool MoveNext() = 0;
+
+ /*!
+ * \brief Method is called to get the current item value. Can be called multiply times
+ *
+ * @return Value of the item if the current item is valid item and empty value otherwise
+ */
+ virtual Value GetCurrent() const = 0;
+
+ /*!
+ * \brief Method is called to make a deep **copy** of the current enumerator state if possible
+ *
+ * @return New enumerator object with copy of the current enumerator state or empty pointer if copying is not applicable to the enumerator
+ */
+ virtual ListEnumeratorPtr Clone() const = 0;
+
+ /*!
+ * \brief Method is called to transfer current enumerator state to the new object
+ *
+ * State of the enumerator after successful creation of the new object is unspecified by there is a guarantee that there will no calls to the 'moved'
+ * enumerator. Destruction of the moved enumerator shouldn't affect the newly created object.
+ *
+ * @return New enumerator object which holds and owns the current enumerator state
+ */
+ virtual ListEnumeratorPtr Move() = 0;
+};
+
+/*!
+ * \brief Generic list implementation interface
+ *
+ * Every list implementation should implement this interface for providing access to the items of the list. There are several types of lists and implementation
+ * notes for every of them:
+ * - **Single-pass sequences** . For instance, input-stream iterators, generator-based sequences. This type of generic lists should provide no size and no indexer. Enumerator of such sequence supports should be moveable but non-copyable and non-resetable.
+ * - **Forward sequences** . For instance, single-linked lists. This type of lists should provide no size and indexer. Enumerator should be moveable, copyable and resetable.
+ * - **Bidirectional sequences**. Have got the same implementation requirements as forward sequences.
+ * - **Random-access sequences**. Such as arrays or vectors. Should provide valid size (number of stored items), valid indexer implementation and moveable,
+ * copyable and resetable enumerator.
+ *
+ * It's assumed that indexer interface is a part of list implementation.
+ */
+struct IListItemAccessor : virtual IComparable
+{
+ virtual ~IListItemAccessor() = default;
+
+ /*!
+ * \brief Called to get pointer to indexer interface implementation (if applicable)
+ *
+ * See implementation notes for the interface. This method should return pointer to the valid indexer interface if (and only if) list implementation
+ * supports random access to the items.
+ *
+ * Method can be called several times from the different threads.
+ *
+ * @return Pointer to the indexer interface implementation or null if indexing isn't supported for the list
+ */
+ virtual const IIndexBasedAccessor* GetIndexer() const = 0;
+
+ /*!
+ * \brief Called to get enumerator of the particular list
+ *
+ * See implementation notes for the interface. This method should return unique pointer (with custom deleter) to the ListEnumerator interface implementation. Enumerator implementation should follow the requirements for the particular list type implementation.
+ *
+ * Method can be called several times from the different threads.
+ *
+ * @return Pointer to the enumerator of the list
+ */
+ virtual ListEnumeratorPtr CreateEnumerator() const = 0;
+
+ /*!
+ * \brief Called to get size of the list if applicable.
+ *
+ * See implementation notes for the interface. This method should return valid (non-empty) size only for random-access sequences. In other cases this
+ * method should return empty optional.
+ *
+ * Method can be called several times from the different threads.
+ *
+ * @return Non-empty optional with the valid size of the list or empty optional in case of non-random sequence implementation
+ */
+ virtual std::optional<size_t> GetSize() const = 0;
+
+ /*!
+ * \brief Helper factory method of particular enumerator implementation
+ *
+ * @tparam T Type of enumerator to create
+ * @tparam Args Type of enumerator construct args
+ * @param args Actual enumerator constructor args
+ * @return Unique pointer to the enumerator
+ */
+ template<typename T, typename... Args>
+ static ListEnumeratorPtr MakeEnumerator(Args&&... args);
+};
+
+
+namespace detail
+{
+class GenericListIterator;
+} // namespace detail
+
+/*!
+ * \brief Facade class for generic lists
+ *
+ * This class holds the implementation of particular generic list interface and provides friendly access to it's method. Also this class is used to hold
+ * the particular list. Pointer to the generic list interface implementation is held inside std::function object which provides access to the pointer to the interface.
+ *
+ * You can use \ref MakeGenericList method to create instances of the GenericList:
+ * ```
+ * std::array<int, 9> sampleList{10, 20, 30, 40, 50, 60, 70, 80, 90};
+ *
+ * ValuesMap params = {
+ * {"input", jinja2::MakeGenericList(begin(sampleList), end(sampleList)) }
+ * };
+ * ```
+ */
+class JINJA2CPP_EXPORT GenericList
+{
+public:
+ //! Default constructor
+ GenericList() = default;
+
+ /*!
+ * \brief Initializing constructor
+ *
+ * This constructor is only one way to create the valid GenericList object. `accessor` is a functional object which provides access to the \ref IListItemAccessor
+ * interface. The most common way of GenericList creation is to initialize it with lambda which simultaniously holds and and provide access to the
+ * generic list implementation:
+ *
+ * ```
+ * auto MakeGeneratedList(ListGenerator&& fn)
+ * {
+ * return GenericList([accessor = GeneratedListAccessor(std::move(fn))]() {return &accessor;});
+ * }
+ * ```
+ *
+ * @param accessor Functional object which provides access to the particular generic list implementation
+ */
+ explicit GenericList(std::function<const IListItemAccessor*()> accessor)
+ : m_accessor(std::move(accessor))
+ {
+ }
+
+ /*!
+ * \brief Get size of the list
+ *
+ * @return Actual size of the generic list or empty optional object if not applicable
+ */
+ std::optional<size_t> GetSize() const
+ {
+ return m_accessor ? m_accessor()->GetSize() : std::optional<size_t>();
+ }
+
+ /*!
+ * \brief Get pointer to the list accessor interface implementation
+ *
+ * @return Pointer to the list accessor interface or nullptr in case of non-initialized GenericList object
+ */
+ auto GetAccessor() const
+ {
+ return m_accessor ? m_accessor() : nullptr;
+ }
+
+ /*!
+ * \brief Check the GenericList object state
+ *
+ * @return true if GenericList object is valid (initialize) or false otherwize
+ */
+ bool IsValid() const
+ {
+ return !(!m_accessor);
+ }
+
+ /*!
+ * \brief Get interator to the first element of the list
+ *
+ * @return Iterator to the first element of the generic list or iterator equal to the `end()` if list is empty or not initialized
+ */
+ detail::GenericListIterator begin() const;
+ /*!
+ * \brief Get the end iterator
+ *
+ * @return 'end' iterator of the generic list
+ */
+ detail::GenericListIterator end() const;
+
+ /*!
+ * \brief Get interator to the first element of the list
+ *
+ * @return Iterator to the first element of the generic list or iterator equal to the `end()` if list is empty or not initialized
+ */
+ auto cbegin() const;
+ /*!
+ * \brief Get the end iterator
+ *
+ * @return 'end' iterator of the generic list
+ */
+ auto cend() const;
+
+ /*!
+ * \brief Compares with the objects of same type
+ *
+ * @return true if equal
+ */
+ bool IsEqual(const GenericList& rhs) const;
+
+private:
+ std::function<const IListItemAccessor*()> m_accessor;
+};
+
+bool operator==(const GenericList& lhs, const GenericList& rhs);
+bool operator!=(const GenericList& lhs, const GenericList& rhs);
+
+template<typename T, typename ...Args>
+inline ListEnumeratorPtr IListItemAccessor::MakeEnumerator(Args&& ...args)
+{
+ return ListEnumeratorPtr(types::MakeValuePtr<T>(std::forward<Args>(args)...));
+}
+} // namespace jinja2
+
+
+#endif // JINJA2CPP_GENERIC_LIST_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list_iterator.h b/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list_iterator.h
new file mode 100644
index 0000000000..7af469f8a3
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/generic_list_iterator.h
@@ -0,0 +1,104 @@
+#ifndef JINJA2CPP_GENERIC_LIST_ITERATOR_H
+#define JINJA2CPP_GENERIC_LIST_ITERATOR_H
+
+#include "generic_list.h"
+#include "value.h"
+#include "value_ptr.h"
+
+namespace jinja2
+{
+namespace detail
+{
+class JINJA2CPP_EXPORT GenericListIterator
+{
+public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = const Value;
+ using difference_type = std::ptrdiff_t;
+ using reference = const Value&;
+ using pointer = const Value*;
+ using EnumeratorPtr = types::ValuePtr<IListEnumerator>;
+
+ GenericListIterator() = default;
+
+ GenericListIterator(ListEnumeratorPtr enumerator)
+ : m_enumerator(types::ValuePtr<IListEnumerator>(enumerator))
+ {
+ if (m_enumerator)
+ m_hasValue = m_enumerator->MoveNext();
+
+ if (m_hasValue)
+ m_current = m_enumerator->GetCurrent();
+ }
+
+ bool operator == (const GenericListIterator& other) const
+ {
+ if (m_hasValue != other.m_hasValue)
+ return false;
+ if (!m_enumerator && !other.m_enumerator)
+ return true;
+ if (this->m_enumerator && other.m_enumerator && !m_enumerator->IsEqual(*other.m_enumerator))
+ return false;
+ if ((m_enumerator && !other.m_enumerator) || (!m_enumerator && other.m_enumerator))
+ return false;
+ if (m_current != other.m_current)
+ return false;
+ return true;
+ }
+
+ bool operator != (const GenericListIterator& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference operator *() const
+ {
+ return m_current;
+ }
+
+ GenericListIterator& operator ++()
+ {
+ if (!m_enumerator)
+ return *this;
+ m_hasValue = m_enumerator->MoveNext();
+ if (m_hasValue)
+ {
+ m_current = m_enumerator->GetCurrent();
+ }
+ else
+ {
+ EnumeratorPtr temp;
+ Value tempVal;
+ using std::swap;
+ swap(m_enumerator, temp);
+ swap(m_current, tempVal);
+ }
+
+ return *this;
+ }
+
+ GenericListIterator operator++(int)
+ {
+ GenericListIterator result(std::move(m_current));
+
+ this->operator++();
+ return result;
+ }
+private:
+ explicit GenericListIterator(Value&& val)
+ : m_hasValue(true)
+ , m_current(std::move(val))
+ {
+
+ }
+
+private:
+ EnumeratorPtr m_enumerator;
+ bool m_hasValue = false;
+ Value m_current;
+};
+
+} // namespace detail
+} // namespace jinja2
+
+#endif // JINJA2CPP_GENERIC_LIST_ITERATOR_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/polymorphic_value.h b/contrib/libs/jinja2cpp/include/jinja2cpp/polymorphic_value.h
new file mode 100644
index 0000000000..69663d859f
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/polymorphic_value.h
@@ -0,0 +1,447 @@
+/*
+
+Copyright (c) 2016 Jonathan B. Coe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#ifndef ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED
+#define ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED
+
+#include <cassert>
+#include <exception>
+#include <memory>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+
+//
+// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
+#define nonstd_lite_HAVE_IN_PLACE_TYPES 1
+
+// C++17 std::in_place in <utility>:
+
+#if variant_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace nonstd {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define nonstd_lite_in_place_t( T) std::in_place_t
+#define nonstd_lite_in_place_type_t( T) std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K) std::in_place_index_t<K>
+
+#define nonstd_lite_in_place( T) std::in_place_t{}
+#define nonstd_lite_in_place_type( T) std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K) std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // variant_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K) nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // variant_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+namespace isocpp_p0201 {
+
+namespace detail {
+
+////////////////////////////////////////////////////////////////////////////
+// Implementation detail classes
+////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+struct default_copy {
+ T* operator()(const T& t) const { return new T(t); }
+};
+
+template <class T>
+struct default_delete {
+ void operator()(const T* t) const { delete t; }
+};
+
+template <class T>
+struct control_block {
+ virtual ~control_block() = default;
+
+ virtual std::unique_ptr<control_block> clone() const = 0;
+
+ virtual T* ptr() = 0;
+};
+
+template <class T, class U = T>
+class direct_control_block : public control_block<T> {
+ static_assert(!std::is_reference<U>::value, "");
+ U u_;
+
+ public:
+ template <class... Ts>
+ explicit direct_control_block(Ts&&... ts) : u_(U(std::forward<Ts>(ts)...)) {}
+
+ std::unique_ptr<control_block<T>> clone() const override {
+ return std::make_unique<direct_control_block>(*this);
+ }
+
+ T* ptr() override { return std::addressof(u_); }
+};
+
+template <class T, class U, class C = default_copy<U>,
+ class D = default_delete<U>>
+class pointer_control_block : public control_block<T>, public C {
+ std::unique_ptr<U, D> p_;
+
+ public:
+ explicit pointer_control_block(U* u, C c = C{}, D d = D{})
+ : C(std::move(c)), p_(u, std::move(d)) {}
+
+ explicit pointer_control_block(std::unique_ptr<U, D> p, C c = C{})
+ : C(std::move(c)), p_(std::move(p)) {}
+
+ std::unique_ptr<control_block<T>> clone() const override {
+ assert(p_);
+ return std::make_unique<pointer_control_block>(
+ C::operator()(*p_), static_cast<const C&>(*this), p_.get_deleter());
+ }
+
+ T* ptr() override { return p_.get(); }
+};
+
+template <class T, class U>
+class delegating_control_block : public control_block<T> {
+ std::unique_ptr<control_block<U>> delegate_;
+
+ public:
+ explicit delegating_control_block(std::unique_ptr<control_block<U>> b)
+ : delegate_(std::move(b)) {}
+
+ std::unique_ptr<control_block<T>> clone() const override {
+ return std::make_unique<delegating_control_block>(delegate_->clone());
+ }
+
+ T* ptr() override { return delegate_->ptr(); }
+};
+
+} // end namespace detail
+
+class bad_polymorphic_value_construction : public std::exception {
+ public:
+ bad_polymorphic_value_construction() noexcept = default;
+
+ const char* what() const noexcept override {
+ return "Dynamic and static type mismatch in polymorphic_value "
+ "construction";
+ }
+};
+
+template <class T>
+class polymorphic_value;
+
+template <class T>
+struct is_polymorphic_value : std::false_type {};
+
+template <class T>
+struct is_polymorphic_value<polymorphic_value<T>> : std::true_type {};
+
+////////////////////////////////////////////////////////////////////////////////
+// `polymorphic_value` class definition
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+class polymorphic_value {
+ static_assert(!std::is_union<T>::value, "");
+ static_assert(std::is_class<T>::value, "");
+
+ template <class U>
+ friend class polymorphic_value;
+
+ template <class T_, class U, class... Ts>
+ friend polymorphic_value<T_> make_polymorphic_value(Ts&&... ts);
+ template <class T_, class... Ts>
+ friend polymorphic_value<T_> make_polymorphic_value(Ts&&... ts);
+
+ T* ptr_ = nullptr;
+ std::unique_ptr<detail::control_block<T>> cb_;
+
+ public:
+ //
+ // Destructor
+ //
+
+ ~polymorphic_value() = default;
+
+ //
+ // Constructors
+ //
+
+ polymorphic_value() {}
+
+ template <class U, class C = detail::default_copy<U>,
+ class D = detail::default_delete<U>,
+ class V = std::enable_if_t<std::is_convertible<U*, T*>::value>>
+ explicit polymorphic_value(U* u, C copier = C{}, D deleter = D{}) {
+ if (!u) {
+ return;
+ }
+
+#ifndef ISOCPP_P0201_POLYMORPHIC_VALUE_NO_RTTI
+ if (std::is_same<D, detail::default_delete<U>>::value &&
+ std::is_same<C, detail::default_copy<U>>::value &&
+ typeid(*u) != typeid(U))
+ throw bad_polymorphic_value_construction();
+#endif
+ std::unique_ptr<U, D> p(u, std::move(deleter));
+
+ cb_ = std::make_unique<detail::pointer_control_block<T, U, C, D>>(
+ std::move(p), std::move(copier));
+ ptr_ = u;
+ }
+
+ //
+ // Copy-constructors
+ //
+
+ polymorphic_value(const polymorphic_value& p) {
+ if (!p) {
+ return;
+ }
+ auto tmp_cb = p.cb_->clone();
+ ptr_ = tmp_cb->ptr();
+ cb_ = std::move(tmp_cb);
+ }
+
+ //
+ // Move-constructors
+ //
+
+ polymorphic_value(polymorphic_value&& p) noexcept {
+ ptr_ = p.ptr_;
+ cb_ = std::move(p.cb_);
+ p.ptr_ = nullptr;
+ }
+
+ //
+ // Converting constructors
+ //
+
+ template <class U,
+ class V = std::enable_if_t<!std::is_same<T, U>::value &&
+ std::is_convertible<U*, T*>::value>>
+ explicit polymorphic_value(const polymorphic_value<U>& p) {
+ polymorphic_value<U> tmp(p);
+ ptr_ = tmp.ptr_;
+ cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
+ std::move(tmp.cb_));
+ }
+
+ template <class U,
+ class V = std::enable_if_t<!std::is_same<T, U>::value &&
+ std::is_convertible<U*, T*>::value>>
+ explicit polymorphic_value(polymorphic_value<U>&& p) {
+ ptr_ = p.ptr_;
+ cb_ = std::make_unique<detail::delegating_control_block<T, U>>(
+ std::move(p.cb_));
+ p.ptr_ = nullptr;
+ }
+
+#if __cplusplus < 201703L
+
+#endif
+ //
+ // In-place constructor
+ //
+
+ template <class U,
+ class V = std::enable_if_t<
+ std::is_convertible<std::decay_t<U>*, T*>::value &&
+ !is_polymorphic_value<std::decay_t<U>>::value>,
+ class... Ts>
+ explicit polymorphic_value(nonstd_lite_in_place_type_t(U), Ts&&... ts)
+// explicit polymorphic_value(std::in_place_type_t<U>, Ts&&... ts)
+ : cb_(std::make_unique<detail::direct_control_block<T, U>>(
+ std::forward<Ts>(ts)...)) {
+ ptr_ = cb_->ptr();
+ }
+
+
+ //
+ // Assignment
+ //
+
+ polymorphic_value& operator=(const polymorphic_value& p) {
+ if (std::addressof(p) == this) {
+ return *this;
+ }
+
+ if (!p) {
+ cb_.reset();
+ ptr_ = nullptr;
+ return *this;
+ }
+
+ auto tmp_cb = p.cb_->clone();
+ ptr_ = tmp_cb->ptr();
+ cb_ = std::move(tmp_cb);
+ return *this;
+ }
+
+ //
+ // Move-assignment
+ //
+
+ polymorphic_value& operator=(polymorphic_value&& p) noexcept {
+ if (std::addressof(p) == this) {
+ return *this;
+ }
+
+ cb_ = std::move(p.cb_);
+ ptr_ = p.ptr_;
+ p.ptr_ = nullptr;
+ return *this;
+ }
+
+ //
+ // Modifiers
+ //
+
+ void swap(polymorphic_value& p) noexcept {
+ using std::swap;
+ swap(ptr_, p.ptr_);
+ swap(cb_, p.cb_);
+ }
+
+ //
+ // Observers
+ //
+
+ explicit operator bool() const { return bool(cb_); }
+
+ const T* operator->() const {
+ assert(ptr_);
+ return ptr_;
+ }
+
+ const T& operator*() const {
+ assert(*this);
+ return *ptr_;
+ }
+
+ T* operator->() {
+ assert(*this);
+ return ptr_;
+ }
+
+ T& operator*() {
+ assert(*this);
+ return *ptr_;
+ }
+};
+
+//
+// polymorphic_value creation
+//
+template <class T, class... Ts>
+polymorphic_value<T> make_polymorphic_value(Ts&&... ts) {
+ polymorphic_value<T> p;
+ p.cb_ = std::make_unique<detail::direct_control_block<T, T>>(
+ std::forward<Ts>(ts)...);
+ p.ptr_ = p.cb_->ptr();
+ return p;
+}
+template <class T, class U, class... Ts>
+polymorphic_value<T> make_polymorphic_value(Ts&&... ts) {
+ polymorphic_value<T> p;
+ p.cb_ = std::make_unique<detail::direct_control_block<T, U>>(
+ std::forward<Ts>(ts)...);
+ p.ptr_ = p.cb_->ptr();
+ return p;
+}
+
+//
+// non-member swap
+//
+template <class T>
+void swap(polymorphic_value<T>& t, polymorphic_value<T>& u) noexcept {
+ t.swap(u);
+}
+
+} // namespace isocpp_p0201
+
+#endif // ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/reflected_value.h b/contrib/libs/jinja2cpp/include/jinja2cpp/reflected_value.h
new file mode 100644
index 0000000000..fcb99a8031
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/reflected_value.h
@@ -0,0 +1,601 @@
+#ifndef JINJA2CPP_REFLECTED_VALUE_H
+#define JINJA2CPP_REFLECTED_VALUE_H
+
+#include "value.h"
+
+#include <optional>
+
+#include <cstddef>
+#include <memory>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+namespace jinja2
+{
+
+/*!
+ * \brief Reflect the arbitrary C++ type to the Jinja2C++ engine
+ *
+ * Generic method which reflects arbitrary C++ type to the Jinja2C++ template engine. The way of reflection depends on the actual reflected type and could be
+ * - Reflect as an exact value if the type is basic type (such as `char`, `int`, `double`, `std::string` etc.)
+ * - Reflect as a GenericList/GenericMap for standard containers respectively
+ * - Reflect as a GenericMap for the user types
+ * Also pointers/shared pointers to the types could be reflected.
+ *
+ * Reflected value takes ownership on the object, passed to the `Reflect` method by r-value reference or value. Actually, such object is moved. For const
+ * references or pointers reflected value holds the pointer to the reflected object. So, it's necessary to be sure that life time of the reflected object is
+ * longer than it's usage within the template.
+ *
+ * In order to reflect custom (user) the \ref jinja2::TypeReflection template should be specialized in the following way:
+ * ```c++
+ * struct jinja2::TypeReflection<TestStruct> : jinja2::TypeReflected<TestStruct>
+ * {
+ * using FieldAccessor = typename jinja2::TypeReflected<TestStruct>::FieldAccessor;
+ * static auto& GetAccessors()
+ * {
+ * static std::unordered_map<std::string, FieldAccessor> accessors = {
+ * {"intValue", [](const TestStruct& obj) {assert(obj.isAlive); return jinja2::Reflect(obj.intValue);}},
+ * {"intEvenValue", [](const TestStruct& obj) -> Value
+ * {
+ * assert(obj.isAlive);
+ * if (obj.intValue % 2)
+ * return {};
+ * return {obj.intValue};
+ * }},
+ * };
+ *
+ * return accessors;
+ * }
+ * };
+ *
+ * `TestStruct` here is a type which should be reflected. Specialization of the \ref TypeReflection template should derived from \ref TypeReflected template
+ * and define only one method: `GetAccessors`. This method returns the unordered map object which maps field name (as a string) to the corresponded field
+ * accessor. And field accessor here is a lambda object which takes the reflected object reference and returns the value of the field from it.
+ *
+ * @tparam T Type of value to reflect
+ * @param val Value to reflect
+ *
+ * @return jinja2::Value which contains the reflected value or the empty one
+ */
+template<typename T>
+Value Reflect(T&& val);
+
+template<typename T, bool val>
+struct TypeReflectedImpl : std::integral_constant<bool, val>
+{
+};
+
+template<typename T>
+using FieldAccessor = std::function<Value(const T&)>;
+
+template<typename T>
+struct TypeReflected : TypeReflectedImpl<T, true>
+{
+ using FieldAccessor = jinja2::FieldAccessor<T>;
+};
+
+
+
+template<typename T, typename = void>
+struct TypeReflection : TypeReflectedImpl<T, false>
+{
+};
+
+#ifndef JINJA2CPP_NO_DOXYGEN
+template<typename Derived>
+class ReflectedMapImplBase : public IMapItemAccessor
+{
+public:
+ bool HasValue(const std::string& name) const override
+ {
+ return Derived::GetAccessors().count(name) != 0;
+ }
+ Value GetValueByName(const std::string& name) const override
+ {
+ const auto& accessors = Derived::GetAccessors();
+ auto p = accessors.find(name);
+ if (p == accessors.end())
+ throw std::runtime_error("Invalid field access");
+
+ return static_cast<const Derived*>(this)->GetField(p->second);
+ }
+ std::vector<std::string> GetKeys() const override
+ {
+ std::vector<std::string> result;
+ const auto& accessors = Derived::GetAccessors();
+ for (auto& i : accessors)
+ result.push_back(i.first);
+
+ return result;
+ }
+ size_t GetSize() const override
+ {
+ return Derived::GetAccessors().size();
+ }
+};
+
+template<typename T, bool byValue = true>
+class ReflectedDataHolder;
+
+template<typename T>
+class ReflectedDataHolder<T, true>
+{
+public:
+ explicit ReflectedDataHolder(T val) : m_value(std::move(val)) {}
+ explicit ReflectedDataHolder(const T* val) : m_valuePtr(val) {}
+
+protected:
+ const T* GetValue() const
+ {
+ return m_valuePtr ? m_valuePtr : (m_value ? &m_value.value() : nullptr);
+ }
+
+private:
+ std::optional<T> m_value;
+ const T* m_valuePtr = nullptr;
+};
+
+template<typename T>
+class ReflectedDataHolder<T, false>
+{
+public:
+ explicit ReflectedDataHolder(const T* val) : m_valuePtr(val) {}
+
+protected:
+ const T* GetValue() const
+ {
+ return m_valuePtr;
+ }
+
+private:
+ const T* m_valuePtr = nullptr;
+};
+
+template<typename T>
+class ReflectedMapImpl : public ReflectedMapImplBase<ReflectedMapImpl<T>>, public ReflectedDataHolder<T>
+{
+public:
+ using ReflectedDataHolder<T>::ReflectedDataHolder;
+ using ThisType = ReflectedMapImpl<T>;
+
+ static auto GetAccessors() {return TypeReflection<T>::GetAccessors();}
+ template<typename Fn>
+ Value GetField(Fn&& accessor) const
+ {
+ auto v = this->GetValue();
+ if (!v)
+ return Value();
+ return accessor(*v);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+
+ return this->GetValue() == val->GetValue();
+ }
+};
+
+namespace detail
+{
+template<typename T, typename Tag = void>
+struct Reflector;
+
+template<typename T>
+using IsReflectedType = std::enable_if_t<TypeReflection<T>::value>;
+
+template<typename It>
+struct Enumerator : public IListEnumerator
+{
+ using ThisType = Enumerator<It>;
+ It m_begin;
+ It m_cur;
+ It m_end;
+ bool m_justInited = true;
+
+ Enumerator(It begin, It end)
+ : m_begin(begin)
+ , m_cur(end)
+ , m_end(end)
+ {}
+
+ void Reset() override
+ {
+ m_justInited = true;
+ }
+
+ bool MoveNext() override
+ {
+ if (m_justInited)
+ {
+ m_cur = m_begin;
+ m_justInited = false;
+ }
+ else
+ {
+ ++ m_cur;
+ }
+
+ return m_cur != m_end;
+ }
+
+ Value GetCurrent() const override
+ {
+ return Reflect(*m_cur);
+ }
+
+ ListEnumeratorPtr Clone() const override
+ {
+ auto result = std::make_unique<Enumerator<It>>(m_begin, m_end);
+ result->m_cur = m_cur;
+ result->m_justInited = m_justInited;
+ return jinja2::ListEnumeratorPtr(result.release()); //, Deleter);
+ }
+
+ ListEnumeratorPtr Move() override
+ {
+ auto result = std::make_unique<Enumerator<It>>(m_begin, m_end);
+ result->m_cur = std::move(m_cur);
+ result->m_justInited = m_justInited;
+ this->m_justInited = true;
+ return jinja2::ListEnumeratorPtr(result.release()); //, Deleter);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ if (m_begin != val->m_begin)
+ return false;
+ if (m_cur != val->m_cur)
+ return false;
+ if (m_end != val->m_end)
+ return false;
+ if (m_justInited != val->m_justInited)
+ return false;
+ return true;
+ }
+
+ /*
+ It m_begin;
+ It m_cur;
+ It m_end;
+ bool m_justInited = true;
+*/
+/*
+ static void Deleter(IListEnumerator* e)
+ {
+ delete static_cast<Enumerator<It>*>(e);
+ }
+ */
+};
+
+struct ContainerReflector
+{
+ template<typename T>
+ struct ValueItemAccessor : IListItemAccessor, IIndexBasedAccessor
+ {
+ using ThisType = ValueItemAccessor<T>;
+
+ T m_value;
+
+ explicit ValueItemAccessor(T&& cont) noexcept
+ : m_value(std::move(cont))
+ {
+ }
+
+ std::optional<size_t> GetSize() const override
+ {
+ return m_value.size();
+ }
+
+ const IIndexBasedAccessor* GetIndexer() const override
+ {
+ return this;
+ }
+
+ ListEnumeratorPtr CreateEnumerator() const override
+ {
+ using Enum = Enumerator<typename T::const_iterator>;
+ return jinja2::ListEnumeratorPtr(new Enum(m_value.begin(), m_value.end()));//, Enum::Deleter);
+ }
+
+ Value GetItemByIndex(int64_t idx) const override
+ {
+ auto p = m_value.begin();
+ std::advance(p, static_cast<size_t>(idx));
+ return Reflect(*p);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ auto enumerator = CreateEnumerator();
+ auto otherEnum = val->CreateEnumerator();
+ if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum))
+ return false;
+ return true;
+ }
+ };
+
+ template<typename T>
+ struct PtrItemAccessor : IListItemAccessor, IIndexBasedAccessor
+ {
+ using ThisType = PtrItemAccessor<T>;
+
+ const T* m_value{};
+
+ explicit PtrItemAccessor(const T* ptr)
+ : m_value(ptr)
+ {
+ }
+ std::optional<size_t> GetSize() const override
+ {
+ return m_value->size();
+ }
+ const IIndexBasedAccessor* GetIndexer() const override
+ {
+ return this;
+ }
+
+ ListEnumeratorPtr CreateEnumerator() const override
+ {
+ using Enum = Enumerator<typename T::const_iterator>;
+ return jinja2::ListEnumeratorPtr(new Enum(m_value->begin(), m_value->end()));//, Enum::Deleter);
+ }
+
+ Value GetItemByIndex(int64_t idx) const override
+ {
+ auto p = m_value->begin();
+ std::advance(p, static_cast<size_t>(idx));
+ return Reflect(*p);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ auto enumerator = CreateEnumerator();
+ auto otherEnum = val->CreateEnumerator();
+ if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum))
+ return false;
+ return true;
+ }
+ };
+
+ template<typename T>
+ static Value CreateFromValue(T&& cont)
+ {
+ return GenericList([accessor = ValueItemAccessor<T>(std::forward<T>(cont))]() {return &accessor;});
+ }
+
+ template<typename T>
+ static Value CreateFromPtr(const T* cont)
+ {
+ return GenericList([accessor = PtrItemAccessor<T>(cont)]() {return &accessor;});
+ }
+
+ template<typename T>
+ static Value CreateFromPtr(std::shared_ptr<T> cont)
+ {
+ return GenericList([ptr = std::move(cont), accessor = PtrItemAccessor<T>(cont.get())]() {return &accessor;});
+ }
+};
+
+template<typename T>
+struct Reflector<std::set<T>>
+{
+ static auto Create(std::set<T> val)
+ {
+ return ContainerReflector::CreateFromValue(std::move(val));
+ }
+ static auto CreateFromPtr(const std::set<T>* val)
+ {
+ return ContainerReflector::CreateFromPtr(val);
+ }
+};
+
+template<typename T>
+struct Reflector<std::vector<T>>
+{
+ static auto Create(std::vector<T> val)
+ {
+ return ContainerReflector::CreateFromValue(std::move(val));
+ }
+ template<typename U>
+ static auto CreateFromPtr(U&& val)
+ {
+ return ContainerReflector::CreateFromPtr(std::forward<U>(val));
+ }
+};
+
+template<typename T>
+struct Reflector<T, IsReflectedType<T>>
+{
+ static auto Create(const T& val)
+ {
+ return GenericMap([accessor = ReflectedMapImpl<T>(val)]() {return &accessor;});
+ }
+
+ static auto CreateFromPtr(const T* val)
+ {
+ return GenericMap([accessor = ReflectedMapImpl<T>(static_cast<const T*>(val))]() {return &accessor;});
+ }
+
+ static auto CreateFromPtr(std::shared_ptr<T> val)
+ {
+ return GenericMap([ptr = val, accessor = ReflectedMapImpl<T>(val.get())]() {return &accessor;});
+ }
+};
+
+template<typename T>
+struct Reflector<const T&>
+{
+ static auto Create(const T& val)
+ {
+ return Reflector<T>::CreateFromPtr(&val);
+ }
+ static auto Create(const T*& val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+};
+
+template<typename T>
+struct Reflector<const T*&>
+{
+ static auto Create(const T*& val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+
+};
+
+template<typename T>
+struct Reflector<const T*const&>
+{
+ static auto Create(const T*const& val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+
+};
+
+template<typename T>
+struct Reflector<const std::shared_ptr<T>&>
+{
+ static auto Create(const std::shared_ptr<T>& val)
+ {
+ return Reflector<T>::CreateFromPtr(val.get());
+ }
+};
+
+template<typename T>
+struct Reflector<T&>
+{
+ static auto Create(T& val)
+ {
+ return Reflector<T>::Create(val);
+ }
+};
+
+template<typename T>
+struct Reflector<const T*>
+{
+ static auto Create(const T* val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+ static auto CreateFromPtr(const T* val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+};
+
+template<typename T>
+struct Reflector<T*>
+{
+ static auto Create(T* val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+};
+
+template<typename T>
+struct Reflector<std::shared_ptr<T>>
+{
+ static auto Create(std::shared_ptr<T> val)
+ {
+ return Reflector<T>::CreateFromPtr(val);
+ }
+};
+
+template<typename CharT>
+struct Reflector<std::basic_string<CharT>>
+{
+ static auto Create(std::basic_string<CharT> str) {
+ return Value(std::move(str));
+ }
+ static auto CreateFromPtr(const std::basic_string<CharT>* str) {
+ return Value(*str);
+ }
+};
+
+template<typename CharT>
+struct Reflector<std::basic_string_view<CharT>>
+{
+ static auto Create(std::basic_string_view<CharT> str) { return Value(std::move(str)); }
+ static auto CreateFromPtr(const std::basic_string_view<CharT>* str) { return Value(*str); }
+};
+
+template<>
+struct Reflector<bool>
+{
+ static auto Create(bool val)
+ {
+ return Value(val);
+ }
+ static auto CreateFromPtr(const bool* val)
+ {
+ return Value(*val);
+ }
+};
+
+template<>
+struct Reflector<float>
+{
+ static auto Create(double val) { return Value(val); }
+ static auto CreateFromPtr(const float* val) { return Value(static_cast<double>(*val)); }
+};
+
+template<>
+struct Reflector<double>
+{
+ static auto Create(double val) { return Value(val); }
+ static auto CreateFromPtr(const double* val) { return Value(*val); }
+};
+
+#define JINJA2_INT_REFLECTOR(Type) \
+template<> \
+struct Reflector<Type> \
+{ \
+ static auto Create(Type val) \
+ { \
+ return Value(static_cast<int64_t>(val)); \
+ } \
+ static auto CreateFromPtr(const Type* val) \
+ { \
+ return Value(static_cast<int64_t>(*val)); \
+ } \
+}
+
+JINJA2_INT_REFLECTOR(char);
+JINJA2_INT_REFLECTOR(wchar_t);
+JINJA2_INT_REFLECTOR(int8_t);
+JINJA2_INT_REFLECTOR(uint8_t);
+JINJA2_INT_REFLECTOR(int16_t);
+JINJA2_INT_REFLECTOR(uint16_t);
+JINJA2_INT_REFLECTOR(int32_t);
+JINJA2_INT_REFLECTOR(uint32_t);
+JINJA2_INT_REFLECTOR(int64_t);
+JINJA2_INT_REFLECTOR(uint64_t);
+} // namespace detail
+#endif
+
+template<typename T>
+Value Reflect(T&& val)
+{
+ return detail::Reflector<T>::Create(std::forward<T>(val));
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_REFLECTED_VALUE_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/string_helpers.h b/contrib/libs/jinja2cpp/include/jinja2cpp/string_helpers.h
new file mode 100644
index 0000000000..b2bb68872c
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/string_helpers.h
@@ -0,0 +1,296 @@
+#ifndef JINJA2CPP_STRING_HELPERS_H
+#define JINJA2CPP_STRING_HELPERS_H
+
+#include "value.h"
+
+#include <string_view>
+
+#include <cwchar>
+#include <string>
+#include <type_traits>
+
+namespace jinja2
+{
+namespace detail
+{
+ template<typename Src, typename Dst>
+ struct StringConverter;
+
+ template<typename Src>
+ struct StringConverter<Src, Src>
+ {
+ static Src DoConvert(const std::basic_string_view<typename Src::value_type>& from)
+ {
+ return Src(from.begin(), from.end());
+ }
+ };
+
+ template<>
+ struct StringConverter<std::wstring, std::string>
+ {
+ static std::string DoConvert(const std::wstring_view& from)
+ {
+ std::mbstate_t state = std::mbstate_t();
+ auto srcPtr = from.data();
+ std::size_t srcSize = from.size();
+ std::size_t destBytes = 0;
+
+#ifndef _MSC_VER
+ destBytes = std::wcsrtombs(nullptr, &srcPtr, srcSize, &state);
+ if (destBytes == static_cast<std::size_t>(-1))
+ return std::string();
+#else
+ auto err = wcsrtombs_s(&destBytes, nullptr, 0, &srcPtr, srcSize, &state);
+ if (err != 0)
+ return std::string();
+#endif
+ std::string result;
+#ifndef _MSC_VER
+ result.resize(destBytes + 1);
+ auto converted = std::wcsrtombs(&result[0], &srcPtr, srcSize, &state);
+ if (converted == static_cast<std::size_t>(-1))
+ return std::string();
+ result.resize(converted);
+#else
+ result.resize(destBytes);
+ wcsrtombs_s(&destBytes, &result[0], destBytes, &srcPtr, srcSize, &state);
+ result.resize(destBytes - 1);
+#endif
+ return result;
+ }
+ };
+
+ template<>
+ struct StringConverter<std::string, std::wstring>
+ {
+ static std::wstring DoConvert(const std::string_view& from)
+ {
+ std::mbstate_t state = std::mbstate_t();
+ auto srcPtr = from.data();
+ std::size_t srcSize = from.size();
+ std::size_t destBytes = 0;
+
+#ifndef _MSC_VER
+ destBytes = std::mbsrtowcs(nullptr, &srcPtr, srcSize, &state);
+ if (destBytes == static_cast<std::size_t>(-1))
+ return std::wstring();
+#else
+ auto err = mbsrtowcs_s(&destBytes, nullptr, 0, &srcPtr, srcSize, &state);
+ if (err != 0)
+ return std::wstring();
+#endif
+ std::wstring result;
+#ifndef _MSC_VER
+ result.resize(destBytes + 1);
+ srcPtr = from.data();
+ auto converted = std::mbsrtowcs(&result[0], &srcPtr, srcSize, &state);
+ if (converted == static_cast<std::size_t>(-1))
+ return std::wstring();
+ result.resize(converted);
+#else
+ result.resize(destBytes);
+ mbsrtowcs_s(&destBytes, &result[0], destBytes, &srcPtr, srcSize, &state);
+ result.resize(destBytes - 1);
+#endif
+ return result;
+ }
+ };
+
+ template<typename CharT, typename T>
+ struct StringConverter<std::basic_string_view<CharT>, T> : public StringConverter<std::basic_string<CharT>, T> {};
+
+} // namespace detail
+
+/*!
+ * \brief Convert string objects from one representation or another
+ *
+ * Converts string or string views to string objects with possible char conversion (char -> wchar_t or wchar_t -> char).
+ * This function should be used when exact type of string is needed.
+ *
+ * @tparam Dst Destination string type. Mandatory. Can be std::string or std::wstring
+ * @tparam Src Source string type. Auto detected. Can be either std::basic_string<CharT> or std::string_view<CharT>
+ *
+ * @param from Source string object which should be converted
+ * @return Destination string object of the specified type
+ */
+template<typename Dst, typename Src>
+Dst ConvertString(Src&& from)
+{
+ using src_t = std::decay_t<Src>;
+ return detail::StringConverter<src_t, std::decay_t<Dst>>::DoConvert(std::basic_string_view<typename src_t::value_type>(from));
+}
+
+/*!
+ * \brief Gets std::string from std::string
+ *
+ * Helper method for use in template context which gets std::string from the other possible string objects (std::string in this case)
+ *
+ * @param str Source string
+ * @return Copy of the source string
+ */
+inline const std::string AsString(const std::string& str)
+{
+ return str;
+}
+/*!
+ * \brief Gets std::string from std::wstring
+ *
+ * Helper method for use in template context which gets std::string from the other possible string objects (std::wstring in this case)
+ * Conversion wchar_t -> char is performing
+ *
+ * @param str Source string
+ * @return Converted source string
+ */
+inline std::string AsString(const std::wstring& str)
+{
+ return ConvertString<std::string>(str);
+}
+/*!
+ * \brief Gets std::string from std::string_view
+ *
+ * Helper method for use in template context which gets std::string from the other possible string objects (std::string_view in this case)
+ *
+ * @param str Source string
+ * @return Copy of the source string
+ */
+inline std::string AsString(const std::string_view& str)
+{
+ return std::string(str.begin(), str.end());
+}
+/*!
+ * \brief Gets std::string from std::wstring_view
+ *
+ * Helper method for use in template context which gets std::string from the other possible string objects (std::wstring_view in this case)
+ * Conversion wchar_t -> char is performing
+ *
+ * @param str Source string
+ * @return Converted source string
+ */
+inline std::string AsString(const std::wstring_view& str)
+{
+ return ConvertString<std::string>(str);
+}
+/*!
+ * \brief Gets std::wstring from std::wstring
+ *
+ * Helper method for use in template context which gets std::wstring from the other possible string objects (std::wstring in this case)
+ *
+ * @param str Source string
+ * @return Copy of the source string
+ */
+inline const std::wstring AsWString(const std::wstring& str)
+{
+ return str;
+}
+/*!
+ * \brief Gets std::wstring from std::string
+ *
+ * Helper method for use in template context which gets std::wstring from the other possible string objects (std::string in this case)
+ * Conversion char -> wchar_t is performing
+ *
+ * @param str Source string
+ * @return Converted source string
+ */
+inline std::wstring AsWString(const std::string& str)
+{
+ return ConvertString<std::wstring>(str);
+}
+/*!
+ * \brief Gets std::wstring from std::wstring_view
+ *
+ * Helper method for use in template context which gets std::wstring from the other possible string objects (std::wstring_view in this case)
+ *
+ * @param str Source string
+ * @return Copy of the source string
+ */
+inline std::wstring AsWString(const std::wstring_view& str)
+{
+ return std::wstring(str.begin(), str.end());
+}
+/*!
+ * \brief Gets std::wstring from std::string_view
+ *
+ * Helper method for use in template context which gets std::wstring from the other possible string objects (std::string_view in this case)
+ * Conversion char -> wchar_t is performing
+ *
+ * @param str Source string
+ * @return Converted source string
+ */
+inline std::wstring AsWString(const std::string_view& str)
+{
+ return ConvertString<std::wstring>(str);
+}
+
+namespace detail
+{
+struct StringGetter
+{
+ template<typename CharT>
+ std::string operator()(const std::basic_string<CharT>& str) const
+ {
+ return AsString(str);
+ }
+ template<typename CharT>
+ std::string operator()(const std::basic_string_view<CharT>& str) const
+ {
+ return AsString(str);
+ }
+
+ template<typename T>
+ std::string operator()(T&&) const
+ {
+ return std::string();
+ }
+};
+
+struct WStringGetter
+{
+ template<typename CharT>
+ std::wstring operator()(const std::basic_string<CharT>& str) const
+ {
+ return AsWString(str);
+ }
+ template<typename CharT>
+ std::wstring operator()(const std::basic_string_view<CharT>& str) const
+ {
+ return AsWString(str);
+ }
+
+ template<typename T>
+ std::wstring operator()(T&&) const
+ {
+ return std::wstring();
+ }
+};
+} // namespace detail
+/*!
+ * \brief Gets std::string from the arbitrary \ref Value
+ *
+ * Helper method for use in template context which gets std::string from the other possible string objects (Value in this case).
+ * Conversion wchar_t -> char is performing if needed. In case of non-string object actually stored in the \ref Value
+ * empty string is returned.
+ *
+ * @param val Source string
+ * @return Extracted or empty string
+ */
+inline std::string AsString(const Value& val)
+{
+ return std::visit(detail::StringGetter(), val.data());
+}
+/*!
+ * \brief Gets std::wstring from the arbitrary \ref Value
+ *
+ * Helper method for use in template context which gets std::wstring from the other possible string objects (Value in this case).
+ * Conversion char -> wchar_t is performing if needed. In case of non-string object actually stored in the \ref Value
+ * empty string is returned.
+ *
+ * @param val Source string
+ * @return Extracted or empty string
+ */
+inline std::wstring AsWString(const Value& val)
+{
+ return std::visit(detail::WStringGetter(), val.data());
+}
+} // namespace jinja2
+
+#endif // JINJA2CPP_STRING_HELPERS_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/template.h b/contrib/libs/jinja2cpp/include/jinja2cpp/template.h
new file mode 100644
index 0000000000..889dbd2ae3
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/template.h
@@ -0,0 +1,305 @@
+#ifndef JINJA2CPP_TEMPLATE_H
+#define JINJA2CPP_TEMPLATE_H
+
+#include "config.h"
+#include "error_info.h"
+#include "value.h"
+
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+namespace jinja2
+{
+class JINJA2CPP_EXPORT ITemplateImpl;
+class JINJA2CPP_EXPORT TemplateEnv;
+template<typename CharT>
+class TemplateImpl;
+template<typename U>
+using Result = nonstd::expected<U, ErrorInfo>;
+template<typename U>
+using ResultW = nonstd::expected<U, ErrorInfoW>;
+
+template<typename CharT>
+struct MetadataInfo
+{
+ std::string metadataType;
+ std::basic_string_view<CharT> metadata;
+ SourceLocation location;
+};
+
+/*!
+ * \brief Template object which is used to render narrow char templates
+ *
+ * This class is a main class for rendering narrow char templates. It can be used independently or together with
+ * \ref TemplateEnv. In the second case it's possible to use templates inheritance and extension.
+ *
+ * Basic usage of Template class:
+ * ```c++
+ * std::string source = "Hello World from Parser!";
+ *
+ * jinja2::Template tpl;
+ * tpl.Load(source);
+ * std::string result = tpl.RenderAsString(ValuesMap{}).value();
+ * ```
+ */
+class JINJA2CPP_EXPORT Template
+{
+public:
+ /*!
+ * \brief Default constructor
+ */
+ Template()
+ : Template(nullptr)
+ {
+ }
+ /*!
+ * \brief Initializing constructor
+ *
+ * Creates instance of the template with the specified template environment object
+ *
+ * @param env Template environment object which created template should refer to
+ */
+ explicit Template(TemplateEnv* env);
+ /*!
+ * Destructor
+ */
+ ~Template();
+
+ /*!
+ * \brief Load template from the zero-terminated narrow char string
+ *
+ * Takes specified narrow char string and parses it as a Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param tpl Zero-terminated narrow char string with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<void> Load(const char* tpl, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the std::string
+ *
+ * Takes specified std::string object and parses it as a Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param str std::string object with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<void> Load(const std::string& str, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the stream
+ *
+ * Takes specified stream object and parses it as a source of Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param stream Stream object with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<void> Load(std::istream& stream, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the specified file
+ *
+ * Loads file with the specified name and parses it as a source of Jinja2 template. In case of error returns
+ * detailed diagnostic
+ *
+ * @param fileName Name of the file to load
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<void> LoadFromFile(const std::string& fileName);
+
+ /*!
+ * \brief Render previously loaded template to the narrow char stream
+ *
+ * Renders previously loaded template to the specified narrow char stream and specified set of params.
+ *
+ * @param os Stream to render template to
+ * @param params Set of params which should be passed to the template engine and can be used within the template
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<void> Render(std::ostream& os, const ValuesMap& params);
+ /*!
+ * \brief Render previously loaded template to the narrow char string
+ *
+ * Renders previously loaded template as a narrow char string and with specified set of params.
+ *
+ * @param params Set of params which should be passed to the template engine and can be used within the template
+ *
+ * @return Either rendered string or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<std::string> RenderAsString(const ValuesMap& params);
+ /*!
+ * \brief Get metadata, provided in the {% meta %} tag
+ *
+ * @return Parsed metadata as a generic map value or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<GenericMap> GetMetadata();
+ /*!
+ * \brief Get non-parsed metadata, provided in the {% meta %} tag
+ *
+ * @return Non-parsed metadata information or instance of \ref ErrorInfoTpl as an error
+ */
+ Result<MetadataInfo<char>> GetMetadataRaw();
+
+ /* !
+ * \brief compares to an other object of the same type
+ *
+ * @return true if equal
+ */
+ bool IsEqual(const Template& other) const;
+
+private:
+ std::shared_ptr<ITemplateImpl> m_impl;
+ friend class TemplateImpl<char>;
+};
+
+bool operator==(const Template& lhs, const Template& rhs);
+bool operator!=(const Template& lhs, const Template& rhs);
+
+/*!
+ * \brief Template object which is used to render wide char templates
+ *
+ * This class is a main class for rendering wide char templates. It can be used independently or together with
+ * \ref TemplateEnv. In the second case it's possible to use templates inheritance and extension.
+ *
+ * Basic usage of Template class:
+ * ```c++
+ * std::string source = "Hello World from Parser!";
+ *
+ * jinja2::Template tpl;
+ * tpl.Load(source);
+ * std::string result = tpl.RenderAsString(ValuesMap{}).value();
+ * ```
+*/
+class JINJA2CPP_EXPORT TemplateW
+{
+public:
+ /*!
+ * \brief Default constructor
+ */
+ TemplateW()
+ : TemplateW(nullptr)
+ {
+ }
+ /*!
+ * \brief Initializing constructor
+ *
+ * Creates instance of the template with the specified template environment object
+ *
+ * @param env Template environment object which created template should refer to
+ */
+ explicit TemplateW(TemplateEnv* env);
+ /*!
+ * Destructor
+ */
+ ~TemplateW();
+
+ /*!
+ * \brief Load template from the zero-terminated wide char string
+ *
+ * Takes specified wide char string and parses it as a Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param tpl Zero-terminated wide char string with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<void> Load(const wchar_t* tpl, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the std::wstring
+ *
+ * Takes specified std::wstring object and parses it as a Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param str std::wstring object with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<void> Load(const std::wstring& str, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the stream
+ *
+ * Takes specified stream object and parses it as a source of Jinja2 template. In case of error returns detailed
+ * diagnostic
+ *
+ * @param stream Stream object with template description
+ * @param tplName Optional name of the template (for the error reporting purposes)
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<void> Load(std::wistream& stream, std::string tplName = std::string());
+ /*!
+ * \brief Load template from the specified file
+ *
+ * Loads file with the specified name and parses it as a source of Jinja2 template. In case of error returns
+ * detailed diagnostic
+ *
+ * @param fileName Name of the file to load
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<void> LoadFromFile(const std::string& fileName);
+
+ /*!
+ * \brief Render previously loaded template to the wide char stream
+ *
+ * Renders previously loaded template to the specified wide char stream and specified set of params.
+ *
+ * @param os Stream to render template to
+ * @param params Set of params which should be passed to the template engine and can be used within the template
+ *
+ * @return Either noting or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<void> Render(std::wostream& os, const ValuesMap& params);
+ /*!
+ * \brief Render previously loaded template to the wide char string
+ *
+ * Renders previously loaded template as a wide char string and with specified set of params.
+ *
+ * @param params Set of params which should be passed to the template engine and can be used within the template
+ *
+ * @return Either rendered string or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<std::wstring> RenderAsString(const ValuesMap& params);
+ /*!
+ * \brief Get metadata, provided in the {% meta %} tag
+ *
+ * @return Parsed metadata as a generic map value or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<GenericMap> GetMetadata();
+ /*!
+ * \brief Get non-parsed metadata, provided in the {% meta %} tag
+ *
+ * @return Non-parsed metadata information or instance of \ref ErrorInfoTpl as an error
+ */
+ ResultW<MetadataInfo<wchar_t>> GetMetadataRaw();
+
+ /* !
+ * \brief compares to an other object of the same type
+ *
+ * @return true if equal
+ */
+ bool IsEqual(const TemplateW& other) const;
+
+private:
+ std::shared_ptr<ITemplateImpl> m_impl;
+ friend class TemplateImpl<wchar_t>;
+};
+
+bool operator==(const TemplateW& lhs, const TemplateW& rhs);
+bool operator!=(const TemplateW& lhs, const TemplateW& rhs);
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_TEMPLATE_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/template_env.h b/contrib/libs/jinja2cpp/include/jinja2cpp/template_env.h
new file mode 100644
index 0000000000..80dcecc1ec
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/template_env.h
@@ -0,0 +1,320 @@
+#ifndef JINJA2CPP_TEMPLATE_ENV_H
+#define JINJA2CPP_TEMPLATE_ENV_H
+
+#include "config.h"
+#include "error_info.h"
+#include "filesystem_handler.h"
+#include "template.h"
+
+#include <mutex>
+#include <shared_mutex>
+#include <unordered_map>
+
+namespace jinja2
+{
+
+class IErrorHandler;
+class IFilesystemHandler;
+
+//! Compatibility mode for jinja2c++ engine
+enum class Jinja2CompatMode
+{
+ None, //!< Default mode
+ Vesrsion_2_10, //!< Compatibility with Jinja2 v.2.10 specification
+};
+
+//! Global template environment settings
+struct Settings
+{
+ /// Extensions set which should be supported
+ struct Extensions
+ {
+ bool Do = false; //!< Enable use of `do` statement
+ };
+
+ //! Enables use of line statements (yet not supported)
+ bool useLineStatements = false;
+ //! Enables blocks trimming the same way as it does python Jinja2 engine
+ bool trimBlocks = false;
+ //! Enables blocks stripping (from the left) the same way as it does python Jinja2 engine
+ bool lstripBlocks = false;
+ //! Templates cache size
+ int cacheSize = 400;
+ //! If auto_reload is set to true (default) every time a template is requested the loader checks if the source changed and if yes, it will reload the template
+ bool autoReload = true;
+ //! Extensions set enabled for templates
+ Extensions extensions;
+ //! Controls Jinja2 compatibility mode
+ Jinja2CompatMode jinja2CompatMode = Jinja2CompatMode::None;
+ //! Default format for metadata block in the templates
+ std::string m_defaultMetadataType = "json";
+};
+
+inline bool operator==(const Settings& lhs, const Settings& rhs)
+{
+ auto lhsTie = std::tie(lhs.useLineStatements, lhs.trimBlocks, lhs.lstripBlocks, lhs.cacheSize, lhs.autoReload, lhs.extensions.Do, lhs.jinja2CompatMode, lhs.m_defaultMetadataType);
+ auto rhsTie = std::tie(rhs.useLineStatements, rhs.trimBlocks, rhs.lstripBlocks, rhs.cacheSize, rhs.autoReload, rhs.extensions.Do, rhs.jinja2CompatMode, rhs.m_defaultMetadataType);
+ return lhsTie == rhsTie;
+}
+inline bool operator!=(const Settings& lhs, const Settings& rhs)
+{
+ return !(lhs == rhs);
+}
+
+/*!
+ * \brief Global template environment which controls behaviour of the different \ref Template instances
+ *
+ * This class is used for fine tuning of the templates behaviour and for state sharing between them. With this class
+ * it's possible to control template loading, provide template sources, set global variables, use template inheritance
+ * and inclusion.
+ *
+ * It's possible to load templates from the environment via \ref LoadTemplate or \ref LoadTemplateW methods
+ * or to pass instance of the environment directly to the \ref Template via constructor.
+ */
+class JINJA2CPP_EXPORT TemplateEnv
+{
+public:
+ using TimePoint = std::chrono::system_clock::time_point;
+ using TimeStamp = std::chrono::steady_clock::time_point;
+
+ /*!
+ * \brief Returns global settings for the environment
+ *
+ * @return Constant reference to the global settings
+ */
+ const Settings& GetSettings() const {return m_settings;}
+ /*!
+ * \brief Returns global settings for the environment available for modification
+ *
+ * @return Reference to the global settings
+ */
+ Settings& GetSettings() {return m_settings;}
+
+ /*!
+ * \brief Replace global settings for the environment with the new ones
+ *
+ * @param setts New settings
+ */
+ void SetSettings(const Settings& setts) {m_settings = setts;}
+
+ /*!
+ * \brief Add pointer to file system handler with the specified prefix
+ *
+ * Adds filesystem handler which provides access to the external source of templates. With added handlers it's
+ * possible to load templates from the `import`, `extends` and `include` jinja2 tags. Prefix is used for
+ * distinguish one templates source from another. \ref LoadTemplate or \ref LoadTemplateW methods use
+ * handlers to load templates with the specified name.
+ * Method is thread-unsafe. It's dangerous to add new filesystem handlers and load templates simultaneously.
+ *
+ * Basic usage:
+ * ```c++
+ * jinja2::TemplateEnv env;
+ *
+ * auto fs = std::make_shared<jinja2::MemoryFileSystem>();
+ * env.AddFilesystemHandler(std::string(), fs);
+ * fs->AddFile("base.j2tpl", "Hello World!");
+ * ```
+ *
+ * @param prefix Optional prefix of the handler's filesystem. Prefix is a part of the file name and passed to the handler's \ref IFilesystemHandler::OpenStream method
+ * @param h Shared pointer to the handler
+ */
+ void AddFilesystemHandler(std::string prefix, FilesystemHandlerPtr h)
+ {
+ m_filesystemHandlers.push_back(FsHandler{std::move(prefix), std::move(h)});
+ }
+ /*!
+ * \brief Add reference to file system handler with the specified prefix
+ *
+ * Adds filesystem handler which provides access to the external source of templates. With added handlers it's
+ * possible to load templates from the `import`, `extends` and `include` jinja2 tags. Prefix is used for
+ * distinguish one templates source from another. \ref LoadTemplate or \ref LoadTemplateW methods use
+ * handlers to load templates with the specified name.
+ * Method is thread-unsafe. It's dangerous to add new filesystem handlers and load templates simultaneously.
+ *
+ * Basic usage:
+ * ```c++
+ * jinja2::TemplateEnv env;
+ *
+ * MemoryFileSystem fs;
+ * env.AddFilesystemHandler(std::string(), fs);
+ * fs.AddFile("base.j2tpl", "Hello World!");
+ * ```
+ *
+ * @param prefix Optional prefix of the handler's filesystem. Prefix is a part of the file name and passed to the handler's \ref IFilesystemHandler::OpenStream method
+ * @param h Reference to the handler. It's assumed that lifetime of the handler is controlled externally
+ */
+ void AddFilesystemHandler(std::string prefix, IFilesystemHandler& h)
+ {
+ m_filesystemHandlers.push_back(FsHandler{std::move(prefix), std::shared_ptr<IFilesystemHandler>(&h, [](auto*) {})});
+ }
+ /*!
+ * \brief Load narrow char template with the specified name via registered file handlers
+ *
+ * In case of specified file present in any of the registered handlers, template is loaded and parsed. If any
+ * error occurred during the loading or parsing detailed diagnostic will be returned.
+ * Method is thread-unsafe. It's dangerous to add new filesystem handlers and load templates simultaneously.
+ *
+ * @param fileName Template name to load
+ *
+ * @return Either loaded template or load/parse error. See \ref ErrorInfoTpl
+ */
+ nonstd::expected<Template, ErrorInfo> LoadTemplate(std::string fileName);
+ /*!
+ * \brief Load wide char template with the specified name via registered file handlers
+ *
+ * In case of specified file present in any of the registered handlers, template is loaded and parsed. If any
+ * error occurred during the loading or parsing detailed diagnostic will be returned.
+ * Method is thread-unsafe. It's dangerous to add new filesystem handlers and load templates simultaneously.
+ *
+ * @param fileName Template name to load
+ *
+ * @return Either loaded template or load/parse error. See \ref ErrorInfoTpl
+ */
+ nonstd::expected<TemplateW, ErrorInfoW> LoadTemplateW(std::string fileName);
+
+ /*!
+ * \brief Add global variable to the environment
+ *
+ * Adds global variable which can be referred in any template which is loaded within this environment object.
+ * Method is thread-safe.
+ *
+ * @param name Name of the variable
+ * @param val Value of the variable
+ */
+ void AddGlobal(std::string name, Value val)
+ {
+ std::unique_lock<std::shared_timed_mutex> l(m_guard);
+ m_globalValues[std::move(name)] = std::move(val);
+ }
+ /*!
+ * \brief Remove global variable from the environment
+ *
+ * Removes global variable from the environment.
+ * Method is thread-safe.
+ *
+ * @param name Name of the variable
+ */
+ void RemoveGlobal(const std::string& name)
+ {
+ std::unique_lock<std::shared_timed_mutex> l(m_guard);
+ m_globalValues.erase(name);
+ }
+
+ /*!
+ * \brief Call the specified function with the current set of global variables under the internal lock
+ *
+ * Main purpose of this method is to help external code to enumerate global variables thread-safely. Provided functional object is called under the
+ * internal lock with the current set of global variables as an argument.
+ *
+ * @tparam Fn Type of the functional object to call
+ * @param fn Functional object to call
+ */
+ template<typename Fn>
+ void ApplyGlobals(Fn&& fn)
+ {
+ std::shared_lock<std::shared_timed_mutex> l(m_guard);
+ fn(m_globalValues);
+ }
+
+ bool IsEqual(const TemplateEnv& other) const
+ {
+ if (m_filesystemHandlers != other.m_filesystemHandlers)
+ return false;
+ if (m_settings != other.m_settings)
+ return false;
+ if (m_globalValues != other.m_globalValues)
+ return false;
+ if (m_templateCache != other.m_templateCache)
+ return false;
+ if (m_templateWCache != other.m_templateWCache)
+ return false;
+
+ return true;
+ }
+
+private:
+ template<typename CharT, typename T, typename Cache>
+ auto LoadTemplateImpl(TemplateEnv* env, std::string fileName, const T& filesystemHandlers, Cache& cache);
+
+
+private:
+ struct FsHandler
+ {
+ std::string prefix;
+ FilesystemHandlerPtr handler;
+ bool operator==(const FsHandler& rhs) const
+ {
+ if (prefix != rhs.prefix)
+ return false;
+ if (handler && rhs.handler && !handler->IsEqual(*rhs.handler))
+ return false;
+ if ((!handler && rhs.handler) || (handler && !rhs.handler))
+ return false;
+ return true;
+ }
+ bool operator!=(const FsHandler& rhs) const
+ {
+ return !(*this == rhs);
+ }
+ };
+
+ struct BaseTemplateInfo
+ {
+ std::optional<TimePoint> lastModification;
+ TimeStamp lastAccessTime;
+ FilesystemHandlerPtr handler;
+ bool operator==(const BaseTemplateInfo& other) const
+ {
+ if (lastModification != other.lastModification)
+ return false;
+ if (lastAccessTime != other.lastAccessTime)
+ return false;
+ if (handler && other.handler && !handler->IsEqual(*other.handler))
+ return false;
+ if ((!handler && other.handler) || (handler && !other.handler))
+ return false;
+ return true;
+ }
+ bool operator!=(const BaseTemplateInfo& other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ struct TemplateCacheEntry : public BaseTemplateInfo
+ {
+ Template tpl;
+ bool operator==(const TemplateCacheEntry& other) const
+ {
+ return BaseTemplateInfo::operator==(other) && tpl == other.tpl;
+ }
+ bool operator!=(const TemplateCacheEntry& other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ struct TemplateWCacheEntry : public BaseTemplateInfo
+ {
+ TemplateW tpl;
+ bool operator==(const TemplateWCacheEntry& other) const
+ {
+ return BaseTemplateInfo::operator==(other) && tpl == other.tpl;
+ }
+ bool operator!=(const TemplateWCacheEntry& other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ std::vector<FsHandler> m_filesystemHandlers;
+ Settings m_settings;
+ ValuesMap m_globalValues;
+ std::shared_timed_mutex m_guard;
+ std::unordered_map<std::string, TemplateCacheEntry> m_templateCache;
+ std::unordered_map<std::string, TemplateWCacheEntry> m_templateWCache;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_TEMPLATE_ENV_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/utils/i_comparable.h b/contrib/libs/jinja2cpp/include/jinja2cpp/utils/i_comparable.h
new file mode 100644
index 0000000000..76f58d5c88
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/utils/i_comparable.h
@@ -0,0 +1,16 @@
+#ifndef JINJA2CPP_ICOMPARABLE_H
+#define JINJA2CPP_ICOMPARABLE_H
+
+#include <jinja2cpp/config.h>
+
+namespace jinja2 {
+
+struct JINJA2CPP_EXPORT IComparable
+{
+ virtual ~IComparable() {}
+ virtual bool IsEqual(const IComparable& other) const = 0;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_ICOMPARABLE_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/value.h b/contrib/libs/jinja2cpp/include/jinja2cpp/value.h
new file mode 100644
index 0000000000..545294fcd5
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/value.h
@@ -0,0 +1,763 @@
+#ifndef JINJA2CPP_VALUE_H
+#define JINJA2CPP_VALUE_H
+
+#include <jinja2cpp/generic_list.h>
+#include <jinja2cpp/utils/i_comparable.h>
+#include <jinja2cpp/value_ptr.h>
+
+#include <variant>
+#include <optional>
+#include <string_view>
+
+#include <atomic>
+#include <vector>
+#include <unordered_map>
+#include <string>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace jinja2
+{
+//! Empty value container
+struct EmptyValue
+{
+ template<typename T>
+ operator T() const {return T{};}
+};
+
+inline bool operator==(const EmptyValue& lhs, const EmptyValue& rhs)
+{
+ (void)lhs;
+ (void)rhs;
+ return true;
+}
+
+class Value;
+
+/*!
+ * \brief Interface to the generic dictionary type which maps string to some value
+ */
+struct IMapItemAccessor : IComparable
+{
+ //! Destructor
+ virtual ~IMapItemAccessor() = default;
+
+ //! Method is called to obtain number of items in the dictionary. Maximum possible size_t value means non-calculable size
+ virtual size_t GetSize() const = 0;
+
+ /*!
+ * \brief Method is called to check presence of the item in the dictionary
+ *
+ * @param name Name of the item
+ *
+ * @return true if item is present and false otherwise.
+ */
+ virtual bool HasValue(const std::string& name) const = 0;
+ /*!
+ * \brief Method is called for retrieving the value by specified name
+ *
+ * @param name Name of the value to retrieve
+ *
+ * @return Requestd value or empty \ref Value if item is absent
+ */
+ virtual Value GetValueByName(const std::string& name) const = 0;
+ /*!
+ * \brief Method is called for retrieving collection of keys in the dictionary
+ *
+ * @return Collection of keys if any. Ordering of keys is unspecified.
+ */
+ virtual std::vector<std::string> GetKeys() const = 0;
+
+ /*!
+ * \brief Compares to object of the same type
+ *
+ * @return true if equal
+ */
+// virtual bool IsEqual(const IMapItemAccessor& rhs) const = 0;
+};
+
+/*!
+ * \brief Helper class for accessing maps specified by the \ref IMapItemAccessor interface
+ *
+ * In the \ref Value type can be stored either ValuesMap instance or GenericMap instance. ValuesMap is a simple
+ * dictionary object based on std::unordered_map. Rather than GenericMap is a more robust object which can provide
+ * access to the different types of dictionary entities. GenericMap takes the \ref IMapItemAccessor interface instance
+ * and uses it to access particular items in the dictionaries.
+ */
+class GenericMap
+{
+public:
+ //! Default constructor
+ GenericMap() = default;
+
+ /*!
+ * \brief Initializing constructor
+ *
+ * The only one way to get valid non-empty GeneridMap is to construct it with the specified \ref IMapItemAccessor
+ * implementation provider. This provider is a functional object which returns pointer to the interface instance.
+ *
+ * @param accessor Functional object which returns pointer to the \ref IMapItemAccessor interface
+ */
+ explicit GenericMap(std::function<const IMapItemAccessor* ()> accessor)
+ : m_accessor(std::move(accessor))
+ {
+ }
+
+ /*!
+ * \brief Check the presence the specific item in the dictionary
+ *
+ * @param name Name of the the item
+ *
+ * @return true of item is present and false otherwise
+ */
+ bool HasValue(const std::string& name) const
+ {
+ return m_accessor ? m_accessor()->HasValue(name) : false;
+ }
+
+ /*!
+ * \brief Get specific item from the dictionary
+ *
+ * @param name Name of the item to get
+ *
+ * @return Value of the item or empty \ref Value if no item
+ */
+ Value GetValueByName(const std::string& name) const;
+ /*!
+ * \brief Get size of the dictionary
+ *
+ * @return Size of the dictionary
+ */
+ size_t GetSize() const
+ {
+ return m_accessor ? m_accessor()->GetSize() : 0;
+ }
+ /*!
+ * \brief Get collection of keys from the dictionary
+ *
+ * @return Collection of the keys or empty collection if no keys
+ */
+ auto GetKeys() const
+ {
+ return m_accessor ? m_accessor()->GetKeys() : std::vector<std::string>();
+ }
+ /*!
+ * \brief Get the underlying access interface to the dictionary
+ *
+ * @return Pointer to the underlying interface or nullptr if no
+ */
+ auto GetAccessor() const
+ {
+ return m_accessor();
+ }
+
+ auto operator[](const std::string& name) const;
+
+private:
+ std::function<const IMapItemAccessor* ()> m_accessor;
+};
+
+bool operator==(const GenericMap& lhs, const GenericMap& rhs);
+bool operator!=(const GenericMap& lhs, const GenericMap& rhs);
+
+using ValuesList = std::vector<Value>;
+struct ValuesMap;
+struct UserCallableArgs;
+struct ParamInfo;
+struct UserCallable;
+
+template<typename T>
+using RecWrapper = types::ValuePtr<T>;
+
+/*!
+ * \brief Generic value class
+ *
+ * Variant-based class which is used for passing values to and from Jinja2C++ template engine. This class store the
+ * following types of values:
+ *
+ * - EmptyValue. In this case instance of this class threated as 'empty'
+ * - Boolean value.
+ * - String value.
+ * - Wide string value
+ * - String view value (std::string_view)
+ * - Wide string view value (std::wstring_view)
+ * - integer (int64_t) value
+ * - floating point (double) value
+ * - Simple list of other values (\ref ValuesList)
+ * - Simple map of other values (\ref ValuesMap)
+ * - Generic list of other values (\ref GenericList)
+ * - Generic map of other values (\ref GenericMap)
+ * - User-defined callable (\ref UserCallable)
+ *
+ * Exact value can be accessed via std::visit method applied to the result of the Value::data() call or any of
+ * asXXX method (ex. \ref Value::asString). In case of string retrieval it's better to use \ref AsString or \ref
+ * AsWString functions. Thay hide all nececcary transformations between various types of strings (or string views).
+ */
+class Value
+{
+public:
+ using ValueData = std::variant<
+ EmptyValue,
+ bool,
+ std::string,
+ std::wstring,
+ std::string_view,
+ std::wstring_view,
+ int64_t,
+ double,
+ RecWrapper<ValuesList>,
+ RecWrapper<ValuesMap>,
+ GenericList,
+ GenericMap,
+ RecWrapper<UserCallable>
+ >;
+
+ template<typename T, typename ... L>
+ struct AnyOf : public std::false_type {};
+
+ template<typename T, typename H, typename ... L>
+ struct AnyOf<T, H, L...> : public std::integral_constant<bool, std::is_same<std::decay_t<T>, H>::value || AnyOf<T, L...>::value> {};
+
+ //! Default constructor
+ Value();
+ //! Copy constructor
+ Value(const Value& val);
+ //! Move constructor
+ Value(Value&& val) noexcept;
+ //! Desctructor
+ // ~Value();
+ ~Value();
+
+ //! Assignment operator
+ Value& operator=(const Value&);
+ //! Move assignment operator
+ Value& operator=(Value&&) noexcept;
+ /*!
+ * \brief Generic initializing constructor
+ *
+ * Creates \ref Value from the arbitrary type which is compatible with types listed in \ref Value::ValueData
+ *
+ * @tparam T Type of value to create \ref Value instance from
+ * @param val Value which should be used to initialize \ref Value instance
+ */
+ template<typename T>
+ Value(T&& val, typename std::enable_if<!AnyOf<T, Value, ValuesList, ValuesMap, UserCallable>::value>::type* = nullptr)
+ : m_data(std::forward<T>(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from pointer to the null-terminated narrow string
+ *
+ * @param val Null-terminated string which should be used to initialize \ref Value instance
+ */
+ Value(const char* val)
+ : m_data(std::string(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from pointer to the null-terminated wide string
+ *
+ * @param val Null-terminated string which should be used to initialize \ref Value instance
+ */
+ Value(const wchar_t* val)
+ : m_data(std::wstring(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from the narrow string literal
+ *
+ * @param val String literal which should be used to initialize \ref Value instance
+ */
+ template<size_t N>
+ Value(char (&val)[N])
+ : m_data(std::string(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from the wide string literal
+ *
+ * @param val String literal which should be used to initialize \ref Value instance
+ */
+ template<size_t N>
+ Value(wchar_t (&val)[N])
+ : m_data(std::wstring(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from the int value
+ *
+ * @param val Integer value which should be used to initialize \ref Value instance
+ */
+ Value(int val)
+ : m_data(static_cast<int64_t>(val))
+ {
+ }
+ /*!
+ * \brief Initializing constructor from the \ref ValuesList
+ *
+ * @param list List of values which should be used to initialize \ref Value instance
+ */
+ Value(const ValuesList& list);
+ /*!
+ * \brief Initializing constructor from the \ref ValuesMap
+ *
+ * @param map Map of values which should be used to initialize \ref Value instance
+ */
+ Value(const ValuesMap& map);
+ /*!
+ * \brief Initializing constructor from the \ref UserCallable
+ *
+ * @param callable UserCallable which should be used to initialize \ref Value instance
+ */
+ Value(const UserCallable& callable);
+ /*!
+ * \brief Initializing move constructor from the \ref ValuesList
+ *
+ * @param list List of values which should be used to initialize \ref Value instance
+ */
+ Value(ValuesList&& list) noexcept;
+ /*!
+ * \brief Initializing move constructor from the \ref ValuesMap
+ *
+ * @param map Map of values which should be used to initialize \ref Value instance
+ */
+ Value(ValuesMap&& map) noexcept;
+ /*!
+ * \brief Initializing move constructor from the \ref UserCallable
+ *
+ * @param callable UserCallable which should be used to initialize \ref Value instance
+ */
+ Value(UserCallable&& callable);
+
+ /*!
+ * \brief Get the non-mutable stored data object
+ *
+ * Returns the stored data object in order to get the typed value from it. For instance:
+ * ```c++
+ * inline std::string AsString(const jinja2::Value& val)
+ * {
+ * return std::visit(StringGetter(), val.data());
+ * }
+ * ```
+ *
+ * @return Non-mutable stored data object
+ */
+ const ValueData& data() const {return m_data;}
+ /*!
+ * \brief Get the mutable stored data object
+ *
+ * Returns the stored data object in order to get the typed value from it. For instance:
+ * ```c++
+ * inline std::string AsString(Value& val)
+ * {
+ * return std::visit(StringGetter(), val.data());
+ * }
+ * ```
+ *
+ * @return Mutable stored data object
+ */
+ ValueData& data() {return m_data;}
+
+ //! Test Value for containing std::string object
+ bool isString() const
+ {
+ return std::get_if<std::string>(&m_data) != nullptr;
+ }
+ /*!
+ * \brief Returns mutable containing std::string object
+ *
+ * Returns containing std::string object. Appropriate exception is thrown in case non-string containing value
+ *
+ * @return Mutable containing std::string object
+ */
+ auto& asString()
+ {
+ return std::get<std::string>(m_data);
+ }
+ /*!
+ * \brief Returns non-mutable containing std::string object
+ *
+ * Returns containing std::string object. Appropriate exception is thrown in case of non-string containing value
+ *
+ * @return Non-mutable containing std::string object
+ */
+ auto& asString() const
+ {
+ return std::get<std::string>(m_data);
+ }
+
+ //! Test Value for containing std::wstring object
+ bool isWString() const
+ {
+ return std::get_if<std::wstring>(&m_data) != nullptr;
+ }
+ /*!
+ * \brief Returns mutable containing std::wstring object
+ *
+ * Returns containing std::wstring object. Appropriate exception is thrown in case of non-wstring containing value
+ *
+ * @return Mutable containing std::wstring object
+ */
+ auto& asWString()
+ {
+ return std::get<std::wstring>(m_data);
+ }
+ /*!
+ * \brief Returns non-mutable containing std::wstring object
+ *
+ * Returns containing std::wstring object. Appropriate exception is thrown in case of non-wstring containing value
+ *
+ * @return Non-mutable containing std::wstring object
+ */
+ auto& asWString() const
+ {
+ return std::get<std::wstring>(m_data);
+ }
+
+ //! Test Value for containing jinja2::ValuesList object
+ bool isList() const
+ {
+ return std::get_if<RecWrapper<ValuesList>>(&m_data) != nullptr || std::get_if<GenericList>(&m_data) != nullptr;
+ }
+ /*!
+ * \brief Returns mutable containing jinja2::ValuesList object
+ *
+ * Returns containing jinja2::ValuesList object. Appropriate exception is thrown in case of non-Valueslist containing value
+ *
+ * @return Mutable containing jinja2::ValuesList object
+ */
+ auto& asList()
+ {
+ return *std::get<RecWrapper<ValuesList>>(m_data);
+ }
+ /*!
+ * \brief Returns non-mutable containing jinja2::ValuesList object
+ *
+ * Returns containing jinja2::ValuesList object. Appropriate exception is thrown in case of non-Valueslist containing value
+ *
+ * @return Non-mutable containing jinja2::ValuesList object
+ */
+ auto& asList() const
+ {
+ return *std::get<RecWrapper<ValuesList>>(m_data);
+ }
+ //! Test Value for containing jinja2::ValuesMap object
+ bool isMap() const
+ {
+ return std::get_if<RecWrapper<ValuesMap>>(&m_data) != nullptr || std::get_if<GenericMap>(&m_data) != nullptr;
+ }
+ /*!
+ * \brief Returns mutable containing jinja2::ValuesMap object
+ *
+ * Returns containing jinja2::ValuesMap object. Appropriate exception is thrown in case of non-ValuesMap containing value
+ *
+ * @return Mutable containing jinja2::ValuesMap object
+ */
+ auto& asMap()
+ {
+ return *std::get<RecWrapper<ValuesMap>>(m_data);
+ }
+ /*!
+ * \brief Returns non-mutable containing jinja2::ValuesMap object
+ *
+ * Returns containing jinja2::ValuesMap object. Appropriate exception is thrown in case of non-ValuesMap containing value
+ *
+ * @return Non-mutable containing jinja2::ValuesMap object
+ */
+ auto& asMap() const
+ {
+ return *std::get<RecWrapper<ValuesMap>>(m_data);
+ }
+
+ template<typename T>
+ auto get()
+ {
+ return std::get<T>(m_data);
+ }
+
+ template<typename T>
+ auto get() const
+ {
+ return std::get<T>(m_data);
+ }
+
+ template<typename T>
+ auto getPtr()
+ {
+ return std::get_if<T>(&m_data); // m_data.index() == ValueData::template index_of<T>() ? &m_data.get<T>() : nullptr;
+ }
+
+ template<typename T>
+ auto getPtr() const
+ {
+ return std::get_if<T>(&m_data); // m_data.index() == ValueData::template index_of<T>() ? &m_data.get<T>() : nullptr;
+ }
+
+ //! Test Value for emptyness
+ bool isEmpty() const
+ {
+ return std::get_if<EmptyValue>(&m_data) != nullptr;
+ }
+
+ bool IsEqual(const Value& rhs) const;
+
+private:
+ ValueData m_data;
+};
+
+JINJA2CPP_EXPORT bool operator==(const Value& lhs, const Value& rhs);
+JINJA2CPP_EXPORT bool operator!=(const Value& lhs, const Value& rhs);
+bool operator==(const types::ValuePtr<Value>& lhs, const types::ValuePtr<Value>& rhs);
+bool operator!=(const types::ValuePtr<Value>& lhs, const types::ValuePtr<Value>& rhs);
+bool operator==(const types::ValuePtr<std::vector<Value>>& lhs, const types::ValuePtr<std::vector<Value>>& rhs);
+bool operator!=(const types::ValuePtr<std::vector<Value>>& lhs, const types::ValuePtr<std::vector<Value>>& rhs);
+
+struct ValuesMap : std::unordered_map<std::string, Value>
+{
+ using unordered_map::unordered_map;
+};
+
+bool operator==(const types::ValuePtr<ValuesMap>& lhs, const types::ValuePtr<ValuesMap>& rhs);
+bool operator!=(const types::ValuePtr<ValuesMap>& lhs, const types::ValuePtr<ValuesMap>& rhs);
+
+/*!
+ * \brief Information about user-callable parameters passed from Jinja2 call context
+ *
+ * This structure prepared by the Jinja2C++ engine and filled by information about call parameters gathered from the
+ * call context. See documentation for \ref UserCallable for detailed information
+ *
+ */
+struct UserCallableParams
+{
+ //! Values of parameters mapped according to \ref UserCallable::argsInfo
+ ValuesMap args;
+ //! Values of extra positional args got from the call expression
+ Value extraPosArgs;
+ //! Values of extra named args got from the call expression
+ Value extraKwArgs;
+ //! Context object which provides access to the current variables set of the template
+ Value context;
+ bool paramsParsed = false;
+
+ Value operator[](const std::string& paramName) const
+ {
+ auto p = args.find(paramName);
+ if (p == args.end())
+ return Value();
+
+ return p->second;
+ }
+};
+
+/*!
+ * \brief Information about one argument of the user-defined callable
+ *
+ * This structure is used as a description of the user-callable argument. Information from this structure is used
+ * by the Jinja2C++ engine to map actual call parameters to the expected ones by the user-defined callable.
+ */
+struct ArgInfo
+{
+ //! Name of the argument
+ std::string paramName;
+ //! Mandatory flag
+ bool isMandatory;
+ //! Default value for the argument
+ Value defValue;
+
+ ArgInfo(std::string name, bool isMandat = false, Value defVal = Value())
+ : paramName(std::move(name))
+ , isMandatory(isMandat)
+ , defValue(std::move(defVal)) {}
+};
+
+inline bool operator==(const ArgInfo& lhs, const ArgInfo& rhs)
+{
+ if (lhs.paramName != rhs.paramName)
+ return false;
+ if (lhs.isMandatory != rhs.isMandatory)
+ return false;
+ return lhs.defValue == rhs.defValue;
+}
+
+inline bool operator!=(const ArgInfo& lhs, const ArgInfo& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename T>
+struct ArgInfoT : public ArgInfo
+{
+ using type = T;
+
+ using ArgInfo::ArgInfo;
+ ArgInfoT(const ArgInfo& info)
+ : ArgInfo(info)
+ {
+ }
+ ArgInfoT(ArgInfo&& info) noexcept
+ : ArgInfo(std::move(info))
+ {
+ }
+};
+
+/*!
+ * \brief User-callable descriptor
+ *
+ * This descriptor is used for description of the user-defined callables passed to the Jinja2C++ engine. Information
+ * from this descriptor is used by the engine to properly parse and prepare of the call parameters and pass it to the
+ * user-callable. For instance, such kind of user-defined callable passed as a parameter:
+ * ```c++
+ * jinja2::UserCallable uc;
+ * uc.callable = [](auto& params)->jinja2::Value {
+ * auto str1 = params["str1"];
+ * auto str2 = params["str2"];
+ *
+ * if (str1.isString())
+ * return str1.asString() + " " + str2.asString();
+ *
+ * return str1.asWString() + L" " + str2.asWString();
+ * };
+ * uc.argsInfo = {{"str1", true}, {"str2", true}};
+ * params["test"] = std::move(uc);
+ * ```
+ * This declaration defines user-defined callable which takes two named parameters: `str1` and `str2`. Further, it's
+ * possible to call this user-defined callable from the Jinja2 template this way:
+ * ```jinja2
+ * {{ test('Hello', 'World!') }}
+ * ```
+ * or:
+ * ```
+ * {{ test(str2='World!', str1='Hello') }}
+ * ```
+ * Jinja2C++ engine maps actual call parameters according the information from \ref UserCallable::argsInfo field and
+ * pass them as a \ref UserCallableParams structure. Every named param (explicitly defined in the call or it's default value)
+ * passed throught \ref UserCallableParams::args field. Every extra positional param mentoined in call passed as \ref
+ * UserCallableParams::extraPosArgs. Every extra named param mentoined in call passed as \ref
+ * UserCallableParams::extraKwArgs.
+ *
+ * If any of argument, marked as `mandatory` in the \ref UserCallable::argsInfo field is missed in the point of the
+ * user-defined call the call is failed.
+ */
+struct JINJA2CPP_EXPORT UserCallable
+{
+ using UserCallableFunctionPtr = std::function<Value (const UserCallableParams&)>;
+ UserCallable() : m_counter(++m_gen) {}
+ UserCallable(const UserCallableFunctionPtr& fptr, const std::vector<ArgInfo>& argsInfos)
+ : callable(fptr)
+ , argsInfo(argsInfos)
+ , m_counter(++m_gen)
+ {
+ }
+ UserCallable(const UserCallable& other)
+ : callable(other.callable)
+ , argsInfo(other.argsInfo)
+ , m_counter(other.m_counter)
+ {
+ }
+ UserCallable& operator=(const UserCallable& other)
+ {
+ if (*this == other)
+ return *this;
+ UserCallable temp(other);
+
+ using std::swap;
+ swap(callable, temp.callable);
+ swap(argsInfo, temp.argsInfo);
+ swap(m_counter, temp.m_counter);
+
+ return *this;
+ }
+
+ UserCallable(UserCallable&& other) noexcept
+ : callable(std::move(other.callable))
+ , argsInfo(std::move(other.argsInfo))
+ , m_counter(other.m_counter)
+ {
+ }
+
+ UserCallable& operator=(UserCallable&& other) noexcept
+ {
+ callable = std::move(other.callable);
+ argsInfo = std::move(other.argsInfo);
+ m_counter = other.m_counter;
+
+ return *this;
+ }
+
+ bool IsEqual(const UserCallable& other) const
+ {
+ return m_counter == other.m_counter;
+ }
+
+ //! Functional object which is actually handle the call
+ UserCallableFunctionPtr callable;
+ //! Information about arguments of the user-defined callable
+ std::vector<ArgInfo> argsInfo;
+
+private:
+ static std::atomic_uint64_t m_gen;
+ uint64_t m_counter{};
+};
+
+bool operator==(const UserCallable& lhs, const UserCallable& rhs);
+bool operator!=(const UserCallable& lhs, const UserCallable& rhs);
+bool operator==(const types::ValuePtr<UserCallable>& lhs, const types::ValuePtr<UserCallable>& rhs);
+bool operator!=(const types::ValuePtr<UserCallable>& lhs, const types::ValuePtr<UserCallable>& rhs);
+
+
+inline Value::Value(const UserCallable& callable)
+ : m_data(types::MakeValuePtr<UserCallable>(callable))
+{
+}
+
+inline Value::Value(UserCallable&& callable)
+ : m_data(types::MakeValuePtr<UserCallable>(std::move(callable)))
+{
+}
+
+inline Value GenericMap::GetValueByName(const std::string& name) const
+{
+ return m_accessor ? m_accessor()->GetValueByName(name) : Value();
+}
+inline auto GenericMap::operator[](const std::string& name) const
+{
+ return GetValueByName(name);
+}
+
+inline Value::Value() = default;
+inline Value::Value(const Value& val) = default;
+inline Value::Value(Value&& val) noexcept
+ : m_data(std::move(val.m_data))
+{
+}
+inline Value::~Value() = default;
+inline Value& Value::operator=(const Value&) = default;
+inline Value& Value::operator=(Value&& val) noexcept
+{
+ if (this == &val)
+ return *this;
+
+ m_data.swap(val.m_data);
+ return *this;
+}
+inline Value::Value(const ValuesMap& map)
+ : m_data(types::MakeValuePtr<ValuesMap>(map))
+{
+}
+inline Value::Value(const ValuesList& list)
+ : m_data(types::MakeValuePtr<ValuesList>(list))
+{
+}
+inline Value::Value(ValuesList&& list) noexcept
+ : m_data(types::MakeValuePtr<ValuesList>(std::move(list)))
+{
+}
+inline Value::Value(ValuesMap&& map) noexcept
+ : m_data(types::MakeValuePtr<ValuesMap>(std::move(map)))
+{
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_VALUE_H
diff --git a/contrib/libs/jinja2cpp/include/jinja2cpp/value_ptr.h b/contrib/libs/jinja2cpp/include/jinja2cpp/value_ptr.h
new file mode 100644
index 0000000000..26eee6b6bb
--- /dev/null
+++ b/contrib/libs/jinja2cpp/include/jinja2cpp/value_ptr.h
@@ -0,0 +1,29 @@
+#ifndef JINJA2CPP_VALUE_PTR_H
+#define JINJA2CPP_VALUE_PTR_H
+
+#include "polymorphic_value.h"
+
+namespace jinja2 {
+namespace types {
+
+using namespace isocpp_p0201;
+
+template<typename T>
+using ValuePtr = polymorphic_value<T>;
+
+template<class T, class... Ts>
+ValuePtr<T> MakeValuePtr(Ts&&... ts)
+{
+ return make_polymorphic_value<T>(std::forward<Ts&&>(ts)...);
+}
+
+template<class T, class U, class... Ts>
+ValuePtr<T> MakeValuePtr(Ts&&... ts)
+{
+ return make_polymorphic_value<T, U>(std::forward<Ts>(ts)...);
+}
+
+} // namespace types
+} // namespace jinja2
+
+#endif // JINJA2CPP_VALUE_PTR_H
diff --git a/contrib/libs/jinja2cpp/src/ast_visitor.h b/contrib/libs/jinja2cpp/src/ast_visitor.h
new file mode 100644
index 0000000000..01b519829b
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/ast_visitor.h
@@ -0,0 +1,117 @@
+#ifndef JINJA2CPP_SRC_AST_VISITOR_H
+#define JINJA2CPP_SRC_AST_VISITOR_H
+
+namespace jinja2
+{
+class IRendererBase;
+class ExpressionEvaluatorBase;
+class Statement;
+class ForStatement;
+class IfStatement;
+class ElseBranchStatement;
+class SetStatement;
+class ParentBlockStatement;
+class BlockStatement;
+class ExtendsStatement;
+class IncludeStatement;
+class ImportStatement;
+class MacroStatement;
+class MacroCallStatement;
+class ComposedRenderer;
+class RawTextRenderer;
+class ExpressionRenderer;
+
+class StatementVisitor;
+
+class VisitableStatement
+{
+public:
+ virtual ~VisitableStatement() = default;
+
+ virtual void ApplyVisitor(StatementVisitor* visitor) = 0;
+ virtual void ApplyVisitor(StatementVisitor* visitor) const = 0;
+};
+
+#define VISITABLE_STATEMENT() \
+ void ApplyVisitor(StatementVisitor* visitor) override {visitor->DoVisit(this);} \
+ void ApplyVisitor(StatementVisitor* visitor) const override {visitor->DoVisit(this);} \
+
+namespace detail
+{
+template<typename Base, typename Type>
+class VisitorIfaceImpl : public Base
+{
+public:
+ using Base::DoVisit;
+
+ virtual void DoVisit(Type*) {}
+ virtual void DoVisit(const Type*) {}
+};
+
+template<typename Type>
+class VisitorIfaceImpl<void, Type>
+{
+public:
+ virtual void DoVisit(Type*) {}
+ virtual void DoVisit(const Type*) {}
+};
+
+template<typename Base, typename ... Types>
+struct VisitorBaseImpl;
+
+template<typename Base, typename T, typename ... Types>
+struct VisitorBaseImpl<Base, T, Types...>
+{
+ using current_base = VisitorIfaceImpl<Base, T>;
+ using base_type = typename VisitorBaseImpl<current_base, Types...>::base_type;
+};
+
+template<typename Base, typename T>
+struct VisitorBaseImpl<Base, T>
+{
+ using base_type = VisitorIfaceImpl<Base, T>;
+};
+
+
+template<typename ... Types>
+struct VisitorBase
+{
+ using type = typename VisitorBaseImpl<void, Types ...>::base_type;
+};
+} // namespace detail
+
+template<typename ... Types>
+using VisitorBase = typename detail::VisitorBase<Types...>::type;
+
+class StatementVisitor : public VisitorBase<
+ IRendererBase,
+ Statement,
+ ForStatement,
+ IfStatement,
+ ElseBranchStatement,
+ SetStatement,
+ ParentBlockStatement,
+ BlockStatement,
+ ExtendsStatement,
+ IncludeStatement,
+ ImportStatement,
+ MacroStatement,
+ MacroCallStatement,
+ ComposedRenderer,
+ RawTextRenderer,
+ ExpressionRenderer>
+{
+public:
+ void Visit(VisitableStatement* stmt)
+ {
+ stmt->ApplyVisitor(this);
+ }
+ void Visit(const VisitableStatement* stmt)
+ {
+ stmt->ApplyVisitor(this);
+ }
+};
+} // namespace jinja2
+
+
+#endif
diff --git a/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.cpp b/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.cpp
new file mode 100644
index 0000000000..1234759d27
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.cpp
@@ -0,0 +1,127 @@
+#include "rapid_json_serializer.h"
+
+#include "../value_visitors.h"
+
+#include <rapidjson/prettywriter.h>
+
+namespace jinja2
+{
+namespace rapidjson_serializer
+{
+namespace
+{
+struct JsonInserter : visitors::BaseVisitor<rapidjson::Value>
+{
+ using BaseVisitor::operator();
+
+ explicit JsonInserter(rapidjson::Document::AllocatorType& allocator)
+ : m_allocator(allocator)
+ {
+ }
+
+ rapidjson::Value operator()(const ListAdapter& list) const
+ {
+ rapidjson::Value listValue(rapidjson::kArrayType);
+
+ for (auto& v : list)
+ {
+ listValue.PushBack(Apply<JsonInserter>(v, m_allocator), m_allocator);
+ }
+ return listValue;
+ }
+
+ rapidjson::Value operator()(const MapAdapter& map) const
+ {
+ rapidjson::Value mapNode(rapidjson::kObjectType);
+
+ const auto& keys = map.GetKeys();
+ for (auto& k : keys)
+ {
+ mapNode.AddMember(rapidjson::Value(k.c_str(), m_allocator), Apply<JsonInserter>(map.GetValueByName(k), m_allocator), m_allocator);
+ }
+
+ return mapNode;
+ }
+
+ rapidjson::Value operator()(const KeyValuePair& kwPair) const
+ {
+ rapidjson::Value pairNode(rapidjson::kObjectType);
+ pairNode.AddMember(rapidjson::Value(kwPair.key.c_str(), m_allocator), Apply<JsonInserter>(kwPair.value, m_allocator), m_allocator);
+
+ return pairNode;
+ }
+
+ rapidjson::Value operator()(const std::string& str) const { return rapidjson::Value(str.c_str(), m_allocator); }
+
+ rapidjson::Value operator()(const std::string_view& str) const
+ {
+ return rapidjson::Value(str.data(), static_cast<rapidjson::SizeType>(str.size()), m_allocator);
+ }
+
+ rapidjson::Value operator()(const std::wstring& str) const
+ {
+ auto s = ConvertString<std::string>(str);
+ return rapidjson::Value(s.c_str(), m_allocator);
+ }
+
+ rapidjson::Value operator()(const std::wstring_view& str) const
+ {
+ auto s = ConvertString<std::string>(str);
+ return rapidjson::Value(s.c_str(), m_allocator);
+ }
+
+ rapidjson::Value operator()(bool val) const { return rapidjson::Value(val); }
+
+ rapidjson::Value operator()(EmptyValue) const { return rapidjson::Value(); }
+
+ rapidjson::Value operator()(const Callable&) const { return rapidjson::Value("<callable>"); }
+
+ rapidjson::Value operator()(double val) const { return rapidjson::Value(val); }
+
+ rapidjson::Value operator()(int64_t val) const { return rapidjson::Value(val); }
+
+ rapidjson::Document::AllocatorType& m_allocator;
+};
+} // namespace
+
+DocumentWrapper::DocumentWrapper()
+ : m_document(std::make_shared<rapidjson::Document>())
+{
+}
+
+ValueWrapper DocumentWrapper::CreateValue(const InternalValue& value) const
+{
+ auto v = Apply<JsonInserter>(value, m_document->GetAllocator());
+ return ValueWrapper(std::move(v), m_document);
+}
+
+ValueWrapper::ValueWrapper(rapidjson::Value&& value, std::shared_ptr<rapidjson::Document> document)
+ : m_value(std::move(value))
+ , m_document(document)
+{
+}
+
+std::string ValueWrapper::AsString(const uint8_t indent) const
+{
+ using Writer = rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::UTF8<>>;
+ using PrettyWriter = rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::UTF8<>>;
+
+ rapidjson::StringBuffer buffer;
+ if (indent == 0)
+ {
+ Writer writer(buffer);
+ m_value.Accept(writer);
+ }
+ else
+ {
+ PrettyWriter writer(buffer);
+ writer.SetIndent(' ', indent);
+ writer.SetFormatOptions(rapidjson::kFormatSingleLineArray);
+ m_value.Accept(writer);
+ }
+
+ return buffer.GetString();
+}
+
+} // namespace rapidjson_serializer
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.h b/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.h
new file mode 100644
index 0000000000..66e26c14da
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/binding/rapid_json_serializer.h
@@ -0,0 +1,50 @@
+#ifndef JINJA2CPP_SRC_RAPID_JSON_SERIALIZER_H
+#define JINJA2CPP_SRC_RAPID_JSON_SERIALIZER_H
+
+#include "../internal_value.h"
+
+#include <rapidjson/document.h>
+#include <rapidjson/rapidjson.h>
+
+#include <memory>
+
+namespace jinja2
+{
+namespace rapidjson_serializer
+{
+
+class ValueWrapper
+{
+ friend class DocumentWrapper;
+
+public:
+ ValueWrapper(ValueWrapper&&) = default;
+ ValueWrapper& operator=(ValueWrapper&&) = default;
+
+ std::string AsString(uint8_t indent = 0) const;
+
+private:
+ ValueWrapper(rapidjson::Value&& value, std::shared_ptr<rapidjson::Document> document);
+
+ rapidjson::Value m_value;
+ std::shared_ptr<rapidjson::Document> m_document;
+};
+
+class DocumentWrapper
+{
+public:
+ DocumentWrapper();
+
+ DocumentWrapper(DocumentWrapper&&) = default;
+ DocumentWrapper& operator=(DocumentWrapper&&) = default;
+
+ ValueWrapper CreateValue(const InternalValue& value) const;
+
+private:
+ std::shared_ptr<rapidjson::Document> m_document;
+};
+
+} // namespace rapidjson_serializer
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_RAPID_JSON_SERIALIZER_H
diff --git a/contrib/libs/jinja2cpp/src/error_handling.h b/contrib/libs/jinja2cpp/src/error_handling.h
new file mode 100644
index 0000000000..db0f02ce52
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/error_handling.h
@@ -0,0 +1,63 @@
+#ifndef JINJA2CPP_SRC_ERROR_HANDLING_H
+#define JINJA2CPP_SRC_ERROR_HANDLING_H
+
+#include "lexer.h"
+#include <jinja2cpp/error_info.h>
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+
+#include <initializer_list>
+#include <vector>
+
+namespace jinja2
+{
+
+struct ParseError
+{
+ ParseError() = default;
+ ParseError(ErrorCode code, Token tok)
+ : errorCode(code)
+ , errorToken(tok)
+ {}
+
+ ParseError(ErrorCode code, Token tok, std::initializer_list<Token> toks)
+ : errorCode(code)
+ , errorToken(tok)
+ , relatedTokens(toks)
+ {}
+ ParseError(const ParseError&) = default;
+ ParseError(ParseError&& other) noexcept(true)
+ : errorCode(std::move(other.errorCode))
+ , errorToken(std::move(other.errorToken))
+ , relatedTokens(std::move(other.relatedTokens))
+ {}
+
+ ParseError& operator =(const ParseError&) = default;
+ ParseError& operator =(ParseError&& error) noexcept
+ {
+ if (this == &error)
+ return *this;
+
+ std::swap(errorCode, error.errorCode);
+ std::swap(errorToken, error.errorToken);
+ std::swap(relatedTokens, error.relatedTokens);
+
+ return *this;
+ }
+
+ ErrorCode errorCode;
+ Token errorToken;
+ std::vector<Token> relatedTokens;
+};
+
+inline auto MakeParseError(ErrorCode code, Token tok)
+{
+ return nonstd::make_unexpected(ParseError{code, tok});
+}
+
+inline auto MakeParseError(ErrorCode code, Token tok, std::initializer_list<Token> toks)
+{
+ return nonstd::make_unexpected(ParseError{code, tok, toks});
+}
+
+} // namespace jinja2
+#endif // JINJA2CPP_SRC_ERROR_HANDLING_H
diff --git a/contrib/libs/jinja2cpp/src/error_info.cpp b/contrib/libs/jinja2cpp/src/error_info.cpp
new file mode 100644
index 0000000000..25ff5dfee7
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/error_info.cpp
@@ -0,0 +1,289 @@
+#include "helpers.h"
+
+#include <fmt/format.h>
+#include <fmt/xchar.h>
+#include <jinja2cpp/error_info.h>
+#include <iterator>
+
+namespace
+{
+template<typename FmtCtx>
+struct ValueRenderer
+{
+ using CharT = typename FmtCtx::char_type;
+ FmtCtx* ctx;
+
+ explicit ValueRenderer(FmtCtx* c)
+ : ctx(c)
+ {
+ }
+
+ constexpr void operator()(bool val) const {
+ fmt::format_to(
+ ctx->out(),
+ UNIVERSAL_STR("{}").GetValue<CharT>(),
+ (val ? UNIVERSAL_STR("True").GetValue<CharT>(): UNIVERSAL_STR("False").GetValue<CharT>()));
+ }
+ void operator()(const jinja2::EmptyValue&) const { fmt::format_to(ctx->out(), UNIVERSAL_STR("").GetValue<CharT>()); }
+ template<typename CharU>
+ void operator()(const std::basic_string<CharU>& val) const
+ {
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{}").GetValue<CharT>(), jinja2::ConvertString<std::basic_string<CharT>>(val));
+ }
+
+ template<typename CharU>
+ void operator()(const std::basic_string_view<CharU>& val) const
+ {
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{}").GetValue<CharT>(), jinja2::ConvertString<std::basic_string<CharT>>(val));
+ }
+
+ void operator()(const jinja2::ValuesList& vals) const
+ {
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{{").GetValue<CharT>());
+ bool isFirst = true;
+ for (auto& val : vals)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ fmt::format_to(ctx->out(), UNIVERSAL_STR(", ").GetValue<CharT>());
+ std::visit(ValueRenderer<FmtCtx>(ctx), val.data());
+ }
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("}}").GetValue<CharT>());
+ }
+
+ void operator()(const jinja2::ValuesMap& vals) const
+ {
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{{").GetValue<CharT>());
+ bool isFirst = true;
+ for (auto& val : vals)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ fmt::format_to(ctx->out(), UNIVERSAL_STR(", ").GetValue<CharT>());
+
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{{\"{}\",").GetValue<CharT>(), jinja2::ConvertString<std::basic_string<CharT>>(val.first));
+ std::visit(ValueRenderer<FmtCtx>(ctx), val.second.data());
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("}}").GetValue<CharT>());
+ }
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("}}").GetValue<CharT>());
+ }
+
+ template<typename T>
+ void operator()(const jinja2::RecWrapper<T>& val) const
+ {
+ this->operator()(const_cast<const T&>(*val));
+ }
+
+ void operator()(const jinja2::GenericMap& /*val*/) const {}
+
+ void operator()(const jinja2::GenericList& /*val*/) const {}
+
+ void operator()(const jinja2::UserCallable& /*val*/) const {}
+
+ template<typename T>
+ void operator()(const T& val) const
+ {
+ fmt::format_to(ctx->out(), UNIVERSAL_STR("{}").GetValue<CharT>(), val);
+ }
+};
+} // namespace
+
+namespace fmt
+{
+template<typename CharT>
+struct formatter<jinja2::Value, CharT>
+{
+ template<typename ParseContext>
+ constexpr auto parse(ParseContext& ctx)
+ {
+ return ctx.begin();
+ }
+
+ template<typename FormatContext>
+ auto format(const jinja2::Value& val, FormatContext& ctx)
+ {
+ std::visit(ValueRenderer<FormatContext>(&ctx), val.data());
+ return fmt::format_to(ctx.out(), UNIVERSAL_STR("").GetValue<CharT>());
+ }
+};
+} // namespace fmt
+
+namespace jinja2
+{
+
+template<typename CharT>
+void RenderErrorInfo(std::basic_string<CharT>& result, const ErrorInfoTpl<CharT>& errInfo)
+{
+ using string_t = std::basic_string<CharT>;
+ auto out = fmt::basic_memory_buffer<CharT>();
+
+ auto& loc = errInfo.GetErrorLocation();
+
+ fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("{}:{}:{}: error: ").GetValue<CharT>(), ConvertString<string_t>(loc.fileName), loc.line, loc.col);
+ ErrorCode errCode = errInfo.GetCode();
+ switch (errCode)
+ {
+ case ErrorCode::Unspecified:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Parse error").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedException:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: {}").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::MetadataParseError:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Error occurred during template metadata parsing. Error: {}").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::YetUnsupported:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("This feature has not been supported yet").GetValue<CharT>());
+ break;
+ case ErrorCode::FileNotFound:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("File not found").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedStringLiteral:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("String expected").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedIdentifier:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Identifier expected").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedSquareBracket:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("']' expected").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedRoundBracket:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("')' expected").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedCurlyBracket:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("'}}' expected").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedToken:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token '{}'").GetValue<CharT>(), extraParams[0]);
+ if (extraParams.size() > 1)
+ {
+ format_to(std::back_inserter(out), UNIVERSAL_STR(". Expected: ").GetValue<CharT>());
+ for (std::size_t i = 1; i < extraParams.size(); ++ i)
+ {
+ if (i != 1)
+ format_to(std::back_inserter(out), UNIVERSAL_STR(", ").GetValue<CharT>());
+ format_to(std::back_inserter(out), UNIVERSAL_STR("\'{}\'").GetValue<CharT>(), extraParams[i]);
+ }
+ }
+ break;
+ }
+ case ErrorCode::ExpectedExpression:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Expected expression, got: '{}'").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::ExpectedEndOfStatement:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of statement, got: '{}'").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::ExpectedRawEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of raw block").GetValue<CharT>());
+ break;
+ case ErrorCode::ExpectedMetaEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of meta block").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedToken:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token: '{}'").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::UnexpectedStatement:
+ {
+ auto& extraParams = errInfo.GetExtraParams();
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement: '{}'").GetValue<CharT>(), extraParams[0]);
+ break;
+ }
+ case ErrorCode::UnexpectedCommentBegin:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment begin").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedCommentEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment end").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedRawBegin:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block begin").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedRawEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block end").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedMetaBegin:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block begin").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedMetaEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block end").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedExprBegin:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block begin").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedExprEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block end").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedStmtBegin:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block begin").GetValue<CharT>());
+ break;
+ case ErrorCode::UnexpectedStmtEnd:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block end").GetValue<CharT>());
+ break;
+ case ErrorCode::TemplateNotParsed:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Template not parsed").GetValue<CharT>());
+ break;
+ case ErrorCode::TemplateNotFound:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Template(s) not found: {}").GetValue<CharT>(), errInfo.GetExtraParams()[0]);
+ break;
+ case ErrorCode::InvalidTemplateName:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid template name: {}").GetValue<CharT>(), errInfo.GetExtraParams()[0]);
+ break;
+ case ErrorCode::InvalidValueType:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid value type").GetValue<CharT>());
+ break;
+ case ErrorCode::ExtensionDisabled:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Extension disabled").GetValue<CharT>());
+ break;
+ case ErrorCode::TemplateEnvAbsent:
+ format_to(std::back_inserter(out), UNIVERSAL_STR("Template environment doesn't set").GetValue<CharT>());
+ break;
+ }
+ format_to(std::back_inserter(out), UNIVERSAL_STR("\n{}").GetValue<CharT>(), errInfo.GetLocationDescr());
+ result = to_string(out);
+}
+
+template<>
+std::string ErrorInfoTpl<char>::ToString() const
+{
+ std::string result;
+ RenderErrorInfo(result, *this);
+ return result;
+}
+
+template<>
+std::wstring ErrorInfoTpl<wchar_t>::ToString() const
+{
+ std::wstring result;
+ RenderErrorInfo(result, *this);
+ return result;
+}
+
+std::ostream& operator << (std::ostream& os, const ErrorInfo& res)
+{
+ os << res.ToString();
+ return os;
+}
+std::wostream& operator << (std::wostream& os, const ErrorInfoW& res)
+{
+ os << res.ToString();
+ return os;
+}
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/expression_evaluator.cpp b/contrib/libs/jinja2cpp/src/expression_evaluator.cpp
new file mode 100644
index 0000000000..136e33d8fc
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/expression_evaluator.cpp
@@ -0,0 +1,602 @@
+#include "expression_evaluator.h"
+#include "filters.h"
+#include "generic_adapters.h"
+#include "internal_value.h"
+#include "out_stream.h"
+#include "testers.h"
+#include "value_visitors.h"
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/container/small_vector.hpp>
+
+#include <cmath>
+#include <stack>
+
+namespace jinja2
+{
+
+void ExpressionEvaluatorBase::Render(OutStream& stream, RenderContext& values)
+{
+ auto val = Evaluate(values);
+ stream.WriteValue(val);
+}
+
+
+InternalValue FullExpressionEvaluator::Evaluate(RenderContext& values)
+{
+ if (!m_expression)
+ return InternalValue();
+
+ auto result = m_expression->Evaluate(values);
+
+ if (m_tester && !m_tester->Evaluate(values))
+ return m_tester->EvaluateAltValue(values);
+
+ return result;
+}
+
+void FullExpressionEvaluator::Render(OutStream& stream, RenderContext& values)
+{
+ if (!m_tester)
+ m_expression->Render(stream, values);
+ else
+ Expression::Render(stream, values);
+}
+
+InternalValue ValueRefExpression::Evaluate(RenderContext& values)
+{
+ bool found = false;
+ auto p = values.FindValue(m_valueName, found);
+ if (found)
+ return p->second;
+
+ return InternalValue();
+}
+
+InternalValue SubscriptExpression::Evaluate(RenderContext& values)
+{
+ InternalValue cur = m_value->Evaluate(values);
+
+ for (auto idx : m_subscriptExprs)
+ {
+ auto subscript = idx->Evaluate(values);
+ auto newVal = Subscript(cur, subscript, &values);
+ if (cur.ShouldExtendLifetime())
+ newVal.SetParentData(cur);
+ std::swap(newVal, cur);
+ }
+
+ return cur;
+}
+
+InternalValue FilteredExpression::Evaluate(RenderContext& values)
+{
+ auto origResult = m_expression->Evaluate(values);
+ return m_filter->Evaluate(origResult, values);
+}
+
+InternalValue UnaryExpression::Evaluate(RenderContext& values)
+{
+ return Apply<visitors::UnaryOperation>(m_expr->Evaluate(values), m_oper);
+}
+
+BinaryExpression::BinaryExpression(BinaryExpression::Operation oper, ExpressionEvaluatorPtr<> leftExpr, ExpressionEvaluatorPtr<> rightExpr)
+ : m_oper(oper)
+ , m_leftExpr(leftExpr)
+ , m_rightExpr(rightExpr)
+{
+ if (m_oper == In)
+ {
+ CallParamsInfo params;
+ params.kwParams["seq"] = rightExpr;
+ m_inTester = CreateTester("in", params);
+ }
+}
+
+InternalValue BinaryExpression::Evaluate(RenderContext& context)
+{
+ InternalValue leftVal = m_leftExpr->Evaluate(context);
+ InternalValue rightVal = m_oper == In ? InternalValue() : m_rightExpr->Evaluate(context);
+ InternalValue result;
+
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalAnd:
+ {
+ bool left = ConvertToBool(leftVal);
+ if (left)
+ left = ConvertToBool(rightVal);
+ result = static_cast<bool>(left);
+ break;
+ }
+ case jinja2::BinaryExpression::LogicalOr:
+ {
+ bool left = ConvertToBool(leftVal);
+ if (!left)
+ left = ConvertToBool(rightVal);
+ result = static_cast<bool>(left);
+ break;
+ }
+ case jinja2::BinaryExpression::LogicalEq:
+ case jinja2::BinaryExpression::LogicalNe:
+ case jinja2::BinaryExpression::LogicalGt:
+ case jinja2::BinaryExpression::LogicalLt:
+ case jinja2::BinaryExpression::LogicalGe:
+ case jinja2::BinaryExpression::LogicalLe:
+ case jinja2::BinaryExpression::Plus:
+ case jinja2::BinaryExpression::Minus:
+ case jinja2::BinaryExpression::Mul:
+ case jinja2::BinaryExpression::Div:
+ case jinja2::BinaryExpression::DivReminder:
+ case jinja2::BinaryExpression::DivInteger:
+ case jinja2::BinaryExpression::Pow:
+ result = Apply2<visitors::BinaryMathOperation>(leftVal, rightVal, m_oper);
+ break;
+ case jinja2::BinaryExpression::In:
+ {
+ result = m_inTester->Test(leftVal, context);
+ break;
+ }
+ case jinja2::BinaryExpression::StringConcat:
+ {
+ auto leftStr = context.GetRendererCallback()->GetAsTargetString(leftVal);
+ auto rightStr = context.GetRendererCallback()->GetAsTargetString(rightVal);
+ TargetString resultStr;
+ std::string* nleftStr = GetIf<std::string>(&leftStr);
+ if (nleftStr != nullptr)
+ {
+ auto* nrightStr = GetIf<std::string>(&rightStr);
+ resultStr = *nleftStr + *nrightStr;
+ }
+ else
+ {
+ auto* wleftStr = GetIf<std::wstring>(&leftStr);
+ auto* wrightStr = GetIf<std::wstring>(&rightStr);
+ resultStr = *wleftStr + *wrightStr;
+ }
+ result = InternalValue(std::move(resultStr));
+ break;
+ }
+ default:
+ break;
+ }
+ return result;
+}
+
+InternalValue TupleCreator::Evaluate(RenderContext& context)
+{
+ InternalValueList result;
+ for (auto& e : m_exprs)
+ {
+ result.push_back(e->Evaluate(context));
+ }
+
+ return ListAdapter::CreateAdapter(std::move(result));
+}
+
+InternalValue DictCreator::Evaluate(RenderContext& context)
+{
+ InternalValueMap result;
+ for (auto& e : m_exprs)
+ {
+ result[e.first] = e.second->Evaluate(context);
+ }
+
+ return CreateMapAdapter(std::move(result));;
+}
+
+ExpressionFilter::ExpressionFilter(const std::string& filterName, CallParamsInfo params)
+{
+ m_filter = CreateFilter(filterName, std::move(params));
+ if (!m_filter)
+ throw std::runtime_error("Can't find filter '" + filterName + "'");
+}
+
+InternalValue ExpressionFilter::Evaluate(const InternalValue& baseVal, RenderContext& context)
+{
+ if (m_parentFilter)
+ return m_filter->Filter(m_parentFilter->Evaluate(baseVal, context), context);
+
+ return m_filter->Filter(baseVal, context);
+}
+
+IsExpression::IsExpression(ExpressionEvaluatorPtr<> value, const std::string& tester, CallParamsInfo params)
+ : m_value(value)
+{
+ m_tester = CreateTester(tester, std::move(params));
+ if (!m_tester)
+ throw std::runtime_error("Can't find tester '" + tester + "'");
+}
+
+InternalValue IsExpression::Evaluate(RenderContext& context)
+{
+ return m_tester->Test(m_value->Evaluate(context), context);
+}
+
+bool IfExpression::Evaluate(RenderContext& context)
+{
+ return ConvertToBool(m_testExpr->Evaluate(context));
+}
+
+InternalValue IfExpression::EvaluateAltValue(RenderContext& context)
+{
+ return m_altValue ? m_altValue->Evaluate(context) : InternalValue();
+}
+
+/*
+InternalValue DictionaryCreator::Evaluate(RenderContext& context)
+{
+ ValuesMap result;
+ for (auto& i : m_items)
+ {
+ result[i.first] = i.second->Evaluate(context);
+ }
+
+ return result;
+}*/
+
+InternalValue CallExpression::Evaluate(RenderContext& values)
+{
+ auto fn = m_valueRef->Evaluate(values);
+
+ auto fnId = ConvertToInt(fn, InvalidFn);
+
+
+ switch (fnId)
+ {
+ case RangeFn:
+ return CallGlobalRange(values);
+ case LoopCycleFn:
+ return CallLoopCycle(values);
+ default:
+ return CallArbitraryFn(values);
+ }
+}
+
+void CallExpression::Render(OutStream& stream, RenderContext& values)
+{
+ auto fnVal = m_valueRef->Evaluate(values);
+ const Callable* callable = GetIf<Callable>(&fnVal);
+ if (callable == nullptr)
+ {
+ fnVal = Subscript(fnVal, std::string("operator()"), &values);
+ callable = GetIf<Callable>(&fnVal);
+ if (callable == nullptr)
+ {
+ Expression::Render(stream, values);
+ return;
+ }
+ }
+
+ auto callParams = helpers::EvaluateCallParams(m_params, values);
+
+ if (callable->GetType() == Callable::Type::Expression)
+ {
+ stream.WriteValue(callable->GetExpressionCallable()(callParams, values));
+ }
+ else
+ {
+ callable->GetStatementCallable()(callParams, stream, values);
+ }
+}
+
+InternalValue CallExpression::CallArbitraryFn(RenderContext& values)
+{
+ auto fnVal = m_valueRef->Evaluate(values);
+ Callable* callable = GetIf<Callable>(&fnVal);
+ if (callable == nullptr)
+ {
+ fnVal = Subscript(fnVal, std::string("operator()"), nullptr);
+ callable = GetIf<Callable>(&fnVal);
+ if (callable == nullptr)
+ return InternalValue();
+ }
+
+ auto kind = callable->GetKind();
+ if (kind != Callable::GlobalFunc && kind != Callable::UserCallable && kind != Callable::Macro)
+ return InternalValue();
+
+ auto callParams = helpers::EvaluateCallParams(m_params, values);
+
+ if (callable->GetType() == Callable::Type::Expression)
+ {
+ return callable->GetExpressionCallable()(callParams, values);
+ }
+
+ TargetString resultStr;
+ auto stream = values.GetRendererCallback()->GetStreamOnString(resultStr);
+ callable->GetStatementCallable()(callParams, stream, values);
+ return resultStr;
+}
+
+InternalValue CallExpression::CallGlobalRange(RenderContext& values)
+{
+ bool isArgsParsed = true;
+
+ auto args = helpers::ParseCallParamsInfo({ { "start" }, { "stop", true }, { "step" } }, m_params, isArgsParsed);
+ if (!isArgsParsed)
+ return InternalValue();
+
+
+ auto startExpr = args["start"];
+ auto stopExpr = args["stop"];
+ auto stepExpr = args["step"];
+
+ InternalValue startVal = startExpr ? startExpr->Evaluate(values) : InternalValue();
+ InternalValue stopVal = stopExpr ? stopExpr->Evaluate(values) : InternalValue();
+ InternalValue stepVal = stepExpr ? stepExpr->Evaluate(values) : InternalValue();
+
+ int64_t start = Apply<visitors::IntegerEvaluator>(startVal);
+ int64_t stop = Apply<visitors::IntegerEvaluator>(stopVal);
+ int64_t step = Apply<visitors::IntegerEvaluator>(stepVal);
+
+ if (!stepExpr)
+ {
+ step = 1;
+ }
+ else
+ {
+ if (step == 0)
+ return InternalValue();
+ }
+
+ auto distance = stop - start;
+ auto items_count = distance / step;
+ items_count = items_count < 0 ? 0 : static_cast<size_t>(items_count);
+
+ return ListAdapter::CreateAdapter(static_cast<size_t>(items_count),
+ [start, step](size_t idx) { return InternalValue(static_cast<int64_t>(start + step * idx)); });
+}
+
+InternalValue CallExpression::CallLoopCycle(RenderContext& values)
+{
+ bool loopFound = false;
+ auto loopValP = values.FindValue("loop", loopFound);
+ if (!loopFound)
+ return InternalValue();
+
+ auto loop = GetIf<MapAdapter>(&loopValP->second);
+ int64_t baseIdx = Apply<visitors::IntegerEvaluator>(loop->GetValueByName("index0"));
+ auto idx = static_cast<size_t>(baseIdx % m_params.posParams.size());
+ return m_params.posParams[idx]->Evaluate(values);
+}
+
+
+void SetupGlobals(InternalValueMap& globalParams)
+{
+ globalParams["range"] = InternalValue(static_cast<int64_t>(RangeFn));
+ // globalParams["loop"] = MapAdapter::CreateAdapter(InternalValueMap{{"cycle", InternalValue(static_cast<int64_t>(LoopCycleFn))}});
+}
+
+namespace helpers
+{
+enum ArgState
+{
+ NotFound,
+ NotFoundMandatory,
+ Keyword,
+ Positional,
+ Ignored
+};
+
+enum ParamState
+{
+ UnknownPos,
+ UnknownKw,
+ MappedPos,
+ MappedKw,
+};
+
+template<typename Result>
+struct ParsedArgumentDefaultValGetter;
+
+template<>
+struct ParsedArgumentDefaultValGetter<ParsedArguments>
+{
+ static auto Get(const InternalValue& val) { return val; }
+};
+
+template<>
+struct ParsedArgumentDefaultValGetter<ParsedArgumentsInfo>
+{
+ static auto Get(const InternalValue& val) { return std::make_shared<ConstantExpression>(val); }
+};
+
+template<typename Result, typename T, typename P>
+Result ParseCallParamsImpl(const T& args, const P& params, bool& isSucceeded)
+{
+ struct ArgInfo
+ {
+ ArgState state = NotFound;
+ int prevNotFound = -1;
+ int nextNotFound = -1;
+ const ArgumentInfo* info = nullptr;
+ };
+
+ boost::container::small_vector<ArgInfo, 8> argsInfo(args.size());
+ boost::container::small_vector<ParamState, 8> posParamsInfo(params.posParams.size());
+
+ isSucceeded = true;
+
+ Result result;
+
+ int argIdx = 0;
+ int firstMandatoryIdx = -1;
+ int prevNotFound = -1;
+ int foundKwArgs = 0;
+ (void)foundKwArgs; // extremely odd bug in clang warning
+ // Wunused-but-set-variable
+
+ // Find all provided keyword args
+ for (auto& argInfo : args)
+ {
+ argsInfo[argIdx].info = &argInfo;
+
+ if (argInfo.name == "*args" || argInfo.name=="**kwargs")
+ {
+ argsInfo[argIdx ++].state = Ignored;
+ continue;
+ }
+
+ auto p = params.kwParams.find(argInfo.name);
+ if (p != params.kwParams.end())
+ {
+ result.args[argInfo.name] = p->second;
+ argsInfo[argIdx].state = Keyword;
+ ++foundKwArgs;
+ }
+ else
+ {
+ if (argInfo.mandatory)
+ {
+ argsInfo[argIdx].state = NotFoundMandatory;
+ if (firstMandatoryIdx == -1)
+ firstMandatoryIdx = argIdx;
+ }
+ else
+ {
+ argsInfo[argIdx].state = NotFound;
+ }
+
+
+ if (prevNotFound != -1)
+ argsInfo[prevNotFound].nextNotFound = argIdx;
+
+ argsInfo[argIdx].prevNotFound = prevNotFound;
+ prevNotFound = argIdx;
+ }
+
+
+ ++ argIdx;
+ }
+
+ std::size_t startPosArg = firstMandatoryIdx == -1 ? 0 : firstMandatoryIdx;
+ std::size_t curPosArg = startPosArg;
+ std::size_t eatenPosArgs = 0;
+
+ // Determine the range for positional arguments scanning
+ bool isFirstTime = true;
+ for (; eatenPosArgs < posParamsInfo.size() && startPosArg < args.size(); eatenPosArgs = eatenPosArgs + (argsInfo[startPosArg].state == Ignored ? 0 : 1))
+ {
+ if (isFirstTime)
+ {
+ for (; startPosArg < args.size() && (argsInfo[startPosArg].state == Keyword || argsInfo[startPosArg].state == Positional); ++ startPosArg)
+ ;
+
+ isFirstTime = false;
+ if (startPosArg == args.size())
+ break;
+ continue;
+ }
+
+ prevNotFound = argsInfo[startPosArg].prevNotFound;
+ if (prevNotFound != -1)
+ {
+ startPosArg = static_cast<std::size_t>(prevNotFound);
+ }
+ else if (curPosArg == args.size())
+ {
+ break;
+ }
+ else
+ {
+ int nextPosArg = argsInfo[curPosArg].nextNotFound;
+ if (nextPosArg == -1)
+ break;
+ curPosArg = static_cast<std::size_t>(nextPosArg);
+ }
+ }
+
+ // Map positional params to the desired arguments
+ auto curArg = static_cast<int>(startPosArg);
+ for (std::size_t idx = 0; idx < eatenPosArgs && curArg != -1 && static_cast<size_t>(curArg) < argsInfo.size(); ++ idx, curArg = argsInfo[curArg].nextNotFound)
+ {
+ if (argsInfo[curArg].state == Ignored)
+ continue;
+
+ result.args[argsInfo[curArg].info->name] = params.posParams[idx];
+ argsInfo[curArg].state = Positional;
+ }
+
+ // Fill default arguments (if missing) and check for mandatory
+ for (std::size_t idx = 0; idx < argsInfo.size(); ++ idx)
+ {
+ auto& argInfo = argsInfo[idx];
+ switch (argInfo.state)
+ {
+ case Positional:
+ case Keyword:
+ case Ignored:
+ continue;
+ case NotFound:
+ {
+ if (!IsEmpty(argInfo.info->defaultVal))
+ {
+#if __cplusplus >= 201703L
+ if constexpr (std::is_same<Result, ParsedArgumentsInfo>::value)
+ result.args[argInfo.info->name] = std::make_shared<ConstantExpression>(argInfo.info->defaultVal);
+ else
+ result.args[argInfo.info->name] = argInfo.info->defaultVal;
+#else
+ result.args[argInfo.info->name] = ParsedArgumentDefaultValGetter<Result>::Get(argInfo.info->defaultVal);
+#endif
+ }
+ break;
+ }
+ case NotFoundMandatory:
+ isSucceeded = false;
+ break;
+ }
+ }
+
+ // Fill the extra positional and kw-args
+ for (auto& kw : params.kwParams)
+ {
+ if (result.args.find(kw.first) != result.args.end())
+ continue;
+
+ result.extraKwArgs[kw.first] = kw.second;
+ }
+
+ for (auto idx = eatenPosArgs; idx < params.posParams.size(); ++ idx)
+ result.extraPosArgs.push_back(params.posParams[idx]);
+
+
+ return result;
+}
+
+ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
+{
+ return ParseCallParamsImpl<ParsedArguments>(args, params, isSucceeded);
+}
+
+ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded)
+{
+ return ParseCallParamsImpl<ParsedArguments>(args, params, isSucceeded);
+}
+
+ParsedArgumentsInfo ParseCallParamsInfo(const std::initializer_list<ArgumentInfo>& args, const CallParamsInfo& params, bool& isSucceeded)
+{
+ return ParseCallParamsImpl<ParsedArgumentsInfo>(args, params, isSucceeded);
+}
+
+ParsedArgumentsInfo ParseCallParamsInfo(const std::vector<ArgumentInfo>& args, const CallParamsInfo& params, bool& isSucceeded)
+{
+ return ParseCallParamsImpl<ParsedArgumentsInfo>(args, params, isSucceeded);
+}
+
+CallParams EvaluateCallParams(const CallParamsInfo& info, RenderContext& context)
+{
+ CallParams result;
+
+ for (auto& p : info.posParams)
+ result.posParams.push_back(p->Evaluate(context));
+
+ for (auto& kw : info.kwParams)
+ result.kwParams[kw.first] = kw.second->Evaluate(context);
+
+ return result;
+}
+
+} // namespace helpers
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/expression_evaluator.h b/contrib/libs/jinja2cpp/src/expression_evaluator.h
new file mode 100644
index 0000000000..06b1c40f60
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/expression_evaluator.h
@@ -0,0 +1,619 @@
+#ifndef JINJA2CPP_SRC_EXPRESSION_EVALUATOR_H
+#define JINJA2CPP_SRC_EXPRESSION_EVALUATOR_H
+
+#include "internal_value.h"
+#include "render_context.h"
+
+#include <jinja2cpp/utils/i_comparable.h>
+
+#include <memory>
+#include <limits>
+
+namespace jinja2
+{
+
+enum
+{
+ InvalidFn = -1,
+ RangeFn = 1,
+ LoopCycleFn = 2
+};
+
+class ExpressionEvaluatorBase : public IComparable
+{
+public:
+ virtual ~ExpressionEvaluatorBase() {}
+
+ virtual InternalValue Evaluate(RenderContext& values) = 0;
+ virtual void Render(OutStream& stream, RenderContext& values);
+};
+
+template<typename T = ExpressionEvaluatorBase>
+using ExpressionEvaluatorPtr = std::shared_ptr<T>;
+using Expression = ExpressionEvaluatorBase;
+
+inline bool operator==(const ExpressionEvaluatorPtr<>& lhs, const ExpressionEvaluatorPtr<>& rhs)
+{
+ if (lhs && rhs && !lhs->IsEqual(*rhs))
+ return false;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+inline bool operator!=(const ExpressionEvaluatorPtr<>& lhs, const ExpressionEvaluatorPtr<>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+struct CallParams
+{
+ std::unordered_map<std::string, InternalValue> kwParams;
+ std::vector<InternalValue> posParams;
+};
+
+inline bool operator==(const CallParams& lhs, const CallParams& rhs)
+{
+ if (lhs.kwParams != rhs.kwParams)
+ return false;
+ if (lhs.posParams != rhs.posParams)
+ return false;
+ return true;
+}
+
+inline bool operator!=(const CallParams& lhs, const CallParams& rhs)
+{
+ return !(lhs == rhs);
+}
+
+struct CallParamsInfo
+{
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<>> kwParams;
+ std::vector<ExpressionEvaluatorPtr<>> posParams;
+};
+
+inline bool operator==(const CallParamsInfo& lhs, const CallParamsInfo& rhs)
+{
+ if (lhs.kwParams != rhs.kwParams)
+ return false;
+ if (lhs.posParams != rhs.posParams)
+ return false;
+ return true;
+}
+
+inline bool operator!=(const CallParamsInfo& lhs, const CallParamsInfo& rhs)
+{
+ return !(lhs == rhs);
+}
+
+struct ArgumentInfo
+{
+ std::string name;
+ bool mandatory = false;
+ InternalValue defaultVal;
+
+ ArgumentInfo(std::string argName, bool isMandatory = false, InternalValue def = InternalValue())
+ : name(std::move(argName))
+ , mandatory(isMandatory)
+ , defaultVal(std::move(def))
+ {
+ }
+};
+
+inline bool operator==(const ArgumentInfo& lhs, const ArgumentInfo& rhs)
+{
+ if (lhs.name != rhs.name)
+ return false;
+ if (lhs.mandatory != rhs.mandatory)
+ return false;
+ if (!(lhs.defaultVal == rhs.defaultVal))
+ return false;
+ return true;
+}
+
+inline bool operator!=(const ArgumentInfo& lhs, const ArgumentInfo& rhs)
+{
+ return !(lhs == rhs);
+}
+
+struct ParsedArgumentsInfo
+{
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<>> args;
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<>> extraKwArgs;
+ std::vector<ExpressionEvaluatorPtr<>> extraPosArgs;
+
+ ExpressionEvaluatorPtr<> operator[](const std::string& name) const
+ {
+ auto p = args.find(name);
+ if (p == args.end())
+ return ExpressionEvaluatorPtr<>();
+
+ return p->second;
+ }
+};
+
+inline bool operator==(const ParsedArgumentsInfo& lhs, const ParsedArgumentsInfo& rhs)
+{
+ if (lhs.args != rhs.args)
+ return false;
+ if (lhs.extraKwArgs != rhs.extraKwArgs)
+ return false;
+ if (lhs.extraPosArgs != rhs.extraPosArgs)
+ return false;
+ return true;
+}
+
+inline bool operator!=(const ParsedArgumentsInfo& lhs, const ParsedArgumentsInfo& rhs)
+{
+ return !(lhs == rhs);
+}
+
+struct ParsedArguments
+{
+ std::unordered_map<std::string, InternalValue> args;
+ std::unordered_map<std::string, InternalValue> extraKwArgs;
+ std::vector<InternalValue> extraPosArgs;
+
+ InternalValue operator[](const std::string& name) const
+ {
+ auto p = args.find(name);
+ if (p == args.end())
+ return InternalValue();
+
+ return p->second;
+ }
+};
+
+inline bool operator==(const ParsedArguments& lhs, const ParsedArguments& rhs)
+{
+ if (lhs.args != rhs.args)
+ return false;
+ if (lhs.extraKwArgs != rhs.extraKwArgs)
+ return false;
+ if (lhs.extraPosArgs != rhs.extraPosArgs)
+ return false;
+ return true;
+}
+
+inline bool operator!=(const ParsedArguments& lhs, const ParsedArguments& rhs)
+{
+ return !(lhs == rhs);
+}
+
+class ExpressionFilter;
+class IfExpression;
+
+class FullExpressionEvaluator : public ExpressionEvaluatorBase
+{
+public:
+ void SetExpression(ExpressionEvaluatorPtr<Expression> expr)
+ {
+ m_expression = std::move(expr);
+ }
+ void SetTester(ExpressionEvaluatorPtr<IfExpression> expr)
+ {
+ m_tester = std::move(expr);
+ }
+ InternalValue Evaluate(RenderContext& values) override;
+ void Render(OutStream &stream, RenderContext &values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ const auto* eval = dynamic_cast<const FullExpressionEvaluator*>(&other);
+ if (!eval)
+ return false;
+ if (m_expression != eval->m_expression)
+ return false;
+ if (m_tester != eval->m_tester)
+ return false;
+ return true;
+ }
+private:
+ ExpressionEvaluatorPtr<Expression> m_expression;
+ ExpressionEvaluatorPtr<IfExpression> m_tester;
+};
+
+class ValueRefExpression : public Expression
+{
+public:
+ ValueRefExpression(std::string valueName)
+ : m_valueName(std::move(valueName))
+ {
+ }
+ InternalValue Evaluate(RenderContext& values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const ValueRefExpression*>(&other);
+ if (!value)
+ return false;
+ return m_valueName != value->m_valueName;
+ }
+private:
+ std::string m_valueName;
+};
+
+class SubscriptExpression : public Expression
+{
+public:
+ SubscriptExpression(ExpressionEvaluatorPtr<Expression> value)
+ : m_value(value)
+ {
+ }
+ InternalValue Evaluate(RenderContext& values) override;
+ void AddIndex(ExpressionEvaluatorPtr<Expression> value)
+ {
+ m_subscriptExprs.push_back(value);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* otherPtr = dynamic_cast<const SubscriptExpression*>(&other);
+ if (!otherPtr)
+ return false;
+ if (m_value != otherPtr->m_value)
+ return false;
+ if (m_subscriptExprs != otherPtr->m_subscriptExprs)
+ return false;
+ return true;
+ }
+
+private:
+ ExpressionEvaluatorPtr<Expression> m_value;
+ std::vector<ExpressionEvaluatorPtr<Expression>> m_subscriptExprs;
+};
+
+class FilteredExpression : public Expression
+{
+public:
+ explicit FilteredExpression(ExpressionEvaluatorPtr<Expression> expression, ExpressionEvaluatorPtr<ExpressionFilter> filter)
+ : m_expression(std::move(expression))
+ , m_filter(std::move(filter))
+ {
+ }
+ InternalValue Evaluate(RenderContext&) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* otherPtr = dynamic_cast<const FilteredExpression*>(&other);
+ if (!otherPtr)
+ return false;
+ if (m_expression != otherPtr->m_expression)
+ return false;
+ if (m_filter != otherPtr->m_filter)
+ return false;
+ return true;
+ }
+
+private:
+ ExpressionEvaluatorPtr<Expression> m_expression;
+ ExpressionEvaluatorPtr<ExpressionFilter> m_filter;
+};
+
+class ConstantExpression : public Expression
+{
+public:
+ ConstantExpression(InternalValue constant)
+ : m_constant(constant)
+ {}
+ InternalValue Evaluate(RenderContext&) override
+ {
+ return m_constant;
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* otherVal = dynamic_cast<const ConstantExpression*>(&other);
+ if (!otherVal)
+ return false;
+ return m_constant == otherVal->m_constant;
+ }
+private:
+ InternalValue m_constant;
+};
+
+class TupleCreator : public Expression
+{
+public:
+ TupleCreator(std::vector<ExpressionEvaluatorPtr<>> exprs)
+ : m_exprs(std::move(exprs))
+ {
+ }
+
+ InternalValue Evaluate(RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const TupleCreator*>(&other);
+ if (!val)
+ return false;
+ return m_exprs == val->m_exprs;
+ }
+private:
+ std::vector<ExpressionEvaluatorPtr<>> m_exprs;
+};
+/*
+class DictionaryCreator : public Expression
+{
+public:
+ DictionaryCreator(std::unordered_map<std::string, ExpressionEvaluatorPtr<>> items)
+ : m_items(std::move(items))
+ {
+ }
+
+ InternalValue Evaluate(RenderContext&) override;
+
+private:
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<>> m_items;
+};*/
+
+class DictCreator : public Expression
+{
+public:
+ DictCreator(std::unordered_map<std::string, ExpressionEvaluatorPtr<>> exprs)
+ : m_exprs(std::move(exprs))
+ {
+ }
+
+ InternalValue Evaluate(RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const DictCreator*>(&other);
+ if (!val)
+ return false;
+ return m_exprs == val->m_exprs;
+ }
+private:
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<>> m_exprs;
+};
+
+class UnaryExpression : public Expression
+{
+public:
+ enum Operation
+ {
+ LogicalNot,
+ UnaryPlus,
+ UnaryMinus
+ };
+
+ UnaryExpression(Operation oper, ExpressionEvaluatorPtr<> expr)
+ : m_oper(oper)
+ , m_expr(expr)
+ {}
+ InternalValue Evaluate(RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const UnaryExpression*>(&other);
+ if (!val)
+ return false;
+ if (m_oper != val->m_oper)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ return true;
+ }
+
+private:
+ Operation m_oper;
+ ExpressionEvaluatorPtr<> m_expr;
+};
+
+class IsExpression : public Expression
+{
+public:
+ virtual ~IsExpression() {}
+
+ struct ITester : IComparable
+ {
+ virtual ~ITester() {}
+ virtual bool Test(const InternalValue& baseVal, RenderContext& context) = 0;
+ };
+ using TesterPtr = std::shared_ptr<ITester>;
+ using TesterFactoryFn = std::function<TesterPtr(CallParamsInfo params)>;
+
+ IsExpression(ExpressionEvaluatorPtr<> value, const std::string& tester, CallParamsInfo params);
+ InternalValue Evaluate(RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const IsExpression*>(&other);
+ if (!val)
+ return false;
+ if (m_value != val->m_value)
+ return false;
+ if (m_tester != val->m_tester)
+ return false;
+ if (m_tester && val->m_tester && !m_tester->IsEqual(*val->m_tester))
+ return false;
+ return true;
+ }
+
+private:
+ ExpressionEvaluatorPtr<> m_value;
+ TesterPtr m_tester;
+};
+
+class BinaryExpression : public Expression
+{
+public:
+ enum Operation
+ {
+ LogicalAnd,
+ LogicalOr,
+ LogicalEq,
+ LogicalNe,
+ LogicalGt,
+ LogicalLt,
+ LogicalGe,
+ LogicalLe,
+ In,
+ Plus,
+ Minus,
+ Mul,
+ Div,
+ DivReminder,
+ DivInteger,
+ Pow,
+ StringConcat
+ };
+
+ enum CompareType
+ {
+ Undefined = 0,
+ CaseSensitive = 0,
+ CaseInsensitive = 1
+ };
+
+ BinaryExpression(Operation oper, ExpressionEvaluatorPtr<> leftExpr, ExpressionEvaluatorPtr<> rightExpr);
+ InternalValue Evaluate(RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const BinaryExpression*>(&other);
+ if (!val)
+ return false;
+ if (m_oper != val->m_oper)
+ return false;
+ if (m_leftExpr != val->m_leftExpr)
+ return false;
+ if (m_rightExpr != val->m_rightExpr)
+ return false;
+ if (m_inTester && val->m_inTester && !m_inTester->IsEqual(*val->m_inTester))
+ return false;
+ if ((!m_inTester && val->m_inTester) || (m_inTester && !val->m_inTester))
+ return false;
+ return true;
+ }
+private:
+ Operation m_oper;
+ ExpressionEvaluatorPtr<> m_leftExpr;
+ ExpressionEvaluatorPtr<> m_rightExpr;
+ IsExpression::TesterPtr m_inTester;
+};
+
+
+class CallExpression : public Expression
+{
+public:
+ virtual ~CallExpression() {}
+
+ CallExpression(ExpressionEvaluatorPtr<> valueRef, CallParamsInfo params)
+ : m_valueRef(std::move(valueRef))
+ , m_params(std::move(params))
+ {
+ }
+
+ InternalValue Evaluate(RenderContext &values) override;
+ void Render(OutStream &stream, RenderContext &values) override;
+
+ auto& GetValueRef() const {return m_valueRef;}
+ auto& GetParams() const {return m_params;}
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const CallExpression*>(&other);
+ if (!val)
+ return false;
+ if (m_valueRef != val->m_valueRef)
+ return false;
+ return m_params == val->m_params;
+ }
+private:
+ InternalValue CallArbitraryFn(RenderContext &values);
+ InternalValue CallGlobalRange(RenderContext &values);
+ InternalValue CallLoopCycle(RenderContext &values);
+
+private:
+ ExpressionEvaluatorPtr<> m_valueRef;
+ CallParamsInfo m_params;
+};
+
+class ExpressionFilter : public IComparable
+{
+public:
+ virtual ~ExpressionFilter() {}
+
+ struct IExpressionFilter : IComparable
+ {
+ virtual ~IExpressionFilter() {}
+ virtual InternalValue Filter(const InternalValue& baseVal, RenderContext& context) = 0;
+ };
+ using ExpressionFilterPtr = std::shared_ptr<IExpressionFilter>;
+ using FilterFactoryFn = std::function<ExpressionFilterPtr(CallParamsInfo params)>;
+
+ ExpressionFilter(const std::string& filterName, CallParamsInfo params);
+
+ InternalValue Evaluate(const InternalValue& baseVal, RenderContext& context);
+ void SetParentFilter(std::shared_ptr<ExpressionFilter> parentFilter)
+ {
+ m_parentFilter = std::move(parentFilter);
+ }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* valuePtr = dynamic_cast<const ExpressionFilter*>(&other);
+ if (!valuePtr)
+ return false;
+ if (m_filter && valuePtr->m_filter && !m_filter->IsEqual(*valuePtr->m_filter))
+ return false;
+ if ((m_filter && !valuePtr->m_filter) || (!m_filter && !valuePtr->m_filter))
+ return false;
+ if (m_parentFilter != valuePtr->m_parentFilter)
+ return false;
+ return true;
+ }
+
+
+private:
+ ExpressionFilterPtr m_filter;
+ std::shared_ptr<ExpressionFilter> m_parentFilter;
+};
+
+
+class IfExpression : public IComparable
+{
+public:
+ virtual ~IfExpression() {}
+
+ IfExpression(ExpressionEvaluatorPtr<> testExpr, ExpressionEvaluatorPtr<> altValue)
+ : m_testExpr(testExpr)
+ , m_altValue(altValue)
+ {
+ }
+
+ bool Evaluate(RenderContext& context);
+ InternalValue EvaluateAltValue(RenderContext& context);
+
+ void SetAltValue(ExpressionEvaluatorPtr<> altValue)
+ {
+ m_altValue = std::move(altValue);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* valPtr = dynamic_cast<const IfExpression*>(&other);
+ if (!valPtr)
+ return false;
+ if (m_testExpr != valPtr->m_testExpr)
+ return false;
+ if (m_altValue != valPtr->m_altValue)
+ return false;
+ return true;
+ }
+
+private:
+ ExpressionEvaluatorPtr<> m_testExpr;
+ ExpressionEvaluatorPtr<> m_altValue;
+};
+
+namespace helpers
+{
+ParsedArguments ParseCallParams(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParams& params, bool& isSucceeded);
+ParsedArguments ParseCallParams(const std::vector<ArgumentInfo>& args, const CallParams& params, bool& isSucceeded);
+ParsedArgumentsInfo ParseCallParamsInfo(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParamsInfo& params, bool& isSucceeded);
+ParsedArgumentsInfo ParseCallParamsInfo(const std::vector<ArgumentInfo>& args, const CallParamsInfo& params, bool& isSucceeded);
+CallParams EvaluateCallParams(const CallParamsInfo& info, RenderContext& context);
+} // namespace helpers
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_EXPRESSION_EVALUATOR_H
diff --git a/contrib/libs/jinja2cpp/src/expression_parser.cpp b/contrib/libs/jinja2cpp/src/expression_parser.cpp
new file mode 100644
index 0000000000..edc599cc19
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/expression_parser.cpp
@@ -0,0 +1,606 @@
+#include "expression_parser.h"
+
+#include <sstream>
+#include <unordered_set>
+#include <jinja2cpp/template_env.h>
+
+namespace jinja2
+{
+
+template<typename T>
+auto ReplaceErrorIfPossible(T& result, const Token& pivotTok, ErrorCode newError)
+{
+ auto& error = result.error();
+ if (error.errorToken.range.startOffset == pivotTok.range.startOffset)
+ return MakeParseError(newError, pivotTok);
+
+ return result.get_unexpected();
+}
+
+ExpressionParser::ExpressionParser(const Settings& /* settings */, TemplateEnv* /* env */)
+{
+
+}
+
+ExpressionParser::ParseResult<RendererPtr> ExpressionParser::Parse(LexScanner& lexer)
+{
+ auto evaluator = ParseFullExpression(lexer);
+ if (!evaluator)
+ return evaluator.get_unexpected();
+
+ auto tok = lexer.NextToken();
+ if (tok != Token::Eof)
+ {
+ auto tok1 = tok;
+ tok1.type = Token::Eof;
+
+ return MakeParseError(ErrorCode::ExpectedToken, tok, {tok1});
+ }
+
+ RendererPtr result = std::make_shared<ExpressionRenderer>(*evaluator);
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<FullExpressionEvaluator>> ExpressionParser::ParseFullExpression(LexScanner &lexer, bool includeIfPart)
+{
+ ExpressionEvaluatorPtr<FullExpressionEvaluator> result;
+ LexScanner::StateSaver saver(lexer);
+
+ ExpressionEvaluatorPtr<FullExpressionEvaluator> evaluator = std::make_shared<FullExpressionEvaluator>();
+ auto value = ParseLogicalOr(lexer);
+ if (!value)
+ return value.get_unexpected();
+
+ evaluator->SetExpression(*value);
+
+ if (includeIfPart && lexer.EatIfEqual(Keyword::If))
+ {
+ auto ifExpr = ParseIfExpression(lexer);
+ if (!ifExpr)
+ return ifExpr.get_unexpected();
+ evaluator->SetTester(*ifExpr);
+ }
+
+ saver.Commit();
+
+ return evaluator;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseLogicalOr(LexScanner& lexer)
+{
+ auto left = ParseLogicalAnd(lexer);
+
+ if (left && lexer.EatIfEqual(Keyword::LogicalOr))
+ {
+ auto right = ParseLogicalOr(lexer);
+ if (!right)
+ return right.get_unexpected();
+
+ return std::make_shared<BinaryExpression>(BinaryExpression::LogicalOr, *left, *right);
+ }
+
+ return left;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseLogicalAnd(LexScanner& lexer)
+{
+ auto left = ParseLogicalCompare(lexer);
+
+ if (left && lexer.EatIfEqual(Keyword::LogicalAnd))
+ {
+ auto right = ParseLogicalAnd(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(BinaryExpression::LogicalAnd, *left, *right);
+ }
+
+ return left;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseLogicalCompare(LexScanner& lexer)
+{
+ auto left = ParseStringConcat(lexer);
+ if (!left)
+ return left;
+
+ auto tok = lexer.NextToken();
+ BinaryExpression::Operation operation;
+ switch (tok.type)
+ {
+ case Token::Equal:
+ operation = BinaryExpression::LogicalEq;
+ break;
+ case Token::NotEqual:
+ operation = BinaryExpression::LogicalNe;
+ break;
+ case '<':
+ operation = BinaryExpression::LogicalLt;
+ break;
+ case '>':
+ operation = BinaryExpression::LogicalGt;
+ break;
+ case Token::GreaterEqual:
+ operation = BinaryExpression::LogicalGe;
+ break;
+ case Token::LessEqual:
+ operation = BinaryExpression::LogicalLe;
+ break;
+ default:
+ switch (lexer.GetAsKeyword(tok))
+ {
+ case Keyword::In:
+ operation = BinaryExpression::In;
+ break;
+ case Keyword::Is:
+ {
+ Token nextTok = lexer.NextToken();
+ if (nextTok != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
+
+ std::string name = AsString(nextTok.value);
+ ParseResult<CallParamsInfo> params;
+
+ if (lexer.EatIfEqual('('))
+ params = ParseCallParams(lexer);
+
+ if (!params)
+ return params.get_unexpected();
+
+ return std::make_shared<IsExpression>(*left, std::move(name), std::move(*params));
+ }
+ default:
+ lexer.ReturnToken();
+ return left;
+ }
+ }
+
+ auto right = ParseStringConcat(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(operation, *left, *right);
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseStringConcat(LexScanner& lexer)
+{
+ auto left = ParseMathPow(lexer);
+
+ if (left && lexer.EatIfEqual('~'))
+ {
+ auto right = ParseLogicalAnd(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(BinaryExpression::StringConcat, *left, *right);
+ }
+ return left;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseMathPow(LexScanner& lexer)
+{
+ auto left = ParseMathPlusMinus(lexer);
+
+ if (left && lexer.EatIfEqual(Token::MulMul))
+ {
+ auto right = ParseMathPow(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(BinaryExpression::Pow, *left, *right);
+ }
+
+ return left;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseMathPlusMinus(LexScanner& lexer)
+{
+ auto left = ParseMathMulDiv(lexer);
+ if (!left)
+ return left;
+
+ auto tok = lexer.NextToken();
+ BinaryExpression::Operation operation;
+ switch (tok.type)
+ {
+ case '+':
+ operation = BinaryExpression::Plus;
+ break;
+ case '-':
+ operation = BinaryExpression::Minus;
+ break;
+ default:
+ lexer.ReturnToken();
+ return left;
+ }
+
+ auto right = ParseMathPlusMinus(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(operation, *left, *right);
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseMathMulDiv(LexScanner& lexer)
+{
+ auto left = ParseUnaryPlusMinus(lexer);
+ if (!left)
+ return left;
+
+ auto tok = lexer.NextToken();
+ BinaryExpression::Operation operation;
+ switch (tok.type)
+ {
+ case '*':
+ operation = BinaryExpression::Mul;
+ break;
+ case '/':
+ operation = BinaryExpression::Div;
+ break;
+ case Token::DivDiv:
+ operation = BinaryExpression::DivInteger;
+ break;
+ case '%':
+ operation = BinaryExpression::DivReminder;
+ break;
+ default:
+ lexer.ReturnToken();
+ return left;
+ }
+
+ auto right = ParseMathMulDiv(lexer);
+ if (!right)
+ return right;
+
+ return std::make_shared<BinaryExpression>(operation, *left, *right);
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseUnaryPlusMinus(LexScanner& lexer)
+{
+ const auto tok = lexer.NextToken();
+ const auto isUnary = tok == '+' || tok == '-' || lexer.GetAsKeyword(tok) == Keyword::LogicalNot;
+ if (!isUnary)
+ lexer.ReturnToken();
+
+ auto subExpr = ParseValueExpression(lexer);
+ if (!subExpr)
+ return subExpr;
+
+ ExpressionEvaluatorPtr<Expression> result;
+ if (isUnary)
+ result = std::make_shared<UnaryExpression>(tok == '+' ? UnaryExpression::UnaryPlus : (tok == '-' ? UnaryExpression::UnaryMinus : UnaryExpression::LogicalNot), *subExpr);
+ else
+ result = subExpr.value();
+
+ if (lexer.EatIfEqual('|'))
+ {
+ auto filter = ParseFilterExpression(lexer);
+ if (!filter)
+ return filter.get_unexpected();
+ result = std::make_shared<FilteredExpression>(std::move(result), *filter);
+ }
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseValueExpression(LexScanner& lexer)
+{
+ Token tok = lexer.NextToken();
+ static const std::unordered_set<Keyword> forbiddenKw = {Keyword::Is, Keyword::In, Keyword::If, Keyword::Else};
+
+ ParseResult<ExpressionEvaluatorPtr<Expression>> valueRef;
+
+ switch (tok.type)
+ {
+ case Token::Identifier:
+ {
+ auto kwType = lexer.GetAsKeyword(tok);
+ if (forbiddenKw.count(kwType) != 0)
+ return MakeParseError(ErrorCode::UnexpectedToken, tok);
+
+ valueRef = std::make_shared<ValueRefExpression>(AsString(tok.value));
+ break;
+ }
+ case Token::IntegerNum:
+ case Token::FloatNum:
+ case Token::String:
+ return std::make_shared<ConstantExpression>(tok.value);
+ case Token::True:
+ return std::make_shared<ConstantExpression>(InternalValue(true));
+ case Token::False:
+ return std::make_shared<ConstantExpression>(InternalValue(false));
+ case '(':
+ valueRef = ParseBracedExpressionOrTuple(lexer);
+ break;
+ case '[':
+ valueRef = ParseTuple(lexer);
+ break;
+ case '{':
+ valueRef = ParseDictionary(lexer);
+ break;
+ default:
+ return MakeParseError(ErrorCode::UnexpectedToken, tok);
+ }
+
+ if (valueRef)
+ {
+ tok = lexer.PeekNextToken();
+ if (tok == '[' || tok == '.')
+ valueRef = ParseSubscript(lexer, *valueRef);
+
+ if (lexer.EatIfEqual('('))
+ valueRef = ParseCall(lexer, *valueRef);
+ }
+ return valueRef;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseBracedExpressionOrTuple(LexScanner& lexer)
+{
+ ExpressionEvaluatorPtr<Expression> result;
+
+ bool isTuple = false;
+ std::vector<ExpressionEvaluatorPtr<Expression>> exprs;
+ for (;;)
+ {
+ Token pivotTok = lexer.PeekNextToken();
+ auto expr = ParseFullExpression(lexer);
+
+ if (!expr)
+ return ReplaceErrorIfPossible(expr, pivotTok, ErrorCode::ExpectedRoundBracket);
+
+ exprs.push_back(*expr);
+ Token tok = lexer.NextToken();
+ if (tok == ')')
+ break;
+ else if (tok == ',')
+ isTuple = true;
+ }
+
+ if (isTuple)
+ result = std::make_shared<TupleCreator>(std::move(exprs));
+ else
+ result = exprs[0];
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseDictionary(LexScanner& lexer)
+{
+ ExpressionEvaluatorPtr<Expression> result;
+
+ std::unordered_map<std::string, ExpressionEvaluatorPtr<Expression>> items;
+ if (lexer.EatIfEqual(']'))
+ return std::make_shared<DictCreator>(std::move(items));
+
+ do
+ {
+ Token key = lexer.NextToken();
+ if (key != Token::String)
+ return MakeParseError(ErrorCode::ExpectedStringLiteral, key);
+
+ if (!lexer.EatIfEqual('='))
+ {
+ auto tok = lexer.PeekNextToken();
+ auto tok1 = tok;
+ tok1.type = Token::Assign;
+ return MakeParseError(ErrorCode::ExpectedToken, tok, {tok1});
+ }
+
+ auto pivotTok = lexer.PeekNextToken();
+ auto expr = ParseFullExpression(lexer);
+ if (!expr)
+ return ReplaceErrorIfPossible(expr, pivotTok, ErrorCode::ExpectedExpression);
+
+ items[AsString(key.value)] = *expr;
+
+ } while (lexer.EatIfEqual(','));
+
+ auto tok = lexer.NextToken();
+ if (tok != '}')
+ return MakeParseError(ErrorCode::ExpectedCurlyBracket, tok);
+
+ result = std::make_shared<DictCreator>(std::move(items));
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseTuple(LexScanner& lexer)
+{
+ ExpressionEvaluatorPtr<Expression> result;
+
+ std::vector<ExpressionEvaluatorPtr<Expression>> exprs;
+ if (lexer.EatIfEqual(']'))
+ return std::make_shared<TupleCreator>(exprs);
+
+ do
+ {
+ auto expr = ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+
+ exprs.push_back(*expr);
+ } while (lexer.EatIfEqual(','));
+
+ auto tok = lexer.NextToken();
+ if (tok != ']')
+ return MakeParseError(ErrorCode::ExpectedSquareBracket, tok);
+
+ result = std::make_shared<TupleCreator>(std::move(exprs));
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseCall(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef)
+{
+ ExpressionEvaluatorPtr<Expression> result;
+
+ ParseResult<CallParamsInfo> params = ParseCallParams(lexer);
+ if (!params)
+ return params.get_unexpected();
+
+ result = std::make_shared<CallExpression>(valueRef, std::move(*params));
+
+ return result;
+}
+
+ExpressionParser::ParseResult<CallParamsInfo> ExpressionParser::ParseCallParams(LexScanner& lexer)
+{
+ CallParamsInfo result;
+
+ if (lexer.EatIfEqual(')'))
+ return result;
+
+ do
+ {
+ Token tok = lexer.NextToken();
+ std::string paramName;
+ if (tok == Token::Identifier && lexer.PeekNextToken() == '=')
+ {
+ paramName = AsString(tok.value);
+ lexer.EatToken();
+ }
+ else
+ {
+ lexer.ReturnToken();
+ }
+
+ auto valueExpr = ParseFullExpression(lexer);
+ if (!valueExpr)
+ {
+ return valueExpr.get_unexpected();
+ }
+ if (paramName.empty())
+ result.posParams.push_back(*valueExpr);
+ else
+ result.kwParams[paramName] = *valueExpr;
+
+ } while (lexer.EatIfEqual(','));
+
+ auto tok = lexer.NextToken();
+ if (tok != ')')
+ return MakeParseError(ErrorCode::ExpectedRoundBracket, tok);
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<Expression>> ExpressionParser::ParseSubscript(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef)
+{
+ ExpressionEvaluatorPtr<SubscriptExpression> result = std::make_shared<SubscriptExpression>(valueRef);
+ for (Token tok = lexer.NextToken(); tok.type == '.' || tok.type == '['; tok = lexer.NextToken())
+ {
+ ParseResult<ExpressionEvaluatorPtr<Expression>> indexExpr;
+ if (tok == '.')
+ {
+ tok = lexer.NextToken();
+ if (tok.type != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, tok);
+
+ auto valueName = AsString(tok.value);
+ indexExpr = std::make_shared<ConstantExpression>(InternalValue(valueName));
+ }
+ else
+ {
+ auto expr = ParseFullExpression(lexer);
+
+ if (!expr)
+ return expr.get_unexpected();
+ else
+ indexExpr = *expr;
+
+ if (!lexer.EatIfEqual(']', &tok))
+ return MakeParseError(ErrorCode::ExpectedSquareBracket, lexer.PeekNextToken());
+ }
+
+ result->AddIndex(*indexExpr);
+ }
+
+ lexer.ReturnToken();
+
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<ExpressionFilter>> ExpressionParser::ParseFilterExpression(LexScanner& lexer)
+{
+ ExpressionEvaluatorPtr<ExpressionFilter> result;
+
+ auto startTok = lexer.PeekNextToken();
+ try
+ {
+ do
+ {
+ Token tok = lexer.NextToken();
+ if (tok != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, tok);
+
+ std::string name = AsString(tok.value);
+ ParseResult<CallParamsInfo> params;
+
+ if (lexer.NextToken() == '(')
+ params = ParseCallParams(lexer);
+ else
+ lexer.ReturnToken();
+
+ if (!params)
+ return params.get_unexpected();
+
+ auto filter = std::make_shared<ExpressionFilter>(name, std::move(*params));
+ if (result)
+ {
+ filter->SetParentFilter(result);
+ result = filter;
+ }
+ else
+ result = filter;
+
+ } while (lexer.NextToken() == '|');
+
+ lexer.ReturnToken();
+ }
+ catch (const ParseError& error)
+ {
+ return nonstd::make_unexpected(error);
+ }
+ catch (const std::runtime_error&)
+ {
+ return MakeParseError(ErrorCode::UnexpectedException, startTok);
+ }
+ return result;
+}
+
+ExpressionParser::ParseResult<ExpressionEvaluatorPtr<IfExpression>> ExpressionParser::ParseIfExpression(LexScanner& lexer)
+{
+ ExpressionEvaluatorPtr<IfExpression> result;
+
+ auto startTok = lexer.PeekNextToken();
+ try
+ {
+ auto testExpr = ParseLogicalOr(lexer);
+ if (!testExpr)
+ return testExpr.get_unexpected();
+
+ ParseResult<ExpressionEvaluatorPtr<>> altValue;
+ if (lexer.GetAsKeyword(lexer.PeekNextToken()) == Keyword::Else)
+ {
+ lexer.EatToken();
+ auto value = ParseFullExpression(lexer);
+ if (!value)
+ return value.get_unexpected();
+ altValue = *value;
+ }
+
+ result = std::make_shared<IfExpression>(*testExpr, *altValue);
+ }
+ catch (const ParseError& error)
+ {
+ return nonstd::make_unexpected(error);
+ }
+ catch (const std::runtime_error& ex)
+ {
+ std::cout << "Filter parsing problem: " << ex.what() << std::endl;
+ }
+
+ return result;
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/expression_parser.h b/contrib/libs/jinja2cpp/src/expression_parser.h
new file mode 100644
index 0000000000..dd0b78458f
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/expression_parser.h
@@ -0,0 +1,49 @@
+#ifndef JINJA2CPP_SRC_EXPRESSION_PARSER_H
+#define JINJA2CPP_SRC_EXPRESSION_PARSER_H
+
+#include "lexer.h"
+#include "error_handling.h"
+#include "expression_evaluator.h"
+#include "renderer.h"
+
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+#include <jinja2cpp/template_env.h>
+
+namespace jinja2
+{
+class ExpressionParser
+{
+public:
+ template<typename T>
+ using ParseResult = nonstd::expected<T, ParseError>;
+
+ explicit ExpressionParser(const Settings& settings, TemplateEnv* env = nullptr);
+ ParseResult<RendererPtr> Parse(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<FullExpressionEvaluator>> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true);
+ ParseResult<CallParamsInfo> ParseCallParams(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<ExpressionFilter>> ParseFilterExpression(LexScanner& lexer);
+private:
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalNot(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalOr(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalAnd(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseLogicalCompare(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseStringConcat(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseMathPow(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseMathMulDiv(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseMathPlusMinus(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseUnaryPlusMinus(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseValueExpression(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseBracedExpressionOrTuple(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseDictionary(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseTuple(LexScanner& lexer);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseCall(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
+ ParseResult<ExpressionEvaluatorPtr<Expression>> ParseSubscript(LexScanner& lexer, ExpressionEvaluatorPtr<Expression> valueRef);
+ ParseResult<ExpressionEvaluatorPtr<IfExpression>> ParseIfExpression(LexScanner& lexer);
+
+private:
+ ComposedRenderer* m_topLevelRenderer = nullptr;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_EXPRESSION_PARSER_H
diff --git a/contrib/libs/jinja2cpp/src/filesystem_handler.cpp b/contrib/libs/jinja2cpp/src/filesystem_handler.cpp
new file mode 100644
index 0000000000..8325487aff
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/filesystem_handler.cpp
@@ -0,0 +1,163 @@
+#include <jinja2cpp/filesystem_handler.h>
+#include <jinja2cpp/string_helpers.h>
+
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <sstream>
+#include <fstream>
+
+namespace jinja2
+{
+
+using TargetFileStream = std::variant<CharFileStreamPtr*, WCharFileStreamPtr*>;
+
+struct FileContentConverter
+{
+ void operator() (const std::string& content, CharFileStreamPtr* sPtr) const
+ {
+ sPtr->reset(new std::istringstream(content));
+ }
+
+ void operator() (const std::wstring& content, WCharFileStreamPtr* sPtr) const
+ {
+ sPtr->reset(new std::wistringstream(content));
+ }
+ void operator() (const std::wstring&, CharFileStreamPtr*) const
+ {
+// CharFileStreamPtr stream(new std::istringstream(content), [](std::istream* s) {delete static_cast<std::istringstream>(s);});
+// std::swap(*sPtr, stream);
+ }
+
+ void operator() (const std::string&, WCharFileStreamPtr*) const
+ {
+// WCharFileStreamPtr stream(new std::wistringstream(content), [](std::wistream* s) {delete static_cast<std::wistringstream>(s);});
+// std::swap(*sPtr, stream);
+ }
+};
+
+void MemoryFileSystem::AddFile(std::string fileName, std::string fileContent)
+{
+ m_filesMap[std::move(fileName)] = FileContent{std::move(fileContent), {}};
+}
+
+void MemoryFileSystem::AddFile(std::string fileName, std::wstring fileContent)
+{
+ m_filesMap[std::move(fileName)] = FileContent{ {}, std::move(fileContent) };
+}
+
+CharFileStreamPtr MemoryFileSystem::OpenStream(const std::string& name) const
+{
+ CharFileStreamPtr result(nullptr, [](std::istream* s) {delete static_cast<std::istringstream*>(s);});
+ auto p = m_filesMap.find(name);
+ if (p == m_filesMap.end())
+ return result;
+
+ auto& content = p->second;
+
+ if (!content.narrowContent && !content.wideContent)
+ return result;
+
+ if (!content.narrowContent)
+ content.narrowContent = ConvertString<std::string>(content.wideContent.value());
+
+ result.reset(new std::istringstream(content.narrowContent.value()));
+
+ return result;
+}
+
+WCharFileStreamPtr MemoryFileSystem::OpenWStream(const std::string& name) const
+{
+ WCharFileStreamPtr result(nullptr, [](std::wistream* s) {delete static_cast<std::wistringstream*>(s);});
+ auto p = m_filesMap.find(name);
+ if (p == m_filesMap.end())
+ return result;
+
+ auto& content = p->second;
+
+ if (!content.narrowContent && !content.wideContent)
+ return result;
+
+ if (!content.wideContent)
+ content.wideContent = ConvertString<std::wstring>(content.narrowContent.value());
+
+ result.reset(new std::wistringstream(content.wideContent.value()));
+
+ return result;
+}
+std::optional<std::chrono::system_clock::time_point> MemoryFileSystem::GetLastModificationDate(const std::string&) const
+{
+ return std::optional<std::chrono::system_clock::time_point>();
+}
+
+bool MemoryFileSystem::IsEqual(const IComparable& other) const
+{
+ auto* ptr = dynamic_cast<const MemoryFileSystem*>(&other);
+ if (!ptr)
+ return false;
+ return m_filesMap == ptr->m_filesMap;
+}
+
+RealFileSystem::RealFileSystem(std::string rootFolder)
+ : m_rootFolder(std::move(rootFolder))
+{
+
+}
+
+std::string RealFileSystem::GetFullFilePath(const std::string& name) const
+{
+ boost::filesystem::path root(m_rootFolder);
+ root /= name;
+ return root.string();
+}
+
+CharFileStreamPtr RealFileSystem::OpenStream(const std::string& name) const
+{
+ auto filePath = GetFullFilePath(name);
+
+ CharFileStreamPtr result(new std::ifstream(filePath), [](std::istream* s) {delete static_cast<std::ifstream*>(s);});
+ if (result->good())
+ return result;
+
+ return CharFileStreamPtr(nullptr, [](std::istream*){});
+}
+
+WCharFileStreamPtr RealFileSystem::OpenWStream(const std::string& name) const
+{
+ auto filePath = GetFullFilePath(name);
+
+ WCharFileStreamPtr result(new std::wifstream(filePath), [](std::wistream* s) {delete static_cast<std::wifstream*>(s);});
+ if (result->good())
+ return result;
+
+ return WCharFileStreamPtr(nullptr, [](std::wistream*){;});
+}
+std::optional<std::chrono::system_clock::time_point> RealFileSystem::GetLastModificationDate(const std::string& name) const
+{
+ boost::filesystem::path root(m_rootFolder);
+ root /= name;
+
+ auto modify_time = boost::filesystem::last_write_time(root);
+
+ return std::chrono::system_clock::from_time_t(modify_time);
+}
+CharFileStreamPtr RealFileSystem::OpenByteStream(const std::string& name) const
+{
+ auto filePath = GetFullFilePath(name);
+
+ CharFileStreamPtr result(new std::ifstream(filePath, std::ios_base::binary), [](std::istream* s) {delete static_cast<std::ifstream*>(s);});
+ if (result->good())
+ return result;
+
+ return CharFileStreamPtr(nullptr, [](std::istream*){});
+}
+
+bool RealFileSystem::IsEqual(const IComparable& other) const
+{
+ auto* ptr = dynamic_cast<const RealFileSystem*>(&other);
+ if (!ptr)
+ return false;
+ return m_rootFolder == ptr->m_rootFolder;
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/filters.cpp b/contrib/libs/jinja2cpp/src/filters.cpp
new file mode 100644
index 0000000000..5e0725be96
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/filters.cpp
@@ -0,0 +1,1086 @@
+#include "filters.h"
+
+#include "binding/rapid_json_serializer.h"
+#include "generic_adapters.h"
+#include "out_stream.h"
+#include "testers.h"
+#include "value_helpers.h"
+#include "value_visitors.h"
+
+#include <algorithm>
+#include <numeric>
+#include <random>
+#include <sstream>
+#include <string>
+
+using namespace std::string_literals;
+
+namespace jinja2
+{
+
+template<typename F>
+struct FilterFactory
+{
+ static FilterPtr Create(FilterParams params) { return std::make_shared<F>(std::move(params)); }
+
+ template<typename... Args>
+ static ExpressionFilter::FilterFactoryFn MakeCreator(Args&&... args)
+ {
+ return [args...](FilterParams params) { return std::make_shared<F>(std::move(params), args...); };
+ }
+};
+
+std::unordered_map<std::string, ExpressionFilter::FilterFactoryFn> s_filters = {
+ { "abs", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::AbsMode) },
+ { "applymacro", &FilterFactory<filters::ApplyMacro>::Create },
+ { "attr", &FilterFactory<filters::Attribute>::Create },
+ { "batch", FilterFactory<filters::Slice>::MakeCreator(filters::Slice::BatchMode) },
+ { "camelize", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::CamelMode) },
+ { "capitalize", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::CapitalMode) },
+ { "center", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::CenterMode) },
+ { "default", &FilterFactory<filters::Default>::Create },
+ { "d", &FilterFactory<filters::Default>::Create },
+ { "dictsort", &FilterFactory<filters::DictSort>::Create },
+ { "escape", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::EscapeHtmlMode) },
+ { "escapecpp", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::EscapeCppMode) },
+ { "first", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::FirstItemMode) },
+ { "float", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::ToFloatMode) },
+ { "format", FilterFactory<filters::StringFormat>::Create },
+ { "groupby", &FilterFactory<filters::GroupBy>::Create },
+ { "int", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::ToIntMode) },
+ { "join", &FilterFactory<filters::Join>::Create },
+ { "last", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::LastItemMode) },
+ { "length", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::LengthMode) },
+ { "list", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::ToListMode) },
+ { "lower", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::LowerMode) },
+ { "map", &FilterFactory<filters::Map>::Create },
+ { "max", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::MaxItemMode) },
+ { "min", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::MinItemMode) },
+ { "pprint", &FilterFactory<filters::PrettyPrint>::Create },
+ { "random", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::RandomMode) },
+ { "reject", FilterFactory<filters::Tester>::MakeCreator(filters::Tester::RejectMode) },
+ { "rejectattr", FilterFactory<filters::Tester>::MakeCreator(filters::Tester::RejectAttrMode) },
+ { "replace", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::ReplaceMode) },
+ { "round", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::RoundMode) },
+ { "reverse", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::ReverseMode) },
+ { "select", FilterFactory<filters::Tester>::MakeCreator(filters::Tester::SelectMode) },
+ { "selectattr", FilterFactory<filters::Tester>::MakeCreator(filters::Tester::SelectAttrMode) },
+ { "slice", FilterFactory<filters::Slice>::MakeCreator(filters::Slice::SliceMode) },
+ { "sort", &FilterFactory<filters::Sort>::Create },
+ { "striptags", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::StriptagsMode) },
+ { "sum", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::SumItemsMode) },
+ { "title", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::TitleMode) },
+ { "tojson", FilterFactory<filters::Serialize>::MakeCreator(filters::Serialize::JsonMode) },
+ { "toxml", FilterFactory<filters::Serialize>::MakeCreator(filters::Serialize::XmlMode) },
+ { "toyaml", FilterFactory<filters::Serialize>::MakeCreator(filters::Serialize::YamlMode) },
+ { "trim", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::TrimMode) },
+ { "truncate", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::TruncateMode) },
+ { "unique", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::UniqueItemsMode) },
+ { "upper", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::UpperMode) },
+ { "urlencode", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::UrlEncodeMode) },
+ { "wordcount", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::WordCountMode) },
+ { "wordwrap", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::WordWrapMode) },
+ { "underscorize", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::UnderscoreMode) },
+ { "xmlattr", &FilterFactory<filters::XmlAttrFilter>::Create }
+};
+
+extern FilterPtr CreateFilter(std::string filterName, CallParamsInfo params)
+{
+ auto p = s_filters.find(filterName);
+ if (p == s_filters.end())
+ return std::make_shared<filters::UserDefinedFilter>(std::move(filterName), std::move(params));
+
+ return p->second(std::move(params));
+}
+
+namespace filters
+{
+
+Join::Join(FilterParams params)
+{
+ ParseParams({ { "d", false, std::string() }, { "attribute" } }, params);
+}
+
+InternalValue Join::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue attrName = GetArgumentValue("attribute", context);
+
+ bool isConverted = false;
+ ListAdapter values = ConvertToList(baseVal, attrName, isConverted);
+
+ if (!isConverted)
+ return InternalValue();
+
+ bool isFirst = true;
+ InternalValue result;
+ InternalValue delimiter = m_args["d"]->Evaluate(context);
+ for (const InternalValue& val : values)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ result = Apply2<visitors::StringJoiner>(result, delimiter);
+
+ result = Apply2<visitors::StringJoiner>(result, val);
+ }
+
+ return result;
+}
+
+Sort::Sort(FilterParams params)
+{
+ ParseParams({ { "reverse", false, InternalValue(false) }, { "case_sensitive", false, InternalValue(false) }, { "attribute", false } }, params);
+}
+
+InternalValue Sort::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue attrName = GetArgumentValue("attribute", context);
+ InternalValue isReverseVal = GetArgumentValue("reverse", context, InternalValue(false));
+ InternalValue isCsVal = GetArgumentValue("case_sensitive", context, InternalValue(false));
+
+ bool isConverted = false;
+ ListAdapter origValues = ConvertToList(baseVal, isConverted);
+ if (!isConverted)
+ return InternalValue();
+ InternalValueList values = origValues.ToValueList();
+
+ BinaryExpression::Operation oper = ConvertToBool(isReverseVal) ? BinaryExpression::LogicalGt : BinaryExpression::LogicalLt;
+ BinaryExpression::CompareType compType = ConvertToBool(isCsVal) ? BinaryExpression::CaseSensitive : BinaryExpression::CaseInsensitive;
+
+ std::sort(values.begin(), values.end(), [&attrName, oper, compType, &context](auto& val1, auto& val2) {
+ InternalValue cmpRes;
+ if (IsEmpty(attrName))
+ cmpRes = Apply2<visitors::BinaryMathOperation>(val1, val2, oper, compType);
+ else
+ cmpRes = Apply2<visitors::BinaryMathOperation>(Subscript(val1, attrName, &context), Subscript(val2, attrName, &context), oper, compType);
+
+ return ConvertToBool(cmpRes);
+ });
+
+ return ListAdapter::CreateAdapter(std::move(values));
+}
+
+Attribute::Attribute(FilterParams params)
+{
+ ParseParams({ { "name", true }, { "default", false } }, params);
+}
+
+InternalValue Attribute::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ const auto attrNameVal = GetArgumentValue("name", context);
+ const auto result = Subscript(baseVal, attrNameVal, &context);
+ if (result.IsEmpty())
+ return GetArgumentValue("default", context);
+ return result;
+}
+
+Default::Default(FilterParams params)
+{
+ ParseParams({ { "default_value", false, InternalValue(""s) }, { "boolean", false, InternalValue(false) } }, params);
+}
+
+InternalValue Default::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue defaultVal = GetArgumentValue("default_value", context);
+ InternalValue conditionResult = GetArgumentValue("boolean", context);
+
+ if (IsEmpty(baseVal))
+ return defaultVal;
+
+ if (ConvertToBool(conditionResult) && !ConvertToBool(baseVal))
+ return defaultVal;
+
+ return baseVal;
+}
+
+DictSort::DictSort(FilterParams params)
+{
+ ParseParams({ { "case_sensitive", false }, { "by", false, "key"s }, { "reverse", false } }, params);
+}
+
+InternalValue DictSort::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ const MapAdapter* map = GetIf<MapAdapter>(&baseVal);
+ if (map == nullptr)
+ return InternalValue();
+
+ InternalValue isReverseVal = GetArgumentValue("reverse", context);
+ InternalValue isCsVal = GetArgumentValue("case_sensitive", context);
+ InternalValue byVal = GetArgumentValue("by", context);
+
+ bool (*comparator)(const KeyValuePair& left, const KeyValuePair& right);
+
+ if (AsString(byVal) == "key") // Sort by key
+ {
+ if (ConvertToBool(isCsVal))
+ {
+ comparator = [](const KeyValuePair& left, const KeyValuePair& right) { return left.key < right.key; };
+ }
+ else
+ {
+ comparator = [](const KeyValuePair& left, const KeyValuePair& right) {
+ return boost::lexicographical_compare(left.key, right.key, boost::algorithm::is_iless());
+ };
+ }
+ }
+ else if (AsString(byVal) == "value")
+ {
+ if (ConvertToBool(isCsVal))
+ {
+ comparator = [](const KeyValuePair& left, const KeyValuePair& right) {
+ return ConvertToBool(
+ Apply2<visitors::BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalLt, BinaryExpression::CaseSensitive));
+ };
+ }
+ else
+ {
+ comparator = [](const KeyValuePair& left, const KeyValuePair& right) {
+ return ConvertToBool(
+ Apply2<visitors::BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalLt, BinaryExpression::CaseInsensitive));
+ };
+ }
+ }
+ else
+ return InternalValue();
+
+ std::vector<KeyValuePair> tempVector;
+ tempVector.reserve(map->GetSize());
+ for (auto& key : map->GetKeys())
+ {
+ auto val = map->GetValueByName(key);
+ tempVector.push_back(KeyValuePair{ key, val });
+ }
+
+ if (ConvertToBool(isReverseVal))
+ std::sort(tempVector.begin(), tempVector.end(), [comparator](auto& l, auto& r) { return comparator(r, l); });
+ else
+ std::sort(tempVector.begin(), tempVector.end(), [comparator](auto& l, auto& r) { return comparator(l, r); });
+
+ InternalValueList resultList;
+ for (auto& tmpVal : tempVector)
+ {
+ auto resultVal = InternalValue(std::move(tmpVal));
+ if (baseVal.ShouldExtendLifetime())
+ resultVal.SetParentData(baseVal);
+ resultList.push_back(std::move(resultVal));
+ }
+
+ return InternalValue(ListAdapter::CreateAdapter(std::move(resultList)));
+}
+
+GroupBy::GroupBy(FilterParams params)
+{
+ ParseParams({ { "attribute", true } }, params);
+}
+
+InternalValue GroupBy::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ bool isConverted = false;
+ ListAdapter list = ConvertToList(baseVal, isConverted);
+
+ if (!isConverted)
+ return InternalValue();
+
+ InternalValue attrName = GetArgumentValue("attribute", context);
+
+ auto equalComparator = [](auto& val1, auto& val2) {
+ InternalValue cmpRes = Apply2<visitors::BinaryMathOperation>(val1, val2, BinaryExpression::LogicalEq, BinaryExpression::CaseSensitive);
+
+ return ConvertToBool(cmpRes);
+ };
+
+ struct GroupInfo
+ {
+ InternalValue grouper;
+ InternalValueList items;
+ };
+
+ std::vector<GroupInfo> groups;
+
+ for (auto& item : list)
+ {
+ auto attr = Subscript(item, attrName, &context);
+ auto p = std::find_if(groups.begin(), groups.end(), [&equalComparator, &attr](auto& i) { return equalComparator(i.grouper, attr); });
+ if (p == groups.end())
+ groups.push_back(GroupInfo{ attr, { item } });
+ else
+ p->items.push_back(item);
+ }
+
+ InternalValueList result;
+ for (auto& g : groups)
+ {
+ InternalValueMap groupItem{ { "grouper", std::move(g.grouper) }, { "list", ListAdapter::CreateAdapter(std::move(g.items)) } };
+ result.push_back(CreateMapAdapter(std::move(groupItem)));
+ }
+
+ return ListAdapter::CreateAdapter(std::move(result));
+}
+
+ApplyMacro::ApplyMacro(FilterParams params)
+{
+ ParseParams({ { "macro", true } }, params);
+ m_mappingParams.kwParams = m_args.extraKwArgs;
+ m_mappingParams.posParams = m_args.extraPosArgs;
+}
+
+InternalValue ApplyMacro::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue macroName = GetArgumentValue("macro", context);
+ if (IsEmpty(macroName))
+ return InternalValue();
+
+ bool macroFound = false;
+ auto macroValPtr = context.FindValue(AsString(macroName), macroFound);
+ if (!macroFound)
+ return InternalValue();
+
+ const Callable* callable = GetIf<Callable>(&macroValPtr->second);
+ if (callable == nullptr || callable->GetKind() != Callable::Macro)
+ return InternalValue();
+
+ CallParams tmpCallParams = helpers::EvaluateCallParams(m_mappingParams, context);
+ CallParams callParams;
+ callParams.kwParams = std::move(tmpCallParams.kwParams);
+ callParams.posParams.reserve(tmpCallParams.posParams.size() + 1);
+ callParams.posParams.push_back(baseVal);
+ for (auto& p : tmpCallParams.posParams)
+ callParams.posParams.push_back(std::move(p));
+
+ InternalValue result;
+ if (callable->GetType() == Callable::Type::Expression)
+ {
+ result = callable->GetExpressionCallable()(callParams, context);
+ }
+ else
+ {
+ TargetString resultStr;
+ auto stream = context.GetRendererCallback()->GetStreamOnString(resultStr);
+ callable->GetStatementCallable()(callParams, stream, context);
+ result = std::move(resultStr);
+ }
+
+ return result;
+}
+
+Map::Map(FilterParams params)
+{
+ ParseParams({ { "filter", true } }, MakeParams(std::move(params)));
+ m_mappingParams.kwParams = m_args.extraKwArgs;
+ m_mappingParams.posParams = m_args.extraPosArgs;
+}
+
+FilterParams Map::MakeParams(FilterParams params)
+{
+ if (!params.posParams.empty() || params.kwParams.empty() || params.kwParams.size() > 2)
+ {
+ return params;
+ }
+
+ const auto attributeIt = params.kwParams.find("attribute");
+ if (attributeIt == params.kwParams.cend())
+ {
+ return params;
+ }
+
+ FilterParams result;
+ result.kwParams["name"] = attributeIt->second;
+ result.kwParams["filter"] = std::make_shared<ConstantExpression>("attr"s);
+
+ const auto defaultIt = params.kwParams.find("default");
+ if (defaultIt != params.kwParams.cend())
+ result.kwParams["default"] = defaultIt->second;
+
+ return result;
+}
+
+InternalValue Map::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue filterName = GetArgumentValue("filter", context);
+ if (IsEmpty(filterName))
+ return InternalValue();
+
+ auto filter = CreateFilter(AsString(filterName), m_mappingParams);
+ if (!filter)
+ return InternalValue();
+
+ bool isConverted = false;
+ auto list = ConvertToList(baseVal, isConverted);
+ if (!isConverted)
+ return InternalValue();
+
+ InternalValueList resultList;
+ resultList.reserve(list.GetSize().value_or(0));
+ std::transform(list.begin(), list.end(), std::back_inserter(resultList), [filter, &context](auto& val) { return filter->Filter(val, context); });
+
+ return ListAdapter::CreateAdapter(std::move(resultList));
+}
+Random::Random(FilterParams params) {}
+
+InternalValue Random::Filter(const InternalValue&, RenderContext&)
+{
+ return InternalValue();
+}
+
+SequenceAccessor::SequenceAccessor(FilterParams params, SequenceAccessor::Mode mode)
+ : m_mode(mode)
+{
+ switch (mode)
+ {
+ case FirstItemMode:
+ break;
+ case LastItemMode:
+ break;
+ case LengthMode:
+ break;
+ case MaxItemMode:
+ case MinItemMode:
+ ParseParams({ { "case_sensitive", false, InternalValue(false) }, { "attribute", false } }, params);
+ break;
+ case RandomMode:
+ case ReverseMode:
+ break;
+ case SumItemsMode:
+ ParseParams({ { "attribute", false }, { "start", false } }, params);
+ break;
+ case UniqueItemsMode:
+ ParseParams({ { "attribute", false } }, params);
+ break;
+ }
+}
+
+InternalValue SequenceAccessor::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue result;
+
+ bool isConverted = false;
+ ListAdapter list = ConvertToList(baseVal, isConverted);
+
+ if (!isConverted)
+ return result;
+
+ InternalValue attrName = GetArgumentValue("attribute", context);
+ InternalValue isCsVal = GetArgumentValue("case_sensitive", context, InternalValue(false));
+
+ BinaryExpression::CompareType compType = ConvertToBool(isCsVal) ? BinaryExpression::CaseSensitive : BinaryExpression::CaseInsensitive;
+
+ auto lessComparator = [&attrName, &compType, &context](auto& val1, auto& val2) {
+ InternalValue cmpRes;
+
+ if (IsEmpty(attrName))
+ cmpRes = Apply2<visitors::BinaryMathOperation>(val1, val2, BinaryExpression::LogicalLt, compType);
+ else
+ cmpRes = Apply2<visitors::BinaryMathOperation>(
+ Subscript(val1, attrName, &context), Subscript(val2, attrName, &context), BinaryExpression::LogicalLt, compType);
+
+ return ConvertToBool(cmpRes);
+ };
+
+ const auto& listSize = list.GetSize();
+
+ switch (m_mode)
+ {
+ case FirstItemMode:
+ if (listSize)
+ result = list.GetValueByIndex(0);
+ else
+ {
+ auto it = list.begin();
+ if (it != list.end())
+ result = *it;
+ }
+ break;
+ case LastItemMode:
+ if (listSize)
+ result = list.GetValueByIndex(listSize.value() - 1);
+ else
+ {
+ auto it = list.begin();
+ auto end = list.end();
+ for (; it != end; ++it)
+ result = *it;
+ }
+ break;
+ case LengthMode:
+ if (listSize)
+ result = static_cast<int64_t>(listSize.value());
+ else
+ result = static_cast<int64_t>(std::distance(list.begin(), list.end()));
+ break;
+ case RandomMode:
+ {
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ if (listSize)
+ {
+ std::uniform_int_distribution<> dis(0, static_cast<int>(listSize.value()) - 1);
+ result = list.GetValueByIndex(dis(gen));
+ }
+ else
+ {
+ auto it = list.begin();
+ auto end = list.end();
+ size_t count = 0;
+ for (; it != end; ++it, ++count)
+ {
+ bool doCopy = count == 0 || std::uniform_int_distribution<size_t>(0, count)(gen) == 0;
+ if (doCopy)
+ result = *it;
+ }
+ }
+ break;
+ }
+ case MaxItemMode:
+ {
+ auto b = list.begin();
+ auto e = list.end();
+ auto p = std::max_element(list.begin(), list.end(), lessComparator);
+ result = p != e ? *p : InternalValue();
+ break;
+ }
+ case MinItemMode:
+ {
+ auto b = list.begin();
+ auto e = list.end();
+ auto p = std::min_element(b, e, lessComparator);
+ result = p != e ? *p : InternalValue();
+ break;
+ }
+ case ReverseMode:
+ {
+ if (listSize)
+ {
+ auto size = listSize.value();
+ InternalValueList resultList(size);
+ for (std::size_t n = 0; n < size; ++n)
+ resultList[size - n - 1] = list.GetValueByIndex(n);
+ result = ListAdapter::CreateAdapter(std::move(resultList));
+ }
+ else
+ {
+ InternalValueList resultList;
+ auto it = list.begin();
+ auto end = list.end();
+ for (; it != end; ++it)
+ resultList.push_back(*it);
+
+ std::reverse(resultList.begin(), resultList.end());
+ result = ListAdapter::CreateAdapter(std::move(resultList));
+ }
+
+ break;
+ }
+ case SumItemsMode:
+ {
+ ListAdapter l1;
+ ListAdapter* actualList;
+ if (IsEmpty(attrName))
+ {
+ actualList = &list;
+ }
+ else
+ {
+ l1 = list.ToSubscriptedList(attrName, true);
+ actualList = &l1;
+ }
+ InternalValue start = GetArgumentValue("start", context);
+ InternalValue resultVal = std::accumulate(actualList->begin(), actualList->end(), start, [](const InternalValue& cur, const InternalValue& val) {
+ if (IsEmpty(cur))
+ return val;
+
+ return Apply2<visitors::BinaryMathOperation>(cur, val, BinaryExpression::Plus);
+ });
+
+ result = std::move(resultVal);
+ break;
+ }
+ case UniqueItemsMode:
+ {
+ InternalValueList resultList;
+
+ struct Item
+ {
+ InternalValue val;
+ int64_t idx;
+ };
+ std::vector<Item> items;
+
+ int idx = 0;
+ for (auto& v : list)
+ items.push_back(Item{ IsEmpty(attrName) ? v : Subscript(v, attrName, &context), idx++ });
+
+ std::stable_sort(items.begin(), items.end(), [&compType](auto& i1, auto& i2) {
+ auto cmpRes = Apply2<visitors::BinaryMathOperation>(i1.val, i2.val, BinaryExpression::LogicalLt, compType);
+
+ return ConvertToBool(cmpRes);
+ });
+
+ auto end = std::unique(items.begin(), items.end(), [&compType](auto& i1, auto& i2) {
+ auto cmpRes = Apply2<visitors::BinaryMathOperation>(i1.val, i2.val, BinaryExpression::LogicalEq, compType);
+
+ return ConvertToBool(cmpRes);
+ });
+ items.erase(end, items.end());
+
+ std::stable_sort(items.begin(), items.end(), [](auto& i1, auto& i2) { return i1.idx < i2.idx; });
+
+ for (auto& i : items)
+ resultList.push_back(list.GetValueByIndex(i.idx));
+
+ result = ListAdapter::CreateAdapter(std::move(resultList));
+ break;
+ }
+ }
+
+ return result;
+}
+Slice::Slice(FilterParams params, Slice::Mode mode)
+ : m_mode{ mode }
+{
+ if (m_mode == BatchMode)
+ ParseParams({ { "linecount"s, true }, { "fill_with"s, false } }, params);
+ else
+ ParseParams({ { "slices"s, true }, { "fill_with"s, false } }, params);
+}
+
+InternalValue Slice::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ if (m_mode == BatchMode)
+ return Batch(baseVal, context);
+
+ InternalValue result;
+
+ bool isConverted = false;
+ ListAdapter list = ConvertToList(baseVal, isConverted);
+
+ if (!isConverted)
+ return result;
+
+ InternalValue sliceLengthValue = GetArgumentValue("slices", context);
+ int64_t sliceLength = ConvertToInt(sliceLengthValue);
+ InternalValue fillWith = GetArgumentValue("fill_with", context);
+
+ InternalValueList resultList;
+ InternalValueList sublist;
+ int sublistItemIndex = 0;
+ for (auto& item : list)
+ {
+ if (sublistItemIndex == 0)
+ sublist.clear();
+ if (sublistItemIndex == sliceLength)
+ {
+ resultList.push_back(ListAdapter::CreateAdapter(std::move(sublist)));
+ sublist.clear();
+ sublistItemIndex %= sliceLength;
+ }
+ sublist.push_back(item);
+ ++sublistItemIndex;
+ }
+ if (!IsEmpty(fillWith))
+ {
+ while (sublistItemIndex++ < sliceLength)
+ sublist.push_back(fillWith);
+ }
+ if (sublistItemIndex > 0)
+ resultList.push_back(ListAdapter::CreateAdapter(std::move(sublist)));
+
+ return InternalValue(ListAdapter::CreateAdapter(std::move(resultList)));
+}
+
+InternalValue Slice::Batch(const InternalValue& baseVal, RenderContext& context)
+{
+ auto linecount_value = ConvertToInt(GetArgumentValue("linecount", context));
+ InternalValue fillWith = GetArgumentValue("fill_with", context);
+
+ if (linecount_value <= 0)
+ return InternalValue();
+ auto linecount = static_cast<std::size_t>(linecount_value);
+
+ bool isConverted = false;
+ auto list = ConvertToList(baseVal, isConverted);
+ if (!isConverted)
+ return InternalValue();
+
+ auto elementsCount = list.GetSize().value_or(0);
+ if (!elementsCount)
+ return InternalValue();
+
+ InternalValueList resultList;
+ resultList.reserve(linecount);
+
+ const auto remainder = elementsCount % linecount;
+ const auto columns = elementsCount / linecount + (remainder > 0 ? 1 : 0);
+ for (std::size_t line = 0, idx = 0; line < linecount; ++line)
+ {
+ const auto elems = columns - (remainder && line >= remainder ? 1 : 0);
+ InternalValueList row;
+ row.reserve(columns);
+ std::fill_n(std::back_inserter(row), columns, fillWith);
+
+ for (std::size_t column = 0; column < elems; ++column)
+ row[column] = list.GetValueByIndex(idx++);
+
+ resultList.push_back(ListAdapter::CreateAdapter(std::move(row)));
+ }
+ return ListAdapter::CreateAdapter(std::move(resultList));
+}
+
+StringFormat::StringFormat(FilterParams params)
+{
+ ParseParams({}, params);
+ m_params.kwParams = std::move(m_args.extraKwArgs);
+ m_params.posParams = std::move(m_args.extraPosArgs);
+}
+
+Tester::Tester(FilterParams params, Tester::Mode mode)
+ : m_mode(mode)
+{
+ FilterParams newParams;
+
+ if ((mode == RejectMode || mode == SelectMode) && params.kwParams.empty() && params.posParams.empty())
+ {
+ m_noParams = true;
+ return;
+ }
+
+ if (mode == RejectMode || mode == SelectMode)
+ ParseParams({ { "tester", false } }, params);
+ else
+ ParseParams({ { "attribute", true }, { "tester", false } }, params);
+
+ m_testingParams.kwParams = std::move(m_args.extraKwArgs);
+ m_testingParams.posParams = std::move(m_args.extraPosArgs);
+}
+
+InternalValue Tester::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue testerName = GetArgumentValue("tester", context);
+ InternalValue attrName = GetArgumentValue("attribute", context);
+
+ TesterPtr tester;
+
+ if (!IsEmpty(testerName))
+ {
+ tester = CreateTester(AsString(testerName), m_testingParams);
+
+ if (!tester)
+ return InternalValue();
+ }
+
+ bool isConverted = false;
+ auto list = ConvertToList(baseVal, isConverted);
+ if (!isConverted)
+ return InternalValue();
+
+ InternalValueList resultList;
+ resultList.reserve(list.GetSize().value_or(0));
+ std::copy_if(list.begin(), list.end(), std::back_inserter(resultList), [this, tester, attrName, &context](auto& val) {
+ InternalValue attrVal;
+ bool isAttr = !IsEmpty(attrName);
+ if (isAttr)
+ attrVal = Subscript(val, attrName, &context);
+
+ bool result = false;
+ if (tester)
+ result = tester->Test(isAttr ? attrVal : val, context);
+ else
+ result = ConvertToBool(isAttr ? attrVal : val);
+
+ return (m_mode == SelectMode || m_mode == SelectAttrMode) ? result : !result;
+ });
+
+ return ListAdapter::CreateAdapter(std::move(resultList));
+}
+
+ValueConverter::ValueConverter(FilterParams params, ValueConverter::Mode mode)
+ : m_mode(mode)
+{
+ switch (mode)
+ {
+ case ToFloatMode:
+ ParseParams({ { "default"s, false } }, params);
+ break;
+ case ToIntMode:
+ ParseParams({ { "default"s, false }, { "base"s, false, static_cast<int64_t>(10) } }, params);
+ break;
+ case ToListMode:
+ case AbsMode:
+ break;
+ case RoundMode:
+ ParseParams({ { "precision"s, false }, { "method"s, false, "common"s } }, params);
+ break;
+ }
+}
+
+struct ConverterParams
+{
+ ValueConverter::Mode mode;
+ InternalValue defValule;
+ InternalValue base;
+ InternalValue prec;
+ InternalValue roundMethod;
+};
+
+struct ValueConverterImpl : visitors::BaseVisitor<>
+{
+ using BaseVisitor::operator();
+
+ ValueConverterImpl(ConverterParams params)
+ : m_params(std::move(params))
+ {
+ }
+
+ InternalValue operator()(int64_t val) const
+ {
+ InternalValue result;
+ switch (m_params.mode)
+ {
+ case ValueConverter::ToFloatMode:
+ result = InternalValue(static_cast<double>(val));
+ break;
+ case ValueConverter::AbsMode:
+ result = InternalValue(static_cast<int64_t>(std::abs(val)));
+ break;
+ case ValueConverter::ToIntMode:
+ case ValueConverter::RoundMode:
+ result = InternalValue(static_cast<int64_t>(val));
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator()(double val) const
+ {
+ InternalValue result;
+ switch (m_params.mode)
+ {
+ case ValueConverter::ToFloatMode:
+ result = static_cast<double>(val);
+ break;
+ case ValueConverter::ToIntMode:
+ result = static_cast<int64_t>(val);
+ break;
+ case ValueConverter::AbsMode:
+ result = InternalValue(fabs(val));
+ break;
+ case ValueConverter::RoundMode:
+ {
+ auto method = AsString(m_params.roundMethod);
+ auto prec = GetAs<int64_t>(m_params.prec);
+ double pow10 = std::pow(10, static_cast<int>(prec));
+ val *= pow10;
+ if (method == "ceil")
+ val = val < 0 ? std::floor(val) : std::ceil(val);
+ else if (method == "floor")
+ val = val > 0 ? std::floor(val) : std::ceil(val);
+ else if (method == "common")
+ val = std::round(val);
+ result = InternalValue(val / pow10);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ static double ConvertToDouble(const char* buff, bool& isConverted)
+ {
+ char* endBuff = nullptr;
+ double dblVal = strtod(buff, &endBuff);
+ isConverted = *endBuff == 0;
+ return dblVal;
+ }
+
+ static double ConvertToDouble(const wchar_t* buff, bool& isConverted)
+ {
+ wchar_t* endBuff = nullptr;
+ double dblVal = wcstod(buff, &endBuff);
+ isConverted = *endBuff == 0;
+ return dblVal;
+ }
+
+ static long long ConvertToInt(const char* buff, int base, bool& isConverted)
+ {
+ char* endBuff = nullptr;
+ long long intVal = strtoll(buff, &endBuff, base);
+ isConverted = *endBuff == 0;
+ return intVal;
+ }
+
+ static long long ConvertToInt(const wchar_t* buff, int base, bool& isConverted)
+ {
+ wchar_t* endBuff = nullptr;
+ long long intVal = wcstoll(buff, &endBuff, base);
+ isConverted = *endBuff == 0;
+ return intVal;
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const std::basic_string<CharT>& val) const
+ {
+ InternalValue result;
+ switch (m_params.mode)
+ {
+ case ValueConverter::ToFloatMode:
+ {
+ bool converted = false;
+ double dblVal = ConvertToDouble(val.c_str(), converted);
+
+ if (!converted)
+ result = m_params.defValule;
+ else
+ result = dblVal;
+ break;
+ }
+ case ValueConverter::ToIntMode:
+ {
+ int base = static_cast<int>(GetAs<int64_t>(m_params.base));
+ bool converted = false;
+ long long intVal = ConvertToInt(val.c_str(), base, converted);
+
+ if (!converted)
+ result = m_params.defValule;
+ else
+ result = static_cast<int64_t>(intVal);
+ break;
+ }
+ case ValueConverter::ToListMode:
+ result = ListAdapter::CreateAdapter(val.size(), [str = val](size_t idx) { return InternalValue(TargetString(str.substr(idx, 1))); });
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const std::basic_string_view<CharT>& val) const
+ {
+ InternalValue result;
+ switch (m_params.mode)
+ {
+ case ValueConverter::ToFloatMode:
+ {
+ bool converted = false;
+ std::basic_string<CharT> str(val.begin(), val.end());
+ double dblVal = ConvertToDouble(str.c_str(), converted);
+
+ if (!converted)
+ result = m_params.defValule;
+ else
+ result = static_cast<double>(dblVal);
+ break;
+ }
+ case ValueConverter::ToIntMode:
+ {
+ int base = static_cast<int>(GetAs<int64_t>(m_params.base));
+ bool converted = false;
+ std::basic_string<CharT> str(val.begin(), val.end());
+ long long intVal = ConvertToInt(str.c_str(), base, converted);
+
+ if (!converted)
+ result = m_params.defValule;
+ else
+ result = static_cast<int64_t>(intVal);
+ break;
+ }
+ case ValueConverter::ToListMode:
+ result = ListAdapter::CreateAdapter(val.size(), [str = val](size_t idx) { return InternalValue(str.substr(idx, 1)); });
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator()(const ListAdapter& val) const
+ {
+ if (m_params.mode == ValueConverter::ToListMode)
+ return InternalValue(val);
+
+ return InternalValue();
+ }
+
+ InternalValue operator()(const MapAdapter& val) const
+ {
+ if (m_params.mode != ValueConverter::ToListMode)
+ return InternalValue();
+
+ auto keys = val.GetKeys();
+ auto num_keys = keys.size();
+ return ListAdapter::CreateAdapter(num_keys, [values = std::move(keys)](size_t idx) { return InternalValue(values[idx]); });
+ }
+
+ template<typename T>
+ static T GetAs(const InternalValue& val, T defValue = 0)
+ {
+ ConverterParams params;
+ params.mode = ValueConverter::ToIntMode;
+ params.base = static_cast<int64_t>(10);
+ InternalValue intVal = Apply<ValueConverterImpl>(val, params);
+ const T* result = GetIf<int64_t>(&intVal);
+ if (result == nullptr)
+ return defValue;
+
+ return *result;
+ }
+
+ ConverterParams m_params;
+};
+
+InternalValue ValueConverter::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ ConverterParams params;
+ params.mode = m_mode;
+ params.defValule = GetArgumentValue("default", context);
+ params.base = GetArgumentValue("base", context);
+ params.prec = GetArgumentValue("precision", context);
+ params.roundMethod = GetArgumentValue("method", context);
+ auto result = Apply<ValueConverterImpl>(baseVal, params);
+ if (baseVal.ShouldExtendLifetime())
+ result.SetParentData(baseVal);
+
+ return result;
+}
+
+UserDefinedFilter::UserDefinedFilter(std::string filterName, FilterParams params)
+ : m_filterName(std::move(filterName))
+{
+ ParseParams({ { "*args" }, { "**kwargs" } }, params);
+ m_callParams.kwParams = m_args.extraKwArgs;
+ m_callParams.posParams = m_args.extraPosArgs;
+}
+
+InternalValue UserDefinedFilter::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ bool filterFound = false;
+ auto filterValPtr = context.FindValue(m_filterName, filterFound);
+ if (!filterFound)
+ throw std::runtime_error("Can't find filter '" + m_filterName + "'");
+
+ const Callable* callable = GetIf<Callable>(&filterValPtr->second);
+ if (callable == nullptr || callable->GetKind() != Callable::UserCallable)
+ return InternalValue();
+
+ CallParams tmpCallParams = helpers::EvaluateCallParams(m_callParams, context);
+ CallParams callParams;
+ callParams.kwParams = std::move(tmpCallParams.kwParams);
+ callParams.posParams.reserve(tmpCallParams.posParams.size() + 1);
+ callParams.posParams.push_back(baseVal);
+ for (auto& p : tmpCallParams.posParams)
+ callParams.posParams.push_back(std::move(p));
+
+ InternalValue result;
+ if (callable->GetType() != Callable::Type::Expression)
+ return InternalValue();
+
+ return callable->GetExpressionCallable()(callParams, context);
+}
+
+} // namespace filters
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/filters.h b/contrib/libs/jinja2cpp/src/filters.h
new file mode 100644
index 0000000000..1fdfc465c3
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/filters.h
@@ -0,0 +1,474 @@
+#ifndef JINJA2CPP_SRC_FILTERS_H
+#define JINJA2CPP_SRC_FILTERS_H
+
+#include "expression_evaluator.h"
+#include "function_base.h"
+#include "jinja2cpp/value.h"
+#include "render_context.h"
+
+#include <memory>
+#include <functional>
+
+namespace jinja2
+{
+using FilterPtr = std::shared_ptr<ExpressionFilter::IExpressionFilter>;
+using FilterParams = CallParamsInfo;
+
+extern FilterPtr CreateFilter(std::string filterName, CallParamsInfo params);
+
+namespace filters
+{
+class FilterBase : public FunctionBase, public ExpressionFilter::IExpressionFilter
+{
+};
+
+class ApplyMacro : public FilterBase
+{
+public:
+ ApplyMacro(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const ApplyMacro*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mappingParams != value->m_mappingParams)
+ return false;
+ return true;
+ }
+private:
+ FilterParams m_mappingParams;
+};
+
+class Attribute : public FilterBase
+{
+public:
+ Attribute(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Attribute*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class Default : public FilterBase
+{
+public:
+ Default(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Default*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class DictSort : public FilterBase
+{
+public:
+ DictSort(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const DictSort*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class GroupBy : public FilterBase
+{
+public:
+ GroupBy(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const GroupBy*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class Join : public FilterBase
+{
+public:
+ Join(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Join*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class Map : public FilterBase
+{
+public:
+ Map(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Map*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mappingParams != value->m_mappingParams)
+ return false;
+ return true;
+ }
+private:
+ static FilterParams MakeParams(FilterParams);
+
+ FilterParams m_mappingParams;
+};
+
+class PrettyPrint : public FilterBase
+{
+public:
+ PrettyPrint(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const PrettyPrint*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class Random : public FilterBase
+{
+public:
+ Random(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Random*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class SequenceAccessor : public FilterBase
+{
+public:
+ enum Mode
+ {
+ FirstItemMode,
+ LastItemMode,
+ LengthMode,
+ MaxItemMode,
+ MinItemMode,
+ RandomMode,
+ ReverseMode,
+ SumItemsMode,
+ UniqueItemsMode,
+
+ };
+
+ SequenceAccessor(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const SequenceAccessor*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mode != value->m_mode)
+ return false;
+ return true;
+ }
+private:
+ Mode m_mode;
+};
+
+class Serialize : public FilterBase
+{
+public:
+ enum Mode
+ {
+ JsonMode,
+ XmlMode,
+ YamlMode
+ };
+
+ Serialize(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Serialize*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mode != value->m_mode)
+ return false;
+ return true;
+ }
+private:
+ Mode m_mode;
+};
+
+class Slice : public FilterBase
+{
+public:
+ enum Mode
+ {
+ BatchMode,
+ SliceMode,
+ };
+
+ Slice(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Slice*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mode != value->m_mode)
+ return false;
+ return true;
+ }
+private:
+ InternalValue Batch(const InternalValue& baseVal, RenderContext& context);
+
+ Mode m_mode;
+};
+
+class Sort : public FilterBase
+{
+public:
+ Sort(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Sort*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class StringConverter : public FilterBase
+{
+public:
+ enum Mode
+ {
+ CapitalMode,
+ CamelMode,
+ EscapeCppMode,
+ EscapeHtmlMode,
+ LowerMode,
+ ReplaceMode,
+ StriptagsMode,
+ TitleMode,
+ TrimMode,
+ TruncateMode,
+ UpperMode,
+ WordCountMode,
+ WordWrapMode,
+ UnderscoreMode,
+ UrlEncodeMode,
+ CenterMode
+ };
+
+ StringConverter(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const StringConverter*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mode != value->m_mode)
+ return false;
+ return true;
+ }
+private:
+ Mode m_mode;
+};
+
+class StringFormat : public FilterBase
+{
+public:
+ StringFormat(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const StringFormat*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_params != value->m_params)
+ return false;
+ return true;
+ }
+
+private:
+ FilterParams m_params;
+};
+
+class Tester : public FilterBase
+{
+public:
+ enum Mode
+ {
+ RejectMode,
+ RejectAttrMode,
+ SelectMode,
+ SelectAttrMode,
+ };
+
+ Tester(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const Tester*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_mode != value->m_mode)
+ return false;
+ if (m_testingParams != value->m_testingParams)
+ return false;
+ return true;
+ }
+private:
+ Mode m_mode;
+ FilterParams m_testingParams;
+ bool m_noParams = false;
+};
+
+class ValueConverter : public FilterBase
+{
+public:
+ enum Mode
+ {
+ ToFloatMode,
+ ToIntMode,
+ ToListMode,
+ AbsMode,
+ RoundMode,
+ };
+
+ ValueConverter(FilterParams params, Mode mode);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const ValueConverter*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return m_mode == value->m_mode;
+ }
+private:
+ Mode m_mode;
+};
+
+class XmlAttrFilter : public FilterBase
+{
+public:
+ explicit XmlAttrFilter(FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const XmlAttrFilter*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ return true;
+ }
+};
+
+class UserDefinedFilter : public FilterBase
+{
+public:
+ UserDefinedFilter(std::string filterName, FilterParams params);
+
+ InternalValue Filter(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* value = dynamic_cast<const UserDefinedFilter*>(&other);
+ if (!value)
+ return false;
+ if (m_args != value->m_args)
+ return false;
+ if (m_filterName != value->m_filterName)
+ return false;
+ if (m_callParams != m_callParams)
+ return false;
+ return true;
+ }
+
+private:
+ std::string m_filterName;
+ FilterParams m_callParams;
+};
+
+} // namespace filters
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_FILTERS_H
diff --git a/contrib/libs/jinja2cpp/src/function_base.h b/contrib/libs/jinja2cpp/src/function_base.h
new file mode 100644
index 0000000000..578545b031
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/function_base.h
@@ -0,0 +1,49 @@
+#ifndef JINJA2CPP_SRC_FUNCTION_BASE_H
+#define JINJA2CPP_SRC_FUNCTION_BASE_H
+
+#include "expression_evaluator.h"
+#include "internal_value.h"
+
+namespace jinja2
+{
+class FunctionBase
+{
+public:
+ bool operator==(const FunctionBase& other) const
+ {
+ return m_args == other.m_args;
+ }
+ bool operator!=(const FunctionBase& other) const
+ {
+ return !(*this == other);
+ }
+protected:
+ bool ParseParams(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParamsInfo& params);
+ InternalValue GetArgumentValue(const std::string& argName, RenderContext& context, InternalValue defVal = InternalValue());
+
+protected:
+ ParsedArgumentsInfo m_args;
+};
+
+//bool operator==(const FunctionBase& lhs, const FunctionBase& rhs)
+//{
+// return
+//}
+
+inline bool FunctionBase::ParseParams(const std::initializer_list<ArgumentInfo>& argsInfo, const CallParamsInfo& params)
+{
+ bool result = true;
+ m_args = helpers::ParseCallParamsInfo(argsInfo, params, result);
+
+ return result;
+}
+
+inline InternalValue FunctionBase::GetArgumentValue(const std::string& argName, RenderContext& context, InternalValue defVal)
+{
+ auto argExpr = m_args[argName];
+ return argExpr ? argExpr->Evaluate(context) : std::move(defVal);
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_FUNCTION_BASE_H
diff --git a/contrib/libs/jinja2cpp/src/generic_adapters.h b/contrib/libs/jinja2cpp/src/generic_adapters.h
new file mode 100644
index 0000000000..e6d0c9bcb0
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/generic_adapters.h
@@ -0,0 +1,215 @@
+#ifndef JINJA2CPP_SRC_GENERIC_ADAPTERS_H
+#define JINJA2CPP_SRC_GENERIC_ADAPTERS_H
+
+#include <jinja2cpp/value.h>
+#include "internal_value.h"
+
+namespace jinja2
+{
+
+template<typename ImplType, typename List, typename ValType, typename Base>
+class IndexedEnumeratorImpl : public Base
+{
+public:
+ using ValueType = ValType;
+ using ThisType = IndexedEnumeratorImpl<ImplType, List, ValType, Base>;
+
+ IndexedEnumeratorImpl(const List* list)
+ : m_list(list)
+ , m_maxItems(list->GetSize().value())
+ { }
+
+ void Reset() override
+ {
+ m_curItem = m_invalidIndex;
+ }
+
+ bool MoveNext() override
+ {
+ if (m_curItem == m_invalidIndex)
+ m_curItem = 0;
+ else
+ ++ m_curItem;
+
+ return m_curItem < m_maxItems;
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ if (m_list && val->m_list && !m_list->IsEqual(*val->m_list))
+ return false;
+ if ((m_list && !val->m_list) || (!m_list && val->m_list))
+ return false;
+ if (m_curItem != val->m_curItem)
+ return false;
+ if (m_maxItems != val->m_maxItems)
+ return false;
+ return true;
+ }
+
+protected:
+ constexpr static auto m_invalidIndex = std::numeric_limits<size_t>::max();
+ const List* m_list{};
+ size_t m_curItem = m_invalidIndex;
+ size_t m_maxItems{};
+};
+
+
+template<typename T>
+class IndexedListItemAccessorImpl : public IListItemAccessor, public IIndexBasedAccessor
+{
+public:
+ using ThisType = IndexedListItemAccessorImpl<T>;
+ class Enumerator : public IndexedEnumeratorImpl<Enumerator, ThisType, Value, IListEnumerator>
+ {
+ public:
+ using BaseClass = IndexedEnumeratorImpl<Enumerator, ThisType, Value, IListEnumerator>;
+#if defined(_MSC_VER)
+ using IndexedEnumeratorImpl::IndexedEnumeratorImpl;
+#else
+ using BaseClass::BaseClass;
+#endif
+
+ typename BaseClass::ValueType GetCurrent() const override
+ {
+ auto indexer = this->m_list->GetIndexer();
+ if (!indexer)
+ return Value();
+
+ return indexer->GetItemByIndex(this->m_curItem);
+ }
+ ListEnumeratorPtr Clone() const override
+ {
+ auto result = MakeEnumerator<Enumerator>(this->m_list);
+ auto base = static_cast<Enumerator*>(&(*result));
+ base->m_curItem = this->m_curItem;
+ return result;
+ }
+
+ ListEnumeratorPtr Move() override
+ {
+ auto result = MakeEnumerator<Enumerator>(this->m_list);
+ auto base = static_cast<Enumerator*>(&(*result));
+ base->m_curItem = this->m_curItem;
+ this->m_list = nullptr;
+ this->m_curItem = this->m_invalidIndex;
+ this->m_maxItems = 0;
+ return result;
+ }
+ };
+
+ Value GetItemByIndex(int64_t idx) const override
+ {
+ return IntValue2Value(std::move(static_cast<const T*>(this)->GetItem(idx).value()));
+ }
+
+ std::optional<size_t> GetSize() const override
+ {
+ return static_cast<const T*>(this)->GetItemsCountImpl();
+ }
+
+ const IIndexBasedAccessor* GetIndexer() const override
+ {
+ return this;
+ }
+
+ ListEnumeratorPtr CreateEnumerator() const override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ThisType*>(&other);
+ if (!val)
+ return false;
+ auto enumerator = CreateEnumerator();
+ auto otherEnum = val->CreateEnumerator();
+ if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum))
+ return false;
+ return true;
+ }
+};
+
+template<typename T>
+class IndexedListAccessorImpl : public IListAccessor, public IndexedListItemAccessorImpl<T>
+{
+public:
+ using ThisType = IndexedListAccessorImpl<T>;
+ class Enumerator : public IndexedEnumeratorImpl<Enumerator, ThisType, InternalValue, IListAccessorEnumerator>
+ {
+ public:
+ using BaseClass = IndexedEnumeratorImpl<Enumerator, ThisType, InternalValue, IListAccessorEnumerator>;
+#if defined(_MSC_VER)
+ using IndexedEnumeratorImpl::IndexedEnumeratorImpl;
+#else
+ using BaseClass::BaseClass;
+#endif
+
+ typename BaseClass::ValueType GetCurrent() const override
+ {
+ const auto& result = this->m_list->GetItem(this->m_curItem);
+ if (!result)
+ return InternalValue();
+
+ return result.value();
+ }
+
+ IListAccessorEnumerator* Clone() const override
+ {
+ auto result = new Enumerator(this->m_list);
+ auto base = result;
+ base->m_curItem = this->m_curItem;
+ return result;
+ }
+
+ IListAccessorEnumerator* Transfer() override
+ {
+ auto result = new Enumerator(std::move(*this));
+ auto base = result;
+ base->m_curItem = this->m_curItem;
+ this->m_list = nullptr;
+ this->m_curItem = this->m_invalidIndex;
+ this->m_maxItems = 0;
+ return result;
+ }
+ };
+
+ std::optional<size_t> GetSize() const override
+ {
+ return static_cast<const T*>(this)->GetItemsCountImpl();
+ }
+ ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override;
+};
+
+template<typename T>
+class MapItemAccessorImpl : public IMapItemAccessor
+{
+public:
+ Value GetValueByName(const std::string& name) const
+ {
+ return IntValue2Value(static_cast<const T*>(this)->GetItem(name));
+ }
+};
+
+template<typename T>
+class MapAccessorImpl : public IMapAccessor, public MapItemAccessorImpl<T>
+{
+public:
+};
+
+template<typename T>
+inline ListAccessorEnumeratorPtr IndexedListAccessorImpl<T>::CreateListAccessorEnumerator() const
+{
+ return ListAccessorEnumeratorPtr(new Enumerator(this));
+}
+
+template<typename T>
+inline ListEnumeratorPtr IndexedListItemAccessorImpl<T>::CreateEnumerator() const
+{
+ return MakeEnumerator<Enumerator>(this);
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_GENERIC_ADAPTERS_H
diff --git a/contrib/libs/jinja2cpp/src/generic_list.cpp b/contrib/libs/jinja2cpp/src/generic_list.cpp
new file mode 100644
index 0000000000..883ef3615a
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/generic_list.cpp
@@ -0,0 +1,38 @@
+#include <jinja2cpp/generic_list.h>
+#include <jinja2cpp/generic_list_iterator.h>
+
+namespace jinja2 {
+
+detail::GenericListIterator GenericList::begin() const
+{
+ return m_accessor && m_accessor() ? detail::GenericListIterator(m_accessor()->CreateEnumerator()) : detail::GenericListIterator();
+}
+
+detail::GenericListIterator GenericList::end() const
+{
+ return detail::GenericListIterator();
+}
+
+auto GenericList::cbegin() const {return begin();}
+auto GenericList::cend() const {return end();}
+
+bool GenericList::IsEqual(const GenericList& rhs) const
+{
+ if (IsValid() && rhs.IsValid() && !GetAccessor()->IsEqual(*rhs.GetAccessor()))
+ return false;
+ if ((IsValid() && !rhs.IsValid()) || (!IsValid() && rhs.IsValid()))
+ return false;
+ return true;
+}
+
+bool operator==(const GenericList& lhs, const GenericList& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+
+bool operator!=(const GenericList& lhs, const GenericList& rhs)
+{
+ return !(lhs == rhs);
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/helpers.h b/contrib/libs/jinja2cpp/src/helpers.h
new file mode 100644
index 0000000000..74e87400df
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/helpers.h
@@ -0,0 +1,119 @@
+#ifndef JINJA2CPP_SRC_HELPERS_H
+#define JINJA2CPP_SRC_HELPERS_H
+
+#include <string_view>
+#include <jinja2cpp/string_helpers.h>
+
+#include <string>
+#include <type_traits>
+#include <cwchar>
+
+namespace jinja2
+{
+struct MultiStringLiteral
+{
+ const char* charValue;
+ const wchar_t* wcharValue;
+
+ constexpr MultiStringLiteral(const char* val, const wchar_t* wval)
+ : charValue(val)
+ , wcharValue(wval)
+ {
+ }
+
+ template<typename CharT>
+ constexpr auto GetValue() const
+ {
+#if __cplusplus < 202002L
+ return GetValueStr<CharT>();
+#else
+ constexpr auto memPtr = SelectMemberPtr<CharT, &MultiStringLiteral::charValue, &MultiStringLiteral::wcharValue>::GetPtr();
+ return std::basic_string_view<CharT>(this->*memPtr);
+#endif
+ }
+
+ template<typename CharT>
+ constexpr auto GetValueStr() const
+ {
+ constexpr auto memPtr = SelectMemberPtr<CharT, &MultiStringLiteral::charValue, &MultiStringLiteral::wcharValue>::GetPtr();
+ return std::basic_string<CharT>(this->*memPtr);
+ }
+
+ template<typename CharT, const char* MultiStringLiteral::*, const wchar_t* MultiStringLiteral::*>
+ struct SelectMemberPtr;
+
+ template<const char* (MultiStringLiteral::*charMemPtr), const wchar_t* (MultiStringLiteral::*wcharMemPtr)>
+ struct SelectMemberPtr<char, charMemPtr, wcharMemPtr>
+ {
+ static constexpr auto GetPtr() {return charMemPtr;}
+ };
+
+ template<const char* (MultiStringLiteral::*charMemPtr), const wchar_t* (MultiStringLiteral::*wcharMemPtr)>
+ struct SelectMemberPtr<wchar_t, charMemPtr, wcharMemPtr>
+ {
+ static constexpr auto GetPtr() {return wcharMemPtr;}
+ };
+
+ template<typename CharT>
+ friend std::basic_ostream<CharT>& operator << (std::basic_ostream<CharT>& os, const MultiStringLiteral& lit)
+ {
+ os << lit.GetValue<CharT>();
+ return os;
+ }
+};
+
+#define UNIVERSAL_STR(Str) \
+ ::jinja2::MultiStringLiteral { Str, L##Str }
+
+//! CompileEscapes replaces escape characters by their meanings.
+/**
+ * @param[in] s Characters sequence with zero or more escape characters.
+ * @return Characters sequence copy where replaced all escape characters by
+ * their meanings.
+ */
+template<typename Sequence>
+Sequence CompileEscapes(Sequence s)
+{
+ auto itr1 = s.begin();
+ auto itr2 = s.begin();
+ const auto end = s.cend();
+
+ auto removalCount = 0;
+
+ while (end != itr1)
+ {
+ if ('\\' == *itr1)
+ {
+ ++removalCount;
+
+ if (end == ++itr1)
+ break;
+ if ('\\' != *itr1)
+ {
+ switch (*itr1)
+ {
+ case 'n': *itr1 = '\n'; break;
+ case 'r': *itr1 = '\r'; break;
+ case 't': *itr1 = '\t'; break;
+ default: break;
+ }
+
+ continue;
+ }
+ }
+
+ if (itr1 != itr2)
+ *itr2 = *itr1;
+
+ ++itr1;
+ ++itr2;
+ }
+
+ s.resize(s.size() - removalCount);
+
+ return s;
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_HELPERS_H
diff --git a/contrib/libs/jinja2cpp/src/internal_value.cpp b/contrib/libs/jinja2cpp/src/internal_value.cpp
new file mode 100644
index 0000000000..7683bc2c97
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/internal_value.cpp
@@ -0,0 +1,1053 @@
+#include "internal_value.h"
+
+#include "expression_evaluator.h"
+#include "generic_adapters.h"
+#include "helpers.h"
+#include "value_visitors.h"
+
+namespace jinja2
+{
+
+std::atomic_uint64_t UserCallable::m_gen{};
+
+bool Value::IsEqual(const Value& rhs) const
+{
+ return this->m_data == rhs.m_data;
+}
+
+bool operator==(const Value& lhs, const Value& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+
+bool operator!=(const Value& lhs, const Value& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const GenericMap& lhs, const GenericMap& rhs)
+{
+ auto* lhsAccessor = lhs.GetAccessor();
+ auto* rhsAccessor = rhs.GetAccessor();
+ return lhsAccessor && rhsAccessor && lhsAccessor->IsEqual(*rhsAccessor);
+}
+
+bool operator!=(const GenericMap& lhs, const GenericMap& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const UserCallable& lhs, const UserCallable& rhs)
+{
+ // TODO: rework
+ return lhs.IsEqual(rhs);
+}
+
+bool operator!=(const UserCallable& lhs, const UserCallable& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const types::ValuePtr<UserCallable>& lhs, const types::ValuePtr<UserCallable>& rhs)
+{
+ if (lhs && rhs)
+ return *lhs == *rhs;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+
+bool operator!=(const types::ValuePtr<UserCallable>& lhs, const types::ValuePtr<UserCallable>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const types::ValuePtr<ValuesMap>& lhs, const types::ValuePtr<ValuesMap>& rhs)
+{
+ if (lhs && rhs)
+ return *lhs == *rhs;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+
+bool operator!=(const types::ValuePtr<ValuesMap>& lhs, const types::ValuePtr<ValuesMap>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const types::ValuePtr<Value>& lhs, const types::ValuePtr<Value>& rhs)
+{
+ if (lhs && rhs)
+ return *lhs == *rhs;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+
+bool operator!=(const types::ValuePtr<Value>& lhs, const types::ValuePtr<Value>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const types::ValuePtr<std::vector<Value>>& lhs, const types::ValuePtr<std::vector<Value>>& rhs)
+{
+ if (lhs && rhs)
+ return *lhs == *rhs;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+
+bool operator!=(const types::ValuePtr<std::vector<Value>>& lhs, const types::ValuePtr<std::vector<Value>>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool InternalValue::IsEqual(const InternalValue &other) const
+{
+ if (m_data != other.m_data)
+ return false;
+ return m_parentData == other.m_parentData;
+}
+
+InternalValue Value2IntValue(const Value& val);
+InternalValue Value2IntValue(Value&& val);
+
+struct SubscriptionVisitor : public visitors::BaseVisitor<>
+{
+ using BaseVisitor<>::operator();
+
+ template<typename CharT>
+ InternalValue operator()(const MapAdapter& values, const std::basic_string<CharT>& fieldName) const
+ {
+ auto field = ConvertString<std::string>(fieldName);
+ if (!values.HasValue(field))
+ return InternalValue();
+
+ return values.GetValueByName(field);
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const MapAdapter& values, const std::basic_string_view<CharT>& fieldName) const
+ {
+ auto field = ConvertString<std::string>(fieldName);
+ if (!values.HasValue(field))
+ return InternalValue();
+
+ return values.GetValueByName(field);
+ }
+
+ template<typename CharT>
+ InternalValue operator()(std::basic_string<CharT> value, const std::basic_string<CharT>& /*fieldName*/) const
+ {
+ return TargetString(std::move(value));
+ }
+
+ InternalValue operator()(const ListAdapter& values, int64_t index) const
+ {
+ if (index < 0 || static_cast<size_t>(index) >= values.GetSize())
+ return InternalValue();
+
+ return values.GetValueByIndex(index);
+ }
+
+ InternalValue operator()(const MapAdapter& /*values*/, int64_t /*index*/) const { return InternalValue(); }
+
+ template<typename CharT>
+ InternalValue operator()(const std::basic_string<CharT>& str, int64_t index) const
+ {
+ if (index < 0 || static_cast<size_t>(index) >= str.size())
+ return InternalValue();
+
+ std::basic_string<CharT> resultStr(1, str[static_cast<size_t>(index)]);
+ return TargetString(std::move(resultStr));
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const std::basic_string_view<CharT>& str, int64_t index) const
+ {
+ // std::cout << "operator() (const std::basic_string<CharT>& str, int64_t index)" << ": index = " << index << std::endl;
+ if (index < 0 || static_cast<size_t>(index) >= str.size())
+ return InternalValue();
+
+ std::basic_string<CharT> result(1, str[static_cast<size_t>(index)]);
+ return TargetString(std::move(result));
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const KeyValuePair& values, const std::basic_string<CharT>& fieldName) const
+ {
+ return SubscriptKvPair(values, ConvertString<std::string>(fieldName));
+ }
+
+ template<typename CharT>
+ InternalValue operator()(const KeyValuePair& values, const std::basic_string_view<CharT>& fieldName) const
+ {
+ return SubscriptKvPair(values, ConvertString<std::string>(fieldName));
+ }
+
+ InternalValue SubscriptKvPair(const KeyValuePair& values, const std::string& field) const
+ {
+ // std::cout << "operator() (const KeyValuePair& values, const std::string& field)" << ": field = " << field << std::endl;
+ if (field == "key")
+ return InternalValue(values.key);
+ else if (field == "value")
+ return values.value;
+
+ return InternalValue();
+ }
+};
+
+InternalValue Subscript(const InternalValue& val, const InternalValue& subscript, RenderContext* values)
+{
+ static const std::string callOperName = "value()";
+ auto result = Apply2<SubscriptionVisitor>(val, subscript);
+
+ if (!values)
+ return result;
+
+ auto map = GetIf<MapAdapter>(&result);
+ if (!map || !map->HasValue(callOperName))
+ return result;
+
+ auto callableVal = map->GetValueByName(callOperName);
+ auto callable = GetIf<Callable>(&callableVal);
+ if (!callable || callable->GetKind() == Callable::Macro || callable->GetType() == Callable::Type::Statement)
+ return result;
+
+ CallParams callParams;
+ return callable->GetExpressionCallable()(callParams, *values);
+}
+
+InternalValue Subscript(const InternalValue& val, const std::string& subscript, RenderContext* values)
+{
+ return Subscript(val, InternalValue(subscript), values);
+}
+
+struct StringGetter : public visitors::BaseVisitor<std::string>
+{
+ using BaseVisitor::operator();
+
+ std::string operator()(const std::string& str) const { return str; }
+ std::string operator()(const std::string_view& str) const { return std::string(str.begin(), str.end()); }
+ std::string operator()(const std::wstring& str) const { return ConvertString<std::string>(str); }
+ std::string operator()(const std::wstring_view& str) const { return ConvertString<std::string>(str); }
+};
+
+std::string AsString(const InternalValue& val)
+{
+ return Apply<StringGetter>(val);
+}
+
+struct ListConverter : public visitors::BaseVisitor<boost::optional<ListAdapter>>
+{
+ using BaseVisitor::operator();
+
+ using result_t = boost::optional<ListAdapter>;
+
+ bool strictConvertion;
+
+ ListConverter(bool strict)
+ : strictConvertion(strict)
+ {
+ }
+
+ result_t operator()(const ListAdapter& list) const { return list; }
+ result_t operator()(const MapAdapter& map) const
+ {
+ if (strictConvertion)
+ return result_t();
+
+ InternalValueList list;
+ for (auto& k : map.GetKeys())
+ list.push_back(TargetString(k));
+
+ return ListAdapter::CreateAdapter(std::move(list));
+ }
+
+ template<typename CharT>
+ result_t operator() (const std::basic_string<CharT>& str) const
+ {
+ return strictConvertion ? result_t() : result_t(ListAdapter::CreateAdapter(str.size(), [str](size_t idx) {
+ return TargetString(str.substr(idx, 1));}));
+ }
+
+ template<typename CharT>
+ result_t operator()(const std::basic_string_view<CharT>& str) const
+ {
+ return strictConvertion ? result_t() : result_t(ListAdapter::CreateAdapter(str.size(), [str](size_t idx) {
+ return TargetString(std::basic_string<CharT>(str[idx], 1)); }));
+ }
+};
+
+ListAdapter ConvertToList(const InternalValue& val, bool& isConverted, bool strictConversion)
+{
+ auto result = Apply<ListConverter>(val, strictConversion);
+ if (!result)
+ {
+ isConverted = false;
+ return ListAdapter();
+ }
+ isConverted = true;
+ return result.get();
+}
+
+ListAdapter ConvertToList(const InternalValue& val, InternalValue subscipt, bool& isConverted, bool strictConversion)
+{
+ auto result = Apply<ListConverter>(val, strictConversion);
+ if (!result)
+ {
+ isConverted = false;
+ return ListAdapter();
+ }
+ isConverted = true;
+
+ if (IsEmpty(subscipt))
+ return std::move(result.get());
+
+ return result.get().ToSubscriptedList(subscipt, false);
+}
+
+template<typename T>
+class ByRef
+{
+public:
+ explicit ByRef(const T& val)
+ : m_val(&val)
+ {
+ }
+
+ const T& Get() const { return *m_val; }
+ T& Get() { return *const_cast<T*>(m_val); }
+ bool ShouldExtendLifetime() const { return false; }
+ bool operator==(const ByRef<T>& other) const
+ {
+ if (m_val && other.m_val && m_val != other.m_val)
+ return false;
+ if ((m_val && !other.m_val) || (!m_val && other.m_val))
+ return false;
+ return true;
+ }
+ bool operator!=(const ByRef<T>& other) const
+ {
+ return !(*this == other);
+ }
+private:
+ const T* m_val{};
+};
+
+template<typename T>
+class ByVal
+{
+public:
+ explicit ByVal(T&& val)
+ : m_val(std::move(val))
+ {
+ }
+ ~ByVal() = default;
+
+ const T& Get() const { return m_val; }
+ T& Get() { return m_val; }
+ bool ShouldExtendLifetime() const { return false; }
+ bool operator==(const ByVal<T>& other) const
+ {
+ return m_val == other.m_val;
+ }
+ bool operator!=(const ByVal<T>& other) const
+ {
+ return !(*this == other);
+ }
+private:
+ T m_val;
+};
+
+template<typename T>
+class BySharedVal
+{
+public:
+ explicit BySharedVal(T&& val)
+ : m_val(std::make_shared<T>(std::move(val)))
+ {
+ }
+ ~BySharedVal() = default;
+
+ const T& Get() const { return *m_val; }
+ T& Get() { return *m_val; }
+ bool ShouldExtendLifetime() const { return true; }
+
+ bool operator==(const BySharedVal<T>& other) const
+ {
+ return m_val == other.m_val;
+ }
+ bool operator!=(const BySharedVal<T>& other) const
+ {
+ return !(*this == other);
+ }
+private:
+ std::shared_ptr<T> m_val;
+};
+
+template<template<typename> class Holder>
+class GenericListAdapter : public IListAccessor
+{
+public:
+ struct Enumerator : public IListAccessorEnumerator
+ {
+ ListEnumeratorPtr m_enum;
+
+ explicit Enumerator(ListEnumeratorPtr e)
+ : m_enum(std::move(e))
+ {
+ }
+
+ // Inherited via IListAccessorEnumerator
+ void Reset() override
+ {
+ if (m_enum)
+ m_enum->Reset();
+ }
+ bool MoveNext() override { return !m_enum ? false : m_enum->MoveNext(); }
+ InternalValue GetCurrent() const override { return !m_enum ? InternalValue() : Value2IntValue(m_enum->GetCurrent()); }
+ IListAccessorEnumerator* Clone() const override { return !m_enum ? new Enumerator(MakeEmptyListEnumeratorPtr()) : new Enumerator(m_enum->Clone()); }
+ IListAccessorEnumerator* Transfer() override { return new Enumerator(std::move(m_enum)); }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const Enumerator*>(&other);
+ if (!val)
+ return false;
+ if (m_enum && val->m_enum && !m_enum->IsEqual(*val->m_enum))
+ return false;
+ if ((m_enum && !val->m_enum) || (!m_enum && val->m_enum))
+ return false;
+ return true;
+ }
+ };
+
+ template<typename U>
+ GenericListAdapter(U&& values)
+ : m_values(std::forward<U>(values))
+ {
+ }
+
+ std::optional<size_t> GetSize() const override { return m_values.Get().GetSize(); }
+ std::optional<InternalValue> GetItem(int64_t idx) const override
+ {
+ const IListItemAccessor* accessor = m_values.Get().GetAccessor();
+ auto indexer = accessor->GetIndexer();
+ if (!indexer)
+ return std::optional<InternalValue>();
+
+ auto val = indexer->GetItemByIndex(idx);
+ return visit(visitors::InputValueConvertor(true, false), std::move(val.data())).get();
+ }
+ bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); }
+ ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override
+ {
+ const IListItemAccessor* accessor = m_values.Get().GetAccessor();
+ if (!accessor)
+ return ListAccessorEnumeratorPtr(new Enumerator(MakeEmptyListEnumeratorPtr()));
+ return ListAccessorEnumeratorPtr(new Enumerator(m_values.Get().GetAccessor()->CreateEnumerator()));
+ }
+ GenericList CreateGenericList() const override
+ {
+ // return m_values.Get();
+ return GenericList([list = m_values]() -> const IListItemAccessor* { return list.Get().GetAccessor(); });
+ }
+
+private:
+ Holder<GenericList> m_values;
+};
+
+template<template<typename> class Holder>
+class ValuesListAdapter : public IndexedListAccessorImpl<ValuesListAdapter<Holder>>
+{
+public:
+ template<typename U>
+ ValuesListAdapter(U&& values)
+ : m_values(std::forward<U>(values))
+ {
+ }
+
+ size_t GetItemsCountImpl() const { return m_values.Get().size(); }
+ std::optional<InternalValue> GetItem(int64_t idx) const override
+ {
+ const auto& val = m_values.Get()[static_cast<size_t>(idx)];
+ return visit(visitors::InputValueConvertor(false, true), val.data()).get();
+ }
+ bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); }
+ GenericList CreateGenericList() const override
+ {
+ // return m_values.Get();
+ return GenericList([list = *this]() -> const IListItemAccessor* { return &list; });
+ }
+
+private:
+ Holder<ValuesList> m_values;
+};
+
+ListAdapter ListAdapter::CreateAdapter(InternalValueList&& values)
+{
+ class Adapter : public IndexedListAccessorImpl<Adapter>
+ {
+ public:
+ explicit Adapter(InternalValueList&& values)
+ : m_values(std::move(values))
+ {
+ }
+
+ size_t GetItemsCountImpl() const { return m_values.size(); }
+ std::optional<InternalValue> GetItem(int64_t idx) const override { return m_values[static_cast<size_t>(idx)]; }
+ bool ShouldExtendLifetime() const override { return false; }
+ GenericList CreateGenericList() const override
+ {
+ return GenericList([adapter = *this]() -> const IListItemAccessor* { return &adapter; });
+ }
+
+ private:
+ InternalValueList m_values;
+ };
+
+ return ListAdapter([accessor = Adapter(std::move(values))]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(const GenericList& values)
+{
+ return ListAdapter([accessor = GenericListAdapter<ByRef>(values)]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(const ValuesList& values)
+{
+ return ListAdapter([accessor = ValuesListAdapter<ByRef>(values)]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(GenericList&& values)
+{
+ return ListAdapter([accessor = GenericListAdapter<BySharedVal>(std::move(values))]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(ValuesList&& values)
+{
+ return ListAdapter([accessor = ValuesListAdapter<BySharedVal>(std::move(values))]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(std::function<std::optional<InternalValue>()> fn)
+{
+ using GenFn = std::function<std::optional<InternalValue>()>;
+
+ class Adapter : public IListAccessor
+ {
+ public:
+ class Enumerator : public IListAccessorEnumerator
+ {
+ public:
+ explicit Enumerator(const GenFn* fn)
+ : m_fn(fn)
+ {
+ if (!fn)
+ throw std::runtime_error("List enumerator couldn't be created without element accessor function!");
+ }
+
+ void Reset() override {}
+
+ bool MoveNext() override
+ {
+ if (m_isFinished)
+ return false;
+
+ auto res = (*m_fn)();
+ if (!res)
+ return false;
+
+ m_current = *res;
+
+ return true;
+ }
+
+ InternalValue GetCurrent() const override { return m_current; }
+
+ IListAccessorEnumerator* Clone() const override
+ {
+ auto result = new Enumerator(*this);
+ return result;
+ }
+
+ IListAccessorEnumerator* Transfer() override
+ {
+ auto result = new Enumerator(std::move(*this));
+ return result;
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const Enumerator*>(&other);
+ if (!val)
+ return false;
+ if (m_isFinished != val->m_isFinished)
+ return false;
+ if (m_current != val->m_current)
+ return false;
+ // TODO: compare fn?
+ if (m_fn != val->m_fn)
+ return false;
+ return true;
+ }
+
+ protected:
+ const GenFn* m_fn{};
+ InternalValue m_current;
+ bool m_isFinished = false;
+ };
+
+ explicit Adapter(std::function<std::optional<InternalValue>()>&& fn)
+ : m_fn(std::move(fn))
+ {
+ }
+
+ std::optional<size_t> GetSize() const override { return std::optional<size_t>(); }
+ std::optional<InternalValue> GetItem(int64_t /*idx*/) const override { return std::optional<InternalValue>(); }
+ bool ShouldExtendLifetime() const override { return false; }
+ ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override { return ListAccessorEnumeratorPtr(new Enumerator(&m_fn)); }
+
+ GenericList CreateGenericList() const override
+ {
+ return GenericList(); // return GenericList([adapter = *this]() -> const ListItemAccessor* {return &adapter; });
+ }
+
+ private:
+ std::function<std::optional<InternalValue>()> m_fn;
+ };
+
+ return ListAdapter([accessor = Adapter(std::move(fn))]() { return &accessor; });
+}
+
+ListAdapter ListAdapter::CreateAdapter(size_t listSize, std::function<InternalValue(size_t idx)> fn)
+{
+ using GenFn = std::function<InternalValue(size_t idx)>;
+
+ class Adapter : public IndexedListAccessorImpl<Adapter>
+ {
+ public:
+ explicit Adapter(size_t listSize, GenFn&& fn)
+ : m_listSize(listSize)
+ , m_fn(std::move(fn))
+ {
+ }
+
+ size_t GetItemsCountImpl() const { return m_listSize; }
+ std::optional<InternalValue> GetItem(int64_t idx) const override { return m_fn(static_cast<size_t>(idx)); }
+ bool ShouldExtendLifetime() const override { return false; }
+ GenericList CreateGenericList() const override
+ {
+ return GenericList([adapter = *this]() -> const IListItemAccessor* { return &adapter; });
+ }
+
+ private:
+ size_t m_listSize;
+ GenFn m_fn;
+ };
+
+ return ListAdapter([accessor = Adapter(listSize, std::move(fn))]() { return &accessor; });
+}
+
+template<typename Holder>
+auto CreateIndexedSubscribedList(Holder&& holder, const InternalValue& subscript, size_t size)
+{
+ return ListAdapter::CreateAdapter(
+ size, [h = std::forward<Holder>(holder), subscript](size_t idx) -> InternalValue { return Subscript(h.Get().GetValueByIndex(idx), subscript, nullptr); });
+}
+
+template<typename Holder>
+auto CreateGenericSubscribedList(Holder&& holder, const InternalValue& subscript)
+{
+ return ListAdapter::CreateAdapter([h = std::forward<Holder>(holder), e = ListAccessorEnumeratorPtr(), isFirst = true, isLast = false, subscript]() mutable {
+ using ResultType = std::optional<InternalValue>;
+ if (isFirst)
+ {
+ e = h.Get().GetEnumerator();
+ isLast = !e->MoveNext();
+ isFirst = false;
+ }
+ if (isLast)
+ return ResultType();
+
+ return ResultType(Subscript(e->GetCurrent(), subscript, nullptr));
+ });
+}
+
+ListAdapter ListAdapter::ToSubscriptedList(const InternalValue& subscript, bool asRef) const
+{
+ auto listSize = GetSize();
+ if (asRef)
+ {
+ ByRef<ListAdapter> holder(*this);
+ return listSize ? CreateIndexedSubscribedList(holder, subscript, *listSize) : CreateGenericSubscribedList(holder, subscript);
+ }
+ else
+ {
+ ListAdapter tmp(*this);
+ BySharedVal<ListAdapter> holder(std::move(tmp));
+ return listSize ? CreateIndexedSubscribedList(std::move(holder), subscript, *listSize) : CreateGenericSubscribedList(std::move(holder), subscript);
+ }
+}
+
+InternalValueList ListAdapter::ToValueList() const
+{
+ InternalValueList result;
+ std::copy(begin(), end(), std::back_inserter(result));
+ return result;
+}
+
+template<template<typename> class Holder, bool CanModify>
+class InternalValueMapAdapter : public MapAccessorImpl<InternalValueMapAdapter<Holder, CanModify>>
+{
+public:
+ template<typename U>
+ InternalValueMapAdapter(U&& values)
+ : m_values(std::forward<U>(values))
+ {
+ }
+
+ size_t GetSize() const override { return m_values.Get().size(); }
+ bool HasValue(const std::string& name) const override { return m_values.Get().count(name) != 0; }
+ InternalValue GetItem(const std::string& name) const override
+ {
+ auto& vals = m_values.Get();
+ auto p = vals.find(name);
+ if (p == vals.end())
+ return InternalValue();
+
+ return p->second;
+ }
+ std::vector<std::string> GetKeys() const override
+ {
+ std::vector<std::string> result;
+
+ for (auto& i : m_values.Get())
+ result.push_back(i.first);
+
+ return result;
+ }
+
+ bool SetValue(std::string name, const InternalValue& val) override
+ {
+ if (CanModify)
+ {
+ m_values.Get()[name] = val;
+ return true;
+ }
+ return false;
+ }
+ bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); }
+ GenericMap CreateGenericMap() const override
+ {
+ return GenericMap([accessor = *this]() -> const IMapItemAccessor* { return &accessor; });
+ }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const InternalValueMapAdapter*>(&other);
+ if (!val)
+ return false;
+ return m_values == val->m_values;
+ }
+private:
+ Holder<InternalValueMap> m_values;
+};
+
+InternalValue Value2IntValue(const Value& val)
+{
+ auto result = std::visit(visitors::InputValueConvertor(false, true), val.data());
+ if (result)
+ return result.get();
+
+ return InternalValue(ValueRef(val));
+}
+
+InternalValue Value2IntValue(Value&& val)
+{
+ auto result = std::visit(visitors::InputValueConvertor(true, false), val.data());
+ if (result)
+ return result.get();
+
+ return InternalValue(ValueRef(val));
+}
+
+template<template<typename> class Holder>
+class GenericMapAdapter : public MapAccessorImpl<GenericMapAdapter<Holder>>
+{
+public:
+ template<typename U>
+ GenericMapAdapter(U&& values)
+ : m_values(std::forward<U>(values))
+ {
+ }
+
+ size_t GetSize() const override { return m_values.Get().GetSize(); }
+ bool HasValue(const std::string& name) const override { return m_values.Get().HasValue(name); }
+ InternalValue GetItem(const std::string& name) const override
+ {
+ auto val = m_values.Get().GetValueByName(name);
+ if (val.isEmpty())
+ return InternalValue();
+
+ return Value2IntValue(std::move(val));
+ }
+ std::vector<std::string> GetKeys() const override { return m_values.Get().GetKeys(); }
+ bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); }
+ GenericMap CreateGenericMap() const override
+ {
+ return GenericMap([accessor = *this]() -> const IMapItemAccessor* { return accessor.m_values.Get().GetAccessor(); });
+ }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const GenericMapAdapter*>(&other);
+ if (!val)
+ return false;
+ return m_values == val->m_values;
+ }
+private:
+ Holder<GenericMap> m_values;
+};
+
+template<template<typename> class Holder>
+class ValuesMapAdapter : public MapAccessorImpl<ValuesMapAdapter<Holder>>
+{
+public:
+ template<typename U>
+ ValuesMapAdapter(U&& values)
+ : m_values(std::forward<U>(values))
+ {
+ }
+
+ size_t GetSize() const override { return m_values.Get().size(); }
+ bool HasValue(const std::string& name) const override { return m_values.Get().count(name) != 0; }
+ InternalValue GetItem(const std::string& name) const override
+ {
+ auto& vals = m_values.Get();
+ auto p = vals.find(name);
+ if (p == vals.end())
+ return InternalValue();
+
+ return Value2IntValue(p->second);
+ }
+ std::vector<std::string> GetKeys() const override
+ {
+ std::vector<std::string> result;
+
+ for (auto& i : m_values.Get())
+ result.push_back(i.first);
+
+ return result;
+ }
+ bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); }
+ GenericMap CreateGenericMap() const override
+ {
+ return GenericMap([accessor = *this]() -> const IMapItemAccessor* { return &accessor; });
+ }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ValuesMapAdapter*>(&other);
+ if (!val)
+ return false;
+ return m_values == val->m_values;
+ }
+private:
+ Holder<ValuesMap> m_values;
+};
+
+MapAdapter CreateMapAdapter(InternalValueMap&& values)
+{
+ return MapAdapter([accessor = InternalValueMapAdapter<ByVal, true>(std::move(values))]() mutable { return &accessor; });
+}
+
+MapAdapter CreateMapAdapter(const InternalValueMap* values)
+{
+ return MapAdapter([accessor = InternalValueMapAdapter<ByRef, false>(*values)]() mutable { return &accessor; });
+}
+
+MapAdapter CreateMapAdapter(const GenericMap& values)
+{
+ return MapAdapter([accessor = GenericMapAdapter<ByRef>(values)]() mutable { return &accessor; });
+}
+
+MapAdapter CreateMapAdapter(GenericMap&& values)
+{
+ return MapAdapter([accessor = GenericMapAdapter<BySharedVal>(std::move(values))]() mutable { return &accessor; });
+}
+
+MapAdapter CreateMapAdapter(const ValuesMap& values)
+{
+ return MapAdapter([accessor = ValuesMapAdapter<ByRef>(values)]() mutable { return &accessor; });
+}
+
+MapAdapter CreateMapAdapter(ValuesMap&& values)
+{
+ return MapAdapter([accessor = ValuesMapAdapter<BySharedVal>(std::move(values))]() mutable { return &accessor; });
+}
+
+struct OutputValueConvertor
+{
+ using result_t = Value;
+
+ result_t operator()(const EmptyValue&) const { return result_t(); }
+ result_t operator()(const MapAdapter& adapter) const { return result_t(adapter.CreateGenericMap()); }
+ result_t operator()(const ListAdapter& adapter) const { return result_t(adapter.CreateGenericList()); }
+ result_t operator()(const ValueRef& ref) const { return ref.get(); }
+ result_t operator()(const TargetString& str) const
+ {
+ switch (str.index())
+ {
+ case 0:
+ return std::get<std::string>(str);
+ default:
+ return std::get<std::wstring>(str);
+ }
+ }
+ result_t operator()(const TargetStringView& str) const
+ {
+ switch (str.index())
+ {
+ case 0:
+ return std::get<std::string_view>(str);
+ default:
+ return std::get<std::wstring_view>(str);
+ }
+ }
+ result_t operator()(const KeyValuePair& pair) const { return ValuesMap{ { "key", Value(pair.key) }, { "value", IntValue2Value(pair.value) } }; }
+ result_t operator()(const Callable&) const { return result_t(); }
+ result_t operator()(const UserCallable&) const { return result_t(); }
+ result_t operator()(const std::shared_ptr<IRendererBase>&) const { return result_t(); }
+
+ template<typename T>
+ result_t operator()(const RecWrapper<T>& val) const
+ {
+ return this->operator()(const_cast<const T&>(*val));
+ }
+
+ template<typename T>
+ result_t operator()(RecWrapper<T>& val) const
+ {
+ return this->operator()(*val);
+ }
+
+ template<typename T>
+ result_t operator()(T&& val) const
+ {
+ return result_t(std::forward<T>(val));
+ }
+
+ bool m_byValue;
+};
+
+Value OptIntValue2Value(std::optional<InternalValue> val)
+{
+ if (val)
+ return Apply<OutputValueConvertor>(val.value());
+
+ return Value();
+}
+
+Value IntValue2Value(const InternalValue& val)
+{
+ return Apply<OutputValueConvertor>(val);
+}
+
+class ContextMapper : public IMapItemAccessor
+{
+public:
+ explicit ContextMapper(RenderContext* context)
+ : m_context(context)
+ {
+ }
+
+ size_t GetSize() const override { return std::numeric_limits<size_t>::max(); }
+ bool HasValue(const std::string& name) const override
+ {
+ bool found = false;
+ m_context->FindValue(name, found);
+ return found;
+ }
+ Value GetValueByName(const std::string& name) const override
+ {
+ bool found = false;
+ auto p = m_context->FindValue(name, found);
+ return found ? IntValue2Value(p->second) : Value();
+ }
+ std::vector<std::string> GetKeys() const override { return std::vector<std::string>(); }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ContextMapper*>(&other);
+ if (!val)
+ return false;
+ if (m_context && val->m_context && !m_context->IsEqual(*val->m_context))
+ {
+ return false;
+ }
+ if ((m_context && !val->m_context) || (!m_context && val->m_context))
+ return false;
+ return true;
+ }
+
+private:
+ RenderContext* m_context;
+};
+
+UserCallableParams PrepareUserCallableParams(const CallParams& params, RenderContext& context, const std::vector<ArgumentInfo>& argsInfo)
+{
+ UserCallableParams result;
+
+ ParsedArguments args = helpers::ParseCallParams(argsInfo, params, result.paramsParsed);
+ if (!result.paramsParsed)
+ return result;
+
+ for (auto& argInfo : argsInfo)
+ {
+ if (argInfo.name.size() > 1 && argInfo.name[0] == '*')
+ continue;
+
+ auto p = args.args.find(argInfo.name);
+ if (p == args.args.end())
+ {
+ result.args[argInfo.name] = IntValue2Value(argInfo.defaultVal);
+ continue;
+ }
+
+ const auto& v = p->second;
+ result.args[argInfo.name] = IntValue2Value(v);
+ }
+
+ ValuesMap extraKwArgs;
+ for (auto& p : args.extraKwArgs)
+ extraKwArgs[p.first] = IntValue2Value(p.second);
+ result.extraKwArgs = Value(std::move(extraKwArgs));
+
+ ValuesList extraPosArgs;
+ for (auto& p : args.extraPosArgs)
+ extraPosArgs.push_back(IntValue2Value(p));
+ result.extraPosArgs = Value(std::move(extraPosArgs));
+ result.context = GenericMap([accessor = ContextMapper(&context)]() -> const IMapItemAccessor* { return &accessor; });
+
+ return result;
+}
+
+namespace visitors
+{
+
+InputValueConvertor::result_t InputValueConvertor::ConvertUserCallable(const UserCallable& val)
+{
+ std::vector<ArgumentInfo> args;
+ for (auto& pi : val.argsInfo)
+ {
+ args.emplace_back(pi.paramName, pi.isMandatory, Value2IntValue(pi.defValue));
+ }
+
+ return InternalValue(Callable(Callable::UserCallable, [val, argsInfo = std::move(args)](const CallParams& params, RenderContext& context) -> InternalValue {
+ auto ucParams = PrepareUserCallableParams(params, context, argsInfo);
+ return Value2IntValue(val.callable(ucParams));
+ }));
+}
+
+} // namespace visitors
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/internal_value.h b/contrib/libs/jinja2cpp/src/internal_value.h
new file mode 100644
index 0000000000..22a933f257
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/internal_value.h
@@ -0,0 +1,673 @@
+#ifndef JINJA2CPP_SRC_INTERNAL_VALUE_H
+#define JINJA2CPP_SRC_INTERNAL_VALUE_H
+
+#include <jinja2cpp/value.h>
+//#include <jinja2cpp/value_ptr.h>
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/variant/recursive_wrapper.hpp>
+#include <boost/unordered_map.hpp>
+
+#include <fmt/core.h>
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900 // robin_hood hash map doesn't compatible with MSVC 14.0
+#include <unordered_map>
+#else
+#include "robin_hood.h"
+#endif
+
+
+#include <string_view>
+#include <variant>
+
+#include <functional>
+
+namespace jinja2
+{
+
+template <class T>
+class ReferenceWrapper
+{
+public:
+ using type = T;
+
+ ReferenceWrapper(T& ref) noexcept
+ : m_ptr(std::addressof(ref))
+ {
+ }
+
+ ReferenceWrapper(T&&) = delete;
+ ReferenceWrapper(const ReferenceWrapper&) noexcept = default;
+
+ // assignment
+ ReferenceWrapper& operator=(const ReferenceWrapper& x) noexcept = default;
+
+ // access
+ T& get() const noexcept
+ {
+ return *m_ptr;
+ }
+
+private:
+ T* m_ptr;
+};
+
+template<typename T, size_t SizeHint = 48>
+class RecursiveWrapper
+{
+public:
+ RecursiveWrapper() = default;
+
+ RecursiveWrapper(const T& value)
+ : m_data(value)
+ {}
+
+ RecursiveWrapper(T&& value)
+ : m_data(std::move(value))
+ {}
+
+ const T& GetValue() const {return m_data.get();}
+ T& GetValue() {return m_data.get();}
+
+private:
+ boost::recursive_wrapper<T> m_data;
+
+#if 0
+ enum class State
+ {
+ Undefined,
+ Inplace,
+ Ptr
+ };
+
+ State m_state;
+
+ union
+ {
+ uint64_t dummy;
+ nonstd::value_ptr<T> ptr;
+ } m_data;
+#endif
+};
+
+template<typename T>
+auto MakeWrapped(T&& val)
+{
+ return RecursiveWrapper<std::decay_t<T>>(std::forward<T>(val));
+}
+
+using ValueRef = ReferenceWrapper<const Value>;
+using TargetString = std::variant<std::string, std::wstring>;
+using TargetStringView = std::variant<std::string_view, std::wstring_view>;
+
+class ListAdapter;
+class MapAdapter;
+class RenderContext;
+class OutStream;
+class Callable;
+struct CallParams;
+struct KeyValuePair;
+class IRendererBase;
+
+class InternalValue;
+using InternalValueData = std::variant<
+ EmptyValue,
+ bool,
+ std::string,
+ TargetString,
+ TargetStringView,
+ int64_t,
+ double,
+ ValueRef,
+ ListAdapter,
+ MapAdapter,
+ RecursiveWrapper<KeyValuePair>,
+ RecursiveWrapper<Callable>,
+ std::shared_ptr<IRendererBase>>;
+
+
+using InternalValueRef = ReferenceWrapper<InternalValue>;
+using InternalValueList = std::vector<InternalValue>;
+
+template<typename T, bool isRecursive = false>
+struct ValueGetter
+{
+ template<typename V>
+ static auto& Get(V&& val)
+ {
+ return std::get<T>(std::forward<V>(val).GetData());
+ }
+
+ static auto GetPtr(const InternalValue* val);
+
+ static auto GetPtr(InternalValue* val);
+
+ template<typename V>
+ static auto GetPtr(V* val, std::enable_if_t<!std::is_same<V, InternalValue>::value>* = nullptr)
+ {
+ return std::get_if<T>(val);
+ }
+};
+
+template<typename T>
+struct ValueGetter<T, true>
+{
+ template<typename V>
+ static auto& Get(V&& val)
+ {
+ auto& ref = std::get<RecursiveWrapper<T>>(std::forward<V>(val));
+ return ref.GetValue();
+ }
+
+ static auto GetPtr(const InternalValue* val);
+
+ static auto GetPtr(InternalValue* val);
+
+ template<typename V>
+ static auto GetPtr(V* val, std::enable_if_t<!std::is_same<V, InternalValue>::value>* = nullptr)
+ {
+ auto ref = std::get_if<RecursiveWrapper<T>>(val);
+ return !ref ? nullptr : &ref->GetValue();
+ }
+};
+
+template<typename T>
+struct IsRecursive : std::false_type {};
+
+template<>
+struct IsRecursive<KeyValuePair> : std::true_type {};
+
+template<>
+struct IsRecursive<Callable> : std::true_type {};
+
+struct IListAccessorEnumerator : virtual IComparable
+{
+ virtual ~IListAccessorEnumerator() {}
+
+ virtual void Reset() = 0;
+
+ virtual bool MoveNext() = 0;
+ virtual InternalValue GetCurrent() const = 0;
+
+ virtual IListAccessorEnumerator* Clone() const = 0;
+ virtual IListAccessorEnumerator* Transfer() = 0;
+/*
+ struct Cloner
+ {
+ Cloner() = default;
+
+ IListAccessorEnumerator* operator()(const IListAccessorEnumerator &x) const
+ {
+ return x.Clone();
+ }
+
+ IListAccessorEnumerator* operator()(IListAccessorEnumerator &&x) const
+ {
+ return x.Transfer();
+ }
+ };
+ */
+};
+
+using ListAccessorEnumeratorPtr = types::ValuePtr<IListAccessorEnumerator>;
+
+struct IListAccessor
+{
+ virtual ~IListAccessor() {}
+
+ virtual std::optional<size_t> GetSize() const = 0;
+ virtual std::optional<InternalValue> GetItem(int64_t idx) const = 0;
+ virtual ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const = 0;
+ virtual GenericList CreateGenericList() const = 0;
+ virtual bool ShouldExtendLifetime() const = 0;
+};
+
+
+using ListAccessorProvider = std::function<const IListAccessor*()>;
+
+struct IMapAccessor
+{
+ virtual ~IMapAccessor() = default;
+ virtual size_t GetSize() const = 0;
+ virtual bool HasValue(const std::string& name) const = 0;
+ virtual InternalValue GetItem(const std::string& name) const = 0;
+ virtual std::vector<std::string> GetKeys() const = 0;
+ virtual bool SetValue(std::string, const InternalValue&) {return false;}
+ virtual GenericMap CreateGenericMap() const = 0;
+ virtual bool ShouldExtendLifetime() const = 0;
+};
+
+using MapAccessorProvider = std::function<IMapAccessor*()>;
+
+class ListAdapter
+{
+public:
+ ListAdapter() {}
+ explicit ListAdapter(ListAccessorProvider prov) : m_accessorProvider(std::move(prov)) {}
+ ListAdapter(const ListAdapter&) = default;
+ ListAdapter(ListAdapter&&) = default;
+
+ static ListAdapter CreateAdapter(InternalValueList&& values);
+ static ListAdapter CreateAdapter(const GenericList& values);
+ static ListAdapter CreateAdapter(const ValuesList& values);
+ static ListAdapter CreateAdapter(GenericList&& values);
+ static ListAdapter CreateAdapter(ValuesList&& values);
+ static ListAdapter CreateAdapter(std::function<std::optional<InternalValue> ()> fn);
+ static ListAdapter CreateAdapter(size_t listSize, std::function<InternalValue (size_t idx)> fn);
+
+ ListAdapter& operator = (const ListAdapter&) = default;
+ ListAdapter& operator = (ListAdapter&&) = default;
+
+ std::optional<size_t> GetSize() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->GetSize();
+ }
+
+ return 0;
+ }
+ InternalValue GetValueByIndex(int64_t idx) const;
+ bool ShouldExtendLifetime() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->ShouldExtendLifetime();
+ }
+
+ return false;
+ }
+
+ ListAdapter ToSubscriptedList(const InternalValue& subscript, bool asRef = false) const;
+ InternalValueList ToValueList() const;
+ GenericList CreateGenericList() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ return m_accessorProvider()->CreateGenericList();
+
+ return GenericList();
+ }
+ ListAccessorEnumeratorPtr GetEnumerator() const;
+
+ class Iterator;
+
+ Iterator begin() const;
+ Iterator end() const;
+
+private:
+ ListAccessorProvider m_accessorProvider;
+};
+
+class MapAdapter
+{
+public:
+ MapAdapter() = default;
+ explicit MapAdapter(MapAccessorProvider prov) : m_accessorProvider(std::move(prov)) {}
+
+ size_t GetSize() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->GetSize();
+ }
+
+ return 0;
+ }
+ // InternalValue GetValueByIndex(int64_t idx) const;
+ bool HasValue(const std::string& name) const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->HasValue(name);
+ }
+
+ return false;
+ }
+ InternalValue GetValueByName(const std::string& name) const;
+ std::vector<std::string> GetKeys() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->GetKeys();
+ }
+
+ return std::vector<std::string>();
+ }
+ bool SetValue(std::string name, const InternalValue& val)
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->SetValue(std::move(name), val);
+ }
+
+ return false;
+ }
+ bool ShouldExtendLifetime() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->ShouldExtendLifetime();
+ }
+
+ return false;
+ }
+
+ GenericMap CreateGenericMap() const
+ {
+ if (m_accessorProvider && m_accessorProvider())
+ return m_accessorProvider()->CreateGenericMap();
+
+ return GenericMap();
+ }
+
+private:
+ MapAccessorProvider m_accessorProvider;
+};
+
+
+class InternalValue
+{
+public:
+ InternalValue() = default;
+
+ template<typename T>
+ InternalValue(T&& val, typename std::enable_if<!std::is_same<std::decay_t<T>, InternalValue>::value>::type* = nullptr)
+ : m_data(InternalValueData(std::forward<T>(val)))
+ {
+ }
+
+ auto& GetData() const {return m_data;}
+ auto& GetData() {return m_data;}
+
+ auto& GetParentData() {return m_parentData;}
+ auto& GetParentData() const {return m_parentData;}
+
+ void SetParentData(const InternalValue& val)
+ {
+ m_parentData = val.GetData();
+ }
+
+ void SetParentData(InternalValue&& val)
+ {
+ m_parentData = std::move(val.GetData());
+ }
+
+ bool ShouldExtendLifetime() const
+ {
+ if (m_parentData.index() != 0)
+ return true;
+
+ const MapAdapter* ma = std::get_if<MapAdapter>(&m_data);
+ if (ma != nullptr)
+ return ma->ShouldExtendLifetime();
+
+ const ListAdapter* la = std::get_if<ListAdapter>(&m_data);
+ if (la != nullptr)
+ return la->ShouldExtendLifetime();
+
+ return false;
+ }
+
+ bool IsEmpty() const {return m_data.index() == 0;}
+
+ bool IsEqual(const InternalValue& other) const;
+
+private:
+ InternalValueData m_data;
+ InternalValueData m_parentData;
+};
+
+inline bool operator==(const InternalValue& lhs, const InternalValue& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+inline bool operator!=(const InternalValue& lhs, const InternalValue& rhs)
+{
+ return !(lhs == rhs);
+}
+
+class ListAdapter::Iterator
+ : public boost::iterator_facade<
+ Iterator,
+ const InternalValue,
+ boost::forward_traversal_tag>
+{
+public:
+ Iterator() = default;
+
+ explicit Iterator(ListAccessorEnumeratorPtr&& iter)
+ : m_iterator(std::move(iter))
+ , m_isFinished(!m_iterator->MoveNext())
+ , m_currentVal(m_isFinished ? InternalValue() : m_iterator->GetCurrent())
+ {}
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ m_isFinished = !m_iterator->MoveNext();
+ ++ m_currentIndex;
+ m_currentVal = m_isFinished ? InternalValue() : m_iterator->GetCurrent();
+ }
+
+ bool equal(const Iterator& other) const
+ {
+ if (!this->m_iterator)
+ return !other.m_iterator ? true : other.equal(*this);
+
+ if (!other.m_iterator)
+ return this->m_isFinished;
+// return true;
+ //const InternalValue& lhs = *(this->m_iterator);
+ //const InternalValue& rhs = *(other.m_iterator);
+ //return lhs == rhs;
+ return this->m_iterator->GetCurrent() == other.m_iterator->GetCurrent() && this->m_currentIndex == other.m_currentIndex;
+ ///return *(this->m_iterator) == *(other.m_iterator) && this->m_currentIndex == other.m_currentIndex;
+ }
+
+ const InternalValue& dereference() const
+ {
+ return m_currentVal;
+ }
+
+ ListAccessorEnumeratorPtr m_iterator;
+ bool m_isFinished = true;
+ mutable uint64_t m_currentIndex = 0;
+ mutable InternalValue m_currentVal;
+};
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900 // robin_hood hash map doesn't compatible with MSVC 14.0
+typedef std::unordered_map<std::string, InternalValue> InternalValueMap;
+#else
+typedef robin_hood::unordered_map<std::string, InternalValue> InternalValueMap;
+#endif
+
+
+MapAdapter CreateMapAdapter(InternalValueMap&& values);
+MapAdapter CreateMapAdapter(const InternalValueMap* values);
+MapAdapter CreateMapAdapter(const GenericMap& values);
+MapAdapter CreateMapAdapter(GenericMap&& values);
+MapAdapter CreateMapAdapter(const ValuesMap& values);
+MapAdapter CreateMapAdapter(ValuesMap&& values);
+
+template<typename T, bool V>
+inline auto ValueGetter<T, V>::GetPtr(const InternalValue* val)
+{
+ return std::get_if<T>(&val->GetData());
+}
+
+template<typename T, bool V>
+inline auto ValueGetter<T, V>::GetPtr(InternalValue* val)
+{
+ return std::get_if<T>(&val->GetData());
+}
+
+template<typename T>
+inline auto ValueGetter<T, true>::GetPtr(const InternalValue* val)
+{
+ auto ref = std::get_if<RecursiveWrapper<T>>(&val->GetData());
+ return !ref ? nullptr : &ref->GetValue();
+}
+
+template<typename T>
+inline auto ValueGetter<T, true>::GetPtr(InternalValue* val)
+{
+ auto ref = std::get_if<RecursiveWrapper<T>>(&val->GetData());
+ return !ref ? nullptr : &ref->GetValue();
+}
+
+template<typename T, typename V>
+auto& Get(V&& val)
+{
+ return ValueGetter<T, IsRecursive<T>::value>::Get(std::forward<V>(val).GetData());
+}
+
+template<typename T, typename V>
+auto GetIf(V* val)
+{
+ return ValueGetter<T, IsRecursive<T>::value>::GetPtr(val);
+}
+
+
+inline InternalValue ListAdapter::GetValueByIndex(int64_t idx) const
+{
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ const auto& val = m_accessorProvider()->GetItem(idx);
+ if (val)
+ return std::move(val.value());
+
+ return InternalValue();
+ }
+
+ return InternalValue();
+}
+
+//inline InternalValue MapAdapter::GetValueByIndex(int64_t idx) const
+//{
+// if (m_accessorProvider && m_accessorProvider())
+// {
+// return static_cast<const IListAccessor*>(m_accessorProvider())->GetItem(idx);
+// }
+
+// return InternalValue();
+//}
+
+inline InternalValue MapAdapter::GetValueByName(const std::string& name) const
+{
+ if (m_accessorProvider && m_accessorProvider())
+ {
+ return m_accessorProvider()->GetItem(name);
+ }
+
+ return InternalValue();
+}
+
+inline ListAccessorEnumeratorPtr ListAdapter::GetEnumerator() const {return m_accessorProvider()->CreateListAccessorEnumerator();}
+inline ListAdapter::Iterator ListAdapter::begin() const {return Iterator(m_accessorProvider()->CreateListAccessorEnumerator());}
+inline ListAdapter::Iterator ListAdapter::end() const {return Iterator();}
+
+
+struct KeyValuePair
+{
+ std::string key;
+ InternalValue value;
+};
+
+
+class Callable
+{
+public:
+ enum Kind
+ {
+ GlobalFunc,
+ SpecialFunc,
+ Macro,
+ UserCallable
+ };
+ using ExpressionCallable = std::function<InternalValue (const CallParams&, RenderContext&)>;
+ using StatementCallable = std::function<void (const CallParams&, OutStream&, RenderContext&)>;
+
+ using CallableHolder = std::variant<ExpressionCallable, StatementCallable>;
+
+ enum class Type
+ {
+ Expression,
+ Statement
+ };
+
+ Callable(Kind kind, ExpressionCallable&& callable)
+ : m_kind(kind)
+ , m_callable(std::move(callable))
+ {
+ }
+
+ Callable(Kind kind, StatementCallable&& callable)
+ : m_kind(kind)
+ , m_callable(std::move(callable))
+ {
+ }
+
+ auto GetType() const
+ {
+ return m_callable.index() == 0 ? Type::Expression : Type::Statement;
+ }
+
+ auto GetKind() const
+ {
+ return m_kind;
+ }
+
+ auto& GetCallable() const
+ {
+ return m_callable;
+ }
+
+ auto& GetExpressionCallable() const
+ {
+ return std::get<ExpressionCallable>(m_callable);
+ }
+
+ auto& GetStatementCallable() const
+ {
+ return std::get<StatementCallable>(m_callable);
+ }
+
+private:
+ Kind m_kind;
+ CallableHolder m_callable;
+};
+
+inline bool IsEmpty(const InternalValue& val)
+{
+ return val.IsEmpty() || std::get_if<EmptyValue>(&val.GetData()) != nullptr;
+}
+
+class RenderContext;
+
+template<typename Fn>
+auto MakeDynamicProperty(Fn&& fn)
+{
+ return CreateMapAdapter(InternalValueMap{
+ {"value()", Callable(Callable::GlobalFunc, std::forward<Fn>(fn))}
+ });
+}
+
+template<typename CharT>
+auto sv_to_string(const std::basic_string_view<CharT>& sv)
+{
+ return std::basic_string<CharT>(sv.begin(), sv.end());
+}
+
+InternalValue Subscript(const InternalValue& val, const InternalValue& subscript, RenderContext* values);
+InternalValue Subscript(const InternalValue& val, const std::string& subscript, RenderContext* values);
+std::string AsString(const InternalValue& val);
+ListAdapter ConvertToList(const InternalValue& val, bool& isConverted, bool strictConversion = true);
+ListAdapter ConvertToList(const InternalValue& val, InternalValue subscipt, bool& isConverted, bool strictConversion = true);
+Value IntValue2Value(const InternalValue& val);
+Value OptIntValue2Value(std::optional<InternalValue> val);
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_INTERNAL_VALUE_H
diff --git a/contrib/libs/jinja2cpp/src/lexer.cpp b/contrib/libs/jinja2cpp/src/lexer.cpp
new file mode 100644
index 0000000000..dfd33eb37e
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/lexer.cpp
@@ -0,0 +1,122 @@
+#include "lexer.h"
+
+#include <iostream>
+
+namespace jinja2
+{
+
+bool Lexer::Preprocess()
+{
+ bool result = true;
+ while (1)
+ {
+ lexertk::token token = m_tokenizer();
+ if (token.is_error())
+ {
+ result = false;
+ break;
+ }
+
+ Token newToken;
+ newToken.range.startOffset = token.position;
+ newToken.range.endOffset = newToken.range.startOffset + token.length;
+
+ if (token.type == lexertk::token::e_eof)
+ {
+ newToken.type = Token::Eof;
+ m_tokens.push_back(std::move(newToken));
+ break;
+ }
+
+ switch (token.type)
+ {
+ case lexertk::token::e_number:
+ result = ProcessNumber(token, newToken);
+ break;
+ case lexertk::token::e_symbol:
+ result = ProcessSymbolOrKeyword(token, newToken);
+ break;
+ case lexertk::token::e_string:
+ result = ProcessString(token, newToken);
+ break;
+ case lexertk::token::e_lte:
+ newToken.type = Token::LessEqual;
+ break;
+ case lexertk::token::e_ne:
+ newToken.type = Token::NotEqual;
+ break;
+ case lexertk::token::e_gte:
+ newToken.type = Token::GreaterEqual;
+ break;
+ case lexertk::token::e_eq:
+ newToken.type = Token::Equal;
+ break;
+ case lexertk::token::e_mulmul:
+ newToken.type = Token::MulMul;
+ break;
+ case lexertk::token::e_divdiv:
+ newToken.type = Token::DivDiv;
+ break;
+ default:
+ newToken.type = static_cast<Token::Type>(token.type);
+ break;
+ }
+
+ if (result)
+ m_tokens.push_back(std::move(newToken));
+ else
+ break;
+ }
+
+ return result;
+}
+
+bool Lexer::ProcessNumber(const lexertk::token&, Token& newToken)
+{
+ newToken.type = Token::FloatNum;
+ newToken.value = m_helper->GetAsValue(newToken.range, newToken.type);
+ return true;
+}
+
+bool Lexer::ProcessSymbolOrKeyword(const lexertk::token&, Token& newToken)
+{
+ Keyword kwType = m_helper->GetKeyword(newToken.range);
+ Token::Type tokType = Token::Unknown;
+
+ switch (kwType)
+ {
+ case Keyword::None:
+ tokType = Token::None;
+ break;
+ case Keyword::True:
+ tokType = Token::True;
+ break;
+ case Keyword::False:
+ tokType = Token::False;
+ break;
+ default:
+ tokType = Token::Unknown;
+ break;
+ }
+
+ if (tokType == Token::Unknown)
+ {
+ newToken.type = Token::Identifier;
+ auto id = m_helper->GetAsString(newToken.range);
+ newToken.value = InternalValue(id);
+ }
+ else
+ {
+ newToken.type = tokType;
+ }
+ return true;
+}
+
+bool Lexer::ProcessString(const lexertk::token&, Token& newToken)
+{
+ newToken.type = Token::String;
+ newToken.value = m_helper->GetAsValue(newToken.range, newToken.type);
+ return true;
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/lexer.h b/contrib/libs/jinja2cpp/src/lexer.h
new file mode 100644
index 0000000000..e644de2f7b
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/lexer.h
@@ -0,0 +1,375 @@
+#ifndef JINJA2CPP_SRC_LEXER_H
+#define JINJA2CPP_SRC_LEXER_H
+
+#include "lexertk.h"
+#include "internal_value.h"
+
+#include <functional>
+
+namespace jinja2
+{
+struct CharRange
+{
+ size_t startOffset;
+ size_t endOffset;
+ auto size() const {return endOffset - startOffset;}
+};
+
+struct Token
+{
+ enum Type
+ {
+ Unknown,
+
+ // One-symbol operators
+ Lt = '<',
+ Gt = '>',
+ Plus = '+',
+ Minus = '-',
+ Percent = '%',
+ Mul = '*',
+ Div = '/',
+ LBracket = '(',
+ RBracket = ')',
+ LSqBracket = '[',
+ RSqBracket = ']',
+ LCrlBracket = '{',
+ RCrlBracket = '}',
+ Assign = '=',
+ Comma = ',',
+ Eof = 256,
+
+ // General
+ Identifier,
+ IntegerNum,
+ FloatNum,
+ String,
+
+ // Operators
+ Equal,
+ NotEqual,
+ LessEqual,
+ GreaterEqual,
+ StarStar,
+ DashDash,
+ MulMul,
+ DivDiv,
+ True,
+ False,
+ None,
+
+ // Keywords
+ LogicalOr,
+ LogicalAnd,
+ LogicalNot,
+ In,
+ Is,
+ For,
+ Endfor,
+ If,
+ Else,
+ ElIf,
+ EndIf,
+ Block,
+ EndBlock,
+ Extends,
+ Macro,
+ EndMacro,
+ Call,
+ EndCall,
+ Filter,
+ EndFilter,
+ Set,
+ EndSet,
+ Include,
+ Import,
+ Recursive,
+ Scoped,
+ With,
+ EndWith,
+ Without,
+ Ignore,
+ Missing,
+ Context,
+ From,
+ As,
+ Do,
+
+ // Template control
+ CommentBegin,
+ CommentEnd,
+ RawBegin,
+ RawEnd,
+ MetaBegin,
+ MetaEnd,
+ StmtBegin,
+ StmtEnd,
+ ExprBegin,
+ ExprEnd,
+ };
+
+ Type type = Unknown;
+ CharRange range = {0, 0};
+ InternalValue value;
+
+ bool IsEof() const
+ {
+ return type == Eof;
+ }
+
+ bool operator == (char ch) const
+ {
+ return type == ch;
+ }
+
+ bool operator == (Type t) const
+ {
+ return type == t;
+ }
+
+ template<typename T>
+ bool operator != (T v) const
+ {
+ return !(*this == v);
+ }
+};
+
+enum class Keyword
+{
+ Unknown,
+
+ // Keywords
+ LogicalOr,
+ LogicalAnd,
+ LogicalNot,
+ True,
+ False,
+ None,
+ In,
+ Is,
+ For,
+ Endfor,
+ If,
+ Else,
+ ElIf,
+ EndIf,
+ Block,
+ EndBlock,
+ Extends,
+ Macro,
+ EndMacro,
+ Call,
+ EndCall,
+ Filter,
+ EndFilter,
+ Set,
+ EndSet,
+ Include,
+ Import,
+ Recursive,
+ Scoped,
+ With,
+ EndWith,
+ Without,
+ Ignore,
+ Missing,
+ Context,
+ From,
+ As,
+ Do,
+};
+
+struct LexerHelper
+{
+ virtual std::string GetAsString(const CharRange& range) = 0;
+ virtual InternalValue GetAsValue(const CharRange& range, Token::Type type) = 0;
+ virtual Keyword GetKeyword(const CharRange& range) = 0;
+ virtual char GetCharAt(size_t pos) = 0;
+};
+
+class Lexer
+{
+public:
+ using TokensList = std::vector<Token>;
+ Lexer(std::function<lexertk::token ()> tokenizer, LexerHelper* helper)
+ : m_tokenizer(std::move(tokenizer))
+ , m_helper(helper)
+ {
+ }
+
+ bool Preprocess();
+ const TokensList& GetTokens() const
+ {
+ return m_tokens;
+ }
+
+ auto GetHelper() const {return m_helper;}
+
+private:
+ bool ProcessNumber(const lexertk::token& token, Token& newToken);
+ bool ProcessSymbolOrKeyword(const lexertk::token& token, Token& newToken);
+ bool ProcessString(const lexertk::token& token, Token& newToken);
+private:
+ std::function<lexertk::token ()> m_tokenizer;
+ TokensList m_tokens;
+ LexerHelper* m_helper;
+};
+
+class LexScanner
+{
+public:
+ struct State
+ {
+ Lexer::TokensList::const_iterator m_begin;
+ Lexer::TokensList::const_iterator m_end;
+ Lexer::TokensList::const_iterator m_cur;
+ };
+
+ struct StateSaver
+ {
+ StateSaver(LexScanner& scanner)
+ : m_state(scanner.m_state)
+ , m_scanner(scanner)
+ {
+ }
+
+ ~StateSaver()
+ {
+ if (!m_commited)
+ m_scanner.m_state = m_state;
+ }
+
+ void Commit()
+ {
+ m_commited = true;
+ }
+
+ State m_state;
+ LexScanner& m_scanner;
+ bool m_commited = false;
+ };
+
+ LexScanner(const Lexer& lexer)
+ : m_helper(lexer.GetHelper())
+ {
+ m_state.m_begin = lexer.GetTokens().begin();
+ m_state.m_end = lexer.GetTokens().end();
+ Reset();
+ }
+
+ void Reset()
+ {
+ m_state.m_cur = m_state.m_begin;
+ }
+
+ auto GetState() const
+ {
+ return m_state;
+ }
+
+ void RestoreState(const State& state)
+ {
+ m_state = state;
+ }
+
+ const Token& NextToken()
+ {
+ if (m_state.m_cur == m_state.m_end)
+ return EofToken();
+
+ return *m_state.m_cur ++;
+ }
+
+ void EatToken()
+ {
+ if (m_state.m_cur != m_state.m_end)
+ ++ m_state.m_cur;
+ }
+
+ void ReturnToken()
+ {
+ if (m_state.m_cur != m_state.m_begin)
+ -- m_state.m_cur;
+ }
+
+ const Token& PeekNextToken() const
+ {
+ if (m_state.m_cur == m_state.m_end)
+ return EofToken();
+
+ return *m_state.m_cur;
+ }
+
+ bool EatIfEqual(char type, Token* tok = nullptr)
+ {
+ return EatIfEqual(static_cast<Token::Type>(type), tok);
+ }
+
+ bool EatIfEqual(Token::Type type, Token* tok = nullptr)
+ {
+ if (m_state.m_cur == m_state.m_end)
+ {
+ if(type == Token::Type::Eof && tok)
+ *tok = EofToken();
+
+ return type == Token::Type::Eof;
+ }
+
+ return EatIfEqualImpl(tok, [type](const Token& t) {return t.type == type;});
+ }
+
+ auto GetAsKeyword(const Token& tok) const
+ {
+ return m_helper->GetKeyword(tok.range);
+ }
+
+ bool EatIfEqual(Keyword kwType, Token* tok = nullptr)
+ {
+ if (m_state.m_cur == m_state.m_end)
+ return false;
+
+ return EatIfEqualImpl(tok, [this, kwType](const Token& t) {return GetAsKeyword(t) == kwType;});
+ }
+
+private:
+ template<typename Fn>
+ bool EatIfEqualImpl(Token* tok, Fn&& predicate)
+ {
+ if (predicate(*m_state.m_cur))
+ {
+ if (tok)
+ *tok = *m_state.m_cur;
+ ++ m_state.m_cur;
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ State m_state;
+ LexerHelper* m_helper;
+
+ static const Token& EofToken()
+ {
+ static Token eof;
+ eof.type = Token::Eof;
+ return eof;
+ }
+};
+
+} // namespace jinja2
+
+namespace std
+{
+template<>
+struct hash<jinja2::Keyword>
+{
+ size_t operator()(jinja2::Keyword kw) const
+ {
+ return std::hash<int>{}(static_cast<int>(kw));
+ }
+};
+} // namespace std
+
+#endif // JINJA2CPP_SRC_LEXER_H
diff --git a/contrib/libs/jinja2cpp/src/lexertk.h b/contrib/libs/jinja2cpp/src/lexertk.h
new file mode 100644
index 0000000000..c318cc7f3d
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/lexertk.h
@@ -0,0 +1,1842 @@
+/*
+ *****************************************************************
+ * Simple C++ Lexer Toolkit Library *
+ * *
+ * Author: Arash Partow (2001) *
+ * Modified: Flex Ferrum (2018)
+ * URL: http://www.partow.net/programming/lexertk/index.html *
+ * *
+ * Copyright notice: *
+ * Free use of the Simple C++ Lexer Toolkit Library is permitted *
+ * under the guidelines and in accordance with the MIT License. *
+ * http://www.opensource.org/licenses/MIT *
+ * *
+ * *
+ * The lexer will tokenize input against the following BNF: *
+ * *
+ * expression ::= term { +|- term } *
+ * term ::= (symbol | factor) {operator symbol | factor} *
+ * factor ::= symbol | ( '(' {-} expression ')' ) *
+ * symbol ::= number | gensymb | string *
+ * gensymb ::= alphabet {alphabet | digit} *
+ * string ::= '"' {alphabet | digit | operator } '"' *
+ * operator ::= * | / | % | ^ | < | > | <= | >= | << | >> != *
+ * alphabet ::= a | b | .. | z | A | B | .. | Z *
+ * digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 *
+ * sign ::= + | - *
+ * edef ::= e | E *
+ * decimal ::= {digit} (digit [.] | [.] digit) {digit} *
+ * exponent ::= edef [sign] digit {digit} *
+ * real ::= [sign] decimal [exponent] *
+ * integer ::= [sign] {digit} *
+ * number ::= real | integer *
+ * *
+ * *
+ * Note: This lexer has been taken from the ExprTk Library. *
+ * *
+ *****************************************************************
+*/
+
+
+#ifndef JINJA2CPP_SRC_LEXERTK_H
+#define JINJA2CPP_SRC_LEXERTK_H
+
+#include <algorithm>
+#include <cctype>
+#include <clocale>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <deque>
+#include <exception>
+#include <limits>
+#include <locale>
+#include <map>
+#include <set>
+#include <stack>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+
+namespace lexertk
+{
+ template<typename CharT>
+ struct CharRange
+ {
+ CharT* start;
+ CharT* end;
+
+ auto length() const {return end - start;}
+ auto offset(CharT* from) const {return start - from;}
+ auto operator[] (size_t idx) const {return start[idx];}
+ };
+ namespace details
+ {
+#if 0
+ inline bool is_whitespace(const char c)
+ {
+ return (' ' == c) || ('\n' == c) ||
+ ('\r' == c) || ('\t' == c) ||
+ ('\b' == c) || ('\v' == c) ||
+ ('\f' == c) ;
+ }
+
+
+ inline bool is_letter(const char c)
+ {
+ return (('a' <= c) && (c <= 'z')) || (('A' <= c) && (c <= 'Z'));
+ }
+
+ inline bool is_digit(const char c)
+ {
+ return ('0' <= c) && (c <= '9');
+ }
+
+ inline bool is_letter_or_digit(const char c)
+ {
+ return is_letter(c) || is_digit(c);
+ }
+#endif
+ template<typename CharT>
+ struct lexer_traits
+ {
+ static auto& get_locale()
+ {
+ static auto locale = std::locale();
+ return locale;
+ }
+
+ static bool is_whitespace(const CharT c)
+ {
+ return std::isspace(c, get_locale());
+ }
+ static bool is_letter(const CharT c)
+ {
+ return std::isalpha(c, get_locale());
+ }
+ static bool is_digit(const CharT c)
+ {
+ return std::isdigit(c, get_locale());
+ }
+ static bool is_letter_or_digit(CharT c)
+ {
+ return std::isalnum(c, get_locale());
+ }
+ static bool is_operator_char(const CharT c);
+ static bool is_left_bracket(const CharT c);
+ static bool is_right_bracket(const CharT c);
+ static bool is_sign(const CharT c);
+ static bool is_invalid(const CharT c);
+ static bool is_bracket(const CharT c)
+ {
+ return is_left_bracket(c) || is_right_bracket(c);
+ }
+ static CharT tolower(const CharT c)
+ {
+ return std::tolower(c, get_locale());
+ }
+ static CharT toupper(const CharT c)
+ {
+ return std::toupper(c, get_locale());
+ }
+ static inline bool imatch(const CharT c1, const CharT c2)
+ {
+ return tolower(c1) == tolower(c2);
+ }
+
+ static inline bool imatch(const CharRange<CharT>& s1, const CharRange<CharT>& s2)
+ {
+ if (s1.length() == s2.length())
+ {
+ for (std::size_t i = 0; i < s1.length(); ++i)
+ {
+ if (tolower(s1[i]) != tolower(s2[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ template<>
+ inline bool lexer_traits<char>::is_operator_char(const char c)
+ {
+ return ('+' == c) || ('-' == c) ||
+ ('*' == c) || ('/' == c) ||
+ ('^' == c) || ('<' == c) ||
+ ('>' == c) || ('=' == c) ||
+ (',' == c) || ('!' == c) ||
+ ('(' == c) || (')' == c) ||
+ ('[' == c) || (']' == c) ||
+ ('{' == c) || ('}' == c) ||
+ ('%' == c) || (':' == c) ||
+ ('?' == c) || ('&' == c) ||
+ ('|' == c) || (';' == c) ||
+ ('~' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<wchar_t>::is_operator_char(const wchar_t c)
+ {
+ return (L'+' == c) || (L'-' == c) ||
+ (L'*' == c) || (L'/' == c) ||
+ (L'^' == c) || (L'<' == c) ||
+ (L'>' == c) || (L'=' == c) ||
+ (L',' == c) || (L'!' == c) ||
+ (L'(' == c) || (L')' == c) ||
+ (L'[' == c) || (L']' == c) ||
+ (L'{' == c) || (L'}' == c) ||
+ (L'%' == c) || (L':' == c) ||
+ (L'?' == c) || (L'&' == c) ||
+ (L'|' == c) || (L';' == c) ||
+ (L'~' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<char>::is_left_bracket(const char c)
+ {
+ return ('(' == c) || ('[' == c) || ('{' == c);
+ }
+ template<>
+ inline bool lexer_traits<wchar_t>::is_left_bracket(const wchar_t c)
+ {
+ return (L'(' == c) || (L'[' == c) || (L'{' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<char>::is_right_bracket(const char c)
+ {
+ return (')' == c) || (']' == c) || ('}' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<wchar_t>::is_right_bracket(const wchar_t c)
+ {
+ return (L')' == c) || (L']' == c) || (L'}' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<char>::is_sign(const char c)
+ {
+ return ('+' == c) || ('-' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<wchar_t>::is_sign(const wchar_t c)
+ {
+ return (L'+' == c) || (L'-' == c);
+ }
+
+ template<>
+ inline bool lexer_traits<char>::is_invalid(const char c)
+ {
+ return !is_whitespace(c) &&
+ !is_operator_char(c) &&
+ !is_letter(c) &&
+ !is_digit(c) &&
+ ('.' != c) &&
+ ('_' != c) &&
+ ('$' != c) &&
+ ('~' != c) &&
+ ('\'' != c);
+ }
+
+ template<>
+ inline bool lexer_traits<wchar_t>::is_invalid(const wchar_t c)
+ {
+ return !is_whitespace(c) &&
+ !is_operator_char(c) &&
+ !is_letter(c) &&
+ !is_digit(c) &&
+ (L'.' != c) &&
+ (L'_' != c) &&
+ (L'$' != c) &&
+ (L'~' != c) &&
+ (L'\'' != c);
+ }
+
+ template<typename CharT>
+ struct ilesscompare
+ {
+ inline bool operator()(const CharRange<CharT>& s1, const CharRange<CharT>& s2) const
+ {
+ using traits = lexer_traits<CharT>;
+ const std::size_t length = std::min(s1.length(),s2.length());
+
+ for (std::size_t i = 0; i < length; ++i)
+ {
+ if (traits::tolower(s1[i]) > traits::tolower(s2[i]))
+ return false;
+ else if (traits::tolower(s1[i]) < traits::tolower(s2[i]))
+ return true;
+ }
+
+ return s1.length() < s2.length();
+ }
+ };
+
+ } // namespace details
+
+ struct token
+ {
+
+ enum token_type
+ {
+ e_none = 0, e_error = 1, e_err_symbol = 2,
+ e_err_number = 3, e_err_string = 4, e_err_sfunc = 5,
+ e_eof = 6, e_number = 7, e_symbol = 8,
+ e_string = 9, e_eq = 10, e_shr = 11,
+ e_shl = 12, e_lte = 13, e_ne = 14,
+ e_gte = 15, e_lt = '<', e_gt = '>',
+ e_rbracket = ')', e_lbracket = '(', e_tilda = '~',
+ e_rsqrbracket = ']', e_lsqrbracket = '[', e_rcrlbracket = '}',
+ e_lcrlbracket = '{', e_comma = ',', e_add = '+',
+ e_sub = '-', e_div = '/', e_mul = '*',
+ e_mod = '%', e_pow = '^', e_colon = ':',
+ e_dot = '.', e_divdiv = 16 , e_mulmul = 17 ,
+ e_assign = '=', e_pipe = '|',
+ };
+
+ token()
+ : type(e_none),
+ position(std::numeric_limits<std::size_t>::max()),
+ length(0)
+ {}
+
+ void clear()
+ {
+ type = e_none;
+ position = std::numeric_limits<std::size_t>::max();
+ }
+
+ template <typename Iterator>
+ inline token& set_operator(const token_type tt, const Iterator begin, const Iterator end, const Iterator base_begin)
+ {
+ type = tt;
+ position = std::distance(base_begin,begin);
+ length = end - begin;
+ return *this;
+ }
+
+ template <typename Iterator>
+ inline token& set_symbol(const Iterator begin, const Iterator end, const Iterator base_begin)
+ {
+ type = e_symbol;
+ position = std::distance(base_begin,begin);
+ length = end - begin;
+ return *this;
+ }
+
+ template <typename Iterator>
+ inline token& set_numeric(const Iterator begin, const Iterator end, const Iterator base_begin)
+ {
+ type = e_number;
+ position = std::distance(base_begin,begin);
+ length = end - begin;
+ return *this;
+ }
+
+ template <typename Iterator>
+ inline token& set_string(const Iterator begin, const Iterator end, const Iterator base_begin)
+ {
+ type = e_string;
+ position = std::distance(base_begin,begin);
+ length = end - begin;
+ return *this;
+ }
+
+ template <typename Iterator>
+ inline token& set_error(const token_type et, const Iterator begin, const Iterator end, const Iterator base_begin)
+ {
+ if (
+ (e_error == et) ||
+ (e_err_symbol == et) ||
+ (e_err_number == et) ||
+ (e_err_string == et)
+ )
+ {
+ type = e_error;
+ }
+ else
+ type = e_error;
+
+ position = std::distance(base_begin,begin);
+ length = end - begin;
+
+ return *this;
+ }
+
+ static inline const char* to_str(token_type t)
+ {
+ switch (t)
+ {
+ case e_none : return "NONE";
+ case e_error : return "ERROR";
+ case e_err_symbol : return "ERROR_SYMBOL";
+ case e_err_number : return "ERROR_NUMBER";
+ case e_err_string : return "ERROR_STRING";
+ case e_eof : return "EOF";
+ case e_number : return "NUMBER";
+ case e_symbol : return "SYMBOL";
+ case e_string : return "STRING";
+ case e_assign : return "=";
+ case e_shr : return ">>";
+ case e_shl : return "<<";
+ case e_lte : return "<=";
+ case e_ne : return "!=";
+ case e_gte : return ">=";
+ case e_lt : return "<";
+ case e_gt : return ">";
+ case e_eq : return "==";
+ case e_rbracket : return ")";
+ case e_lbracket : return "(";
+ case e_rsqrbracket : return "]";
+ case e_lsqrbracket : return "[";
+ case e_rcrlbracket : return "}";
+ case e_lcrlbracket : return "{";
+ case e_comma : return ",";
+ case e_dot : return ".";
+ case e_add : return "+";
+ case e_sub : return "-";
+ case e_div : return "/";
+ case e_mul : return "*";
+ case e_mod : return "%";
+ case e_pow : return "^";
+ case e_colon : return ":";
+ case e_divdiv : return "//";
+ case e_mulmul : return "**";
+ default : return "UNKNOWN";
+ }
+ }
+
+ inline bool is_error() const
+ {
+ return (
+ (e_error == type) ||
+ (e_err_symbol == type) ||
+ (e_err_number == type) ||
+ (e_err_string == type)
+ );
+ }
+
+ token_type type;
+ size_t position;
+ size_t length;
+ };
+
+ template<typename CharT>
+ class generator
+ {
+ public:
+
+ typedef token token_t;
+ typedef std::vector<token_t> token_list_t;
+ typedef std::vector<token_t>::iterator token_list_itr_t;
+ typedef details::lexer_traits<CharT> traits;
+
+ generator()
+ : base_itr_(0),
+ s_itr_(0),
+ s_end_(0)
+ {
+ clear();
+ }
+
+ inline void clear()
+ {
+ base_itr_ = 0;
+ s_itr_ = 0;
+ s_end_ = 0;
+ token_list_.clear();
+ token_itr_ = token_list_.end();
+ store_token_itr_ = token_itr_;
+ }
+
+ inline bool process(const std::basic_string<CharT>& str)
+ {
+ return process(str.data(), str.data() + str.size());
+ }
+
+ inline bool process(const CharT* begin, const CharT* end)
+ {
+ base_itr_ = begin;
+ s_itr_ = begin;
+ s_end_ = end;
+
+ eof_token_.set_operator(token_t::e_eof,s_end_,s_end_,base_itr_);
+ token_list_.clear();
+
+ while (!is_end(s_itr_))
+ {
+ scan_token();
+
+ if (token_list_.empty())
+ return true;
+ else if (token_list_.back().is_error())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ inline bool empty() const
+ {
+ return token_list_.empty();
+ }
+
+ inline size_t size() const
+ {
+ return token_list_.size();
+ }
+
+ inline void begin()
+ {
+ token_itr_ = token_list_.begin();
+ store_token_itr_ = token_itr_;
+ }
+
+ inline void store()
+ {
+ store_token_itr_ = token_itr_;
+ }
+
+ inline void restore()
+ {
+ token_itr_ = store_token_itr_;
+ }
+
+ inline token_t& next_token()
+ {
+ if (token_list_.end() != token_itr_)
+ {
+ return *token_itr_++;
+ }
+ else
+ return eof_token_;
+ }
+
+ inline token_t& peek_next_token()
+ {
+ if (token_list_.end() != token_itr_)
+ {
+ return *token_itr_;
+ }
+ else
+ return eof_token_;
+ }
+
+ inline token_t& operator[](const std::size_t& index)
+ {
+ if (index < token_list_.size())
+ return token_list_[index];
+ else
+ return eof_token_;
+ }
+
+ inline token_t operator[](const std::size_t& index) const
+ {
+ if (index < token_list_.size())
+ return token_list_[index];
+ else
+ return eof_token_;
+ }
+
+ inline bool finished() const
+ {
+ return (token_list_.end() == token_itr_);
+ }
+
+ inline std::basic_string<CharT> remaining() const
+ {
+ using string = std::basic_string<CharT>;
+ if (finished())
+ return string();
+ else if (token_list_.begin() != token_itr_)
+ return string(base_itr_ + (token_itr_ - 1)->position,s_end_);
+ else
+ return string(base_itr_ + token_itr_->position,s_end_);
+ }
+
+ private:
+
+ inline bool is_end(const CharT* itr)
+ {
+ return (s_end_ == itr);
+ }
+
+ inline void skip_whitespace()
+ {
+ while (!is_end(s_itr_) && traits::is_whitespace(*s_itr_))
+ {
+ ++s_itr_;
+ }
+ }
+ inline void scan_token()
+ {
+ skip_whitespace();
+
+ if (is_end(s_itr_))
+ {
+ return;
+ }
+ else if (traits::is_operator_char(*s_itr_))
+ {
+ scan_operator();
+ return;
+ }
+ else if (traits::is_letter(*s_itr_) || ('_' == (*s_itr_)))
+ {
+ scan_symbol();
+ return;
+ }
+ else if (traits::is_digit((*s_itr_)) || ('.' == (*s_itr_)))
+ {
+ scan_number();
+ return;
+ }
+ else if ('\'' == (*s_itr_) || '\"' == (*s_itr_))
+ {
+ scan_string();
+ return;
+ }
+ else
+ {
+ token_t t;
+ t.set_error(token::e_error,s_itr_,s_itr_ + 2,base_itr_);
+ token_list_.push_back(t);
+ ++s_itr_;
+ }
+ }
+
+ inline void scan_operator()
+ {
+ token_t t;
+
+ if (!is_end(s_itr_ + 1))
+ {
+ token_t::token_type ttype = token_t::e_none;
+
+ CharT c0 = s_itr_[0];
+ CharT c1 = s_itr_[1];
+
+ if ((c0 == '<') && (c1 == '=')) ttype = token_t::e_lte;
+ else if ((c0 == '>') && (c1 == '=')) ttype = token_t::e_gte;
+ else if ((c0 == '<') && (c1 == '>')) ttype = token_t::e_ne;
+ else if ((c0 == '!') && (c1 == '=')) ttype = token_t::e_ne;
+ else if ((c0 == '=') && (c1 == '=')) ttype = token_t::e_eq;
+ else if ((c0 == ':') && (c1 == '=')) ttype = token_t::e_assign;
+ else if ((c0 == '<') && (c1 == '<')) ttype = token_t::e_shl;
+ else if ((c0 == '>') && (c1 == '>')) ttype = token_t::e_shr;
+ else if ((c0 == '*') && (c1 == '*')) ttype = token_t::e_mulmul;
+ else if ((c0 == '/') && (c1 == '/')) ttype = token_t::e_divdiv;
+
+ if (token_t::e_none != ttype)
+ {
+ t.set_operator(ttype,s_itr_,s_itr_ + 2,base_itr_);
+ token_list_.push_back(t);
+ s_itr_ += 2;
+ return;
+ }
+ }
+
+ if ('<' == *s_itr_)
+ t.set_operator(token_t::e_lt ,s_itr_,s_itr_ + 1,base_itr_);
+ else if ('>' == *s_itr_)
+ t.set_operator(token_t::e_gt ,s_itr_,s_itr_ + 1,base_itr_);
+ else if (';' == *s_itr_)
+ t.set_operator(token_t::e_eof,s_itr_,s_itr_ + 1,base_itr_);
+ else if ('&' == *s_itr_)
+ t.set_symbol(s_itr_,s_itr_ + 1,base_itr_);
+ else if ('|' == *s_itr_)
+ t.set_operator(token::e_pipe,s_itr_,s_itr_ + 1,base_itr_);
+ else
+ t.set_operator(token_t::token_type(*s_itr_),s_itr_,s_itr_ + 1,base_itr_);
+
+ token_list_.push_back(t);
+
+ ++s_itr_;
+ }
+
+ inline void scan_symbol()
+ {
+ const CharT* begin = s_itr_;
+ while (
+ (!is_end(s_itr_)) &&
+ (traits::is_letter_or_digit(*s_itr_) || ((*s_itr_) == '_'))
+ )
+ {
+ ++s_itr_;
+ }
+ token_t t;
+ t.set_symbol(begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+ }
+
+ inline void scan_number()
+ {
+ /*
+ Attempt to match a valid numeric value in one of the following formats:
+ 01. 123456
+ 02. 123.456
+ 03. 123.456e3
+ 04. 123.456E3
+ 05. 123.456e+3
+ 06. 123.456E+3
+ 07. 123.456e-3
+ 08. 123.456E-3
+ 09. .1234
+ 10. .1234e3
+ 11. .1234E+3
+ 12. .1234e+3
+ 13. .1234E-3
+ 14. .1234e-3
+ */
+ const CharT* begin = s_itr_;
+ bool dot_found = false;
+ bool e_found = false;
+ bool post_e_sign_found = false;
+ bool post_e_digit_found = false;
+ token_t t;
+
+ if ('.' == *begin && !is_end(begin + 1) && !traits::is_digit(begin[1]))
+ {
+ scan_operator();
+ return;
+ }
+
+ while (!is_end(s_itr_))
+ {
+ if ('.' == (*s_itr_))
+ {
+ if (dot_found)
+ {
+ t.set_error(token::e_err_number,begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ dot_found = true;
+ ++s_itr_;
+
+ continue;
+ }
+ else if (traits::imatch('e',(*s_itr_)))
+ {
+ const CharT& c = *(s_itr_ + 1);
+
+ if (is_end(s_itr_ + 1))
+ {
+ t.set_error(token::e_err_number,begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+ else if (
+ ('+' != c) &&
+ ('-' != c) &&
+ !traits::is_digit(c)
+ )
+ {
+ t.set_error(token::e_err_number,begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ e_found = true;
+ ++s_itr_;
+
+ continue;
+ }
+ else if (e_found && traits::is_sign(*s_itr_) && !post_e_digit_found)
+ {
+ if (post_e_sign_found)
+ {
+ t.set_error(token::e_err_number,begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ post_e_sign_found = true;
+ ++s_itr_;
+
+ continue;
+ }
+ else if (e_found && traits::is_digit(*s_itr_))
+ {
+ post_e_digit_found = true;
+ ++s_itr_;
+
+ continue;
+ }
+ else if (('.' != (*s_itr_)) && !traits::is_digit(*s_itr_))
+ break;
+ else
+ ++s_itr_;
+ }
+
+ t.set_numeric(begin,s_itr_,base_itr_);
+
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ inline void scan_string()
+ {
+ CharT endChar = *s_itr_;
+ const CharT* begin = s_itr_ + 1;
+
+ token_t t;
+
+ if (std::distance(s_itr_,s_end_) < 2)
+ {
+ t.set_error(token::e_err_string,s_itr_,s_end_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ ++s_itr_;
+
+ bool escaped = false;
+
+ while (!is_end(s_itr_))
+ {
+ if (!escaped && ('\\' == *s_itr_))
+ {
+ escaped = true;
+ ++s_itr_;
+
+ continue;
+ }
+ else if (!escaped)
+ {
+ if (endChar == *s_itr_)
+ break;
+ }
+ else
+ escaped = false;
+
+ ++s_itr_;
+ }
+
+ if (is_end(s_itr_))
+ {
+ t.set_error(token::e_err_string,begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+
+ return;
+ }
+
+ t.set_string(begin,s_itr_,base_itr_);
+ token_list_.push_back(t);
+ ++s_itr_;
+
+ return;
+ }
+
+ private:
+
+ token_list_t token_list_;
+ token_list_itr_t token_itr_;
+ token_list_itr_t store_token_itr_;
+ token_t eof_token_;
+ const CharT* base_itr_;
+ const CharT* s_itr_;
+ const CharT* s_end_;
+
+ friend class token_scanner;
+ friend class token_modifier;
+ friend class token_inserter;
+ friend class token_joiner;
+ };
+#if 0
+ class helper_interface
+ {
+ public:
+
+ virtual void init() { }
+ virtual void reset() { }
+ virtual bool result() { return true; }
+ virtual std::size_t process(generator&) { return 0; }
+ virtual ~helper_interface() { }
+ };
+
+ class token_scanner : public helper_interface
+ {
+ public:
+
+ virtual ~token_scanner()
+ {}
+
+ explicit token_scanner(const std::size_t& stride)
+ : stride_(stride)
+ {
+ if (stride > 4)
+ {
+ throw std::invalid_argument("token_scanner() - Invalid stride value");
+ }
+ }
+
+ inline std::size_t process(generator& g)
+ {
+ if (!g.token_list_.empty())
+ {
+ for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i)
+ {
+ token t;
+ switch (stride_)
+ {
+ case 1 :
+ {
+ const token& t0 = g.token_list_[i];
+
+ if (!operator()(t0)) return i;
+ }
+ break;
+
+ case 2 :
+ {
+ const token& t0 = g.token_list_[i ];
+ const token& t1 = g.token_list_[i + 1];
+
+ if (!operator()(t0,t1)) return i;
+ }
+ break;
+
+ case 3 :
+ {
+ const token& t0 = g.token_list_[i ];
+ const token& t1 = g.token_list_[i + 1];
+ const token& t2 = g.token_list_[i + 2];
+
+ if (!operator()(t0,t1,t2)) return i;
+ }
+ break;
+
+ case 4 :
+ {
+ const token& t0 = g.token_list_[i ];
+ const token& t1 = g.token_list_[i + 1];
+ const token& t2 = g.token_list_[i + 2];
+ const token& t3 = g.token_list_[i + 3];
+
+ if (!operator()(t0,t1,t2,t3)) return i;
+ }
+ break;
+ }
+ }
+ }
+
+ return (g.token_list_.size() - stride_ + 1);
+ }
+
+ virtual bool operator()(const token&)
+ {
+ return false;
+ }
+
+ virtual bool operator()(const token&, const token&)
+ {
+ return false;
+ }
+
+ virtual bool operator()(const token&, const token&, const token&)
+ {
+ return false;
+ }
+
+ virtual bool operator()(const token&, const token&, const token&, const token&)
+ {
+ return false;
+ }
+
+ private:
+
+ std::size_t stride_;
+ };
+
+ class token_modifier : public helper_interface
+ {
+ public:
+
+ inline std::size_t process(generator& g)
+ {
+ std::size_t changes = 0;
+
+ for (std::size_t i = 0; i < g.token_list_.size(); ++i)
+ {
+ if (modify(g.token_list_[i])) changes++;
+ }
+
+ return changes;
+ }
+
+ virtual bool modify(token& t) = 0;
+ };
+
+ class token_inserter : public helper_interface
+ {
+ public:
+
+ explicit token_inserter(const std::size_t& stride)
+ : stride_(stride)
+ {
+ if (stride > 5)
+ {
+ throw std::invalid_argument("token_inserter() - Invalid stride value");
+ }
+ }
+
+ inline std::size_t process(generator& g)
+ {
+ if (g.token_list_.empty())
+ return 0;
+
+ std::size_t changes = 0;
+
+ for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i)
+ {
+ token t;
+ int insert_index = -1;
+
+ switch (stride_)
+ {
+ case 1 : insert_index = insert(g.token_list_[i],t);
+ break;
+
+ case 2 : insert_index = insert(g.token_list_[i],g.token_list_[i + 1],t);
+ break;
+
+ case 3 : insert_index = insert(g.token_list_[i],g.token_list_[i + 1],g.token_list_[i + 2],t);
+ break;
+
+ case 4 : insert_index = insert(g.token_list_[i],g.token_list_[i + 1],g.token_list_[i + 2],g.token_list_[i + 3],t);
+ break;
+
+ case 5 : insert_index = insert(g.token_list_[i],g.token_list_[i + 1],g.token_list_[i + 2],g.token_list_[i + 3],g.token_list_[i + 4],t);
+ break;
+ }
+
+ if ((insert_index >= 0) && (insert_index <= (static_cast<int>(stride_) + 1)))
+ {
+ g.token_list_.insert(g.token_list_.begin() + (i + insert_index),t);
+ changes++;
+ }
+ }
+
+ return changes;
+ }
+
+ virtual inline int insert(const token&, token& )
+ {
+ return -1;
+ }
+
+ virtual inline int insert(const token&, const token&, token&)
+ {
+ return -1;
+ }
+
+ virtual inline int insert(const token&, const token&, const token&, token&)
+ {
+ return -1;
+ }
+
+ virtual inline int insert(const token&, const token&, const token&, const token&, token&)
+ {
+ return -1;
+ }
+
+ virtual inline int insert(const token&, const token&, const token&, const token&, const token&, token&)
+ {
+ return -1;
+ }
+
+ private:
+
+ std::size_t stride_;
+ };
+
+ class token_joiner : public helper_interface
+ {
+ public:
+
+ inline std::size_t process(generator& g)
+ {
+ if (g.token_list_.empty())
+ return 0;
+
+ std::size_t changes = 0;
+
+ for (std::size_t i = 0; i < g.token_list_.size() - 1; ++i)
+ {
+ token t;
+
+ if (join(g.token_list_[i],g.token_list_[i + 1],t))
+ {
+ g.token_list_[i] = t;
+ g.token_list_.erase(g.token_list_.begin() + (i + 1));
+
+ ++changes;
+ }
+ }
+
+ return changes;
+ }
+
+ virtual bool join(const token&, const token&, token&) = 0;
+ };
+
+ namespace helper
+ {
+
+ inline void dump(lexertk::generator& generator)
+ {
+ for (std::size_t i = 0; i < generator.size(); ++i)
+ {
+ lexertk::token t = generator[i];
+ printf("Token[%02d] @ %03d %6s --> '%s'\n",
+ static_cast<unsigned int>(i),
+ static_cast<unsigned int>(t.position),
+ t.to_str(t.type).c_str(),
+ t.value.c_str());
+ }
+ }
+
+ class commutative_inserter : public token_inserter
+ {
+ public:
+
+ commutative_inserter()
+ : lexertk::token_inserter(2)
+ {}
+
+ inline void ignore_symbol(const std::string& symbol)
+ {
+ ignore_set_.insert(symbol);
+ }
+
+ inline int insert(const lexertk::token& t0, const lexertk::token& t1, lexertk::token& new_token)
+ {
+ new_token.type = lexertk::token::e_mul;
+ new_token.value = "*";
+ new_token.position = t1.position;
+ bool match = false;
+
+ if (t0.type == lexertk::token::e_symbol)
+ {
+ if (ignore_set_.end() != ignore_set_.find(t0.value))
+ {
+ return -1;
+ }
+ else if (!t0.value.empty() && ('$' == t0.value[0]))
+ {
+ return -1;
+ }
+ }
+
+ if (t1.type == lexertk::token::e_symbol)
+ {
+ if (ignore_set_.end() != ignore_set_.find(t1.value))
+ {
+ return -1;
+ }
+ }
+
+ if ((t0.type == lexertk::token::e_number ) && (t1.type == lexertk::token::e_symbol )) match = true;
+ else if ((t0.type == lexertk::token::e_number ) && (t1.type == lexertk::token::e_lbracket )) match = true;
+ else if ((t0.type == lexertk::token::e_number ) && (t1.type == lexertk::token::e_lcrlbracket)) match = true;
+ else if ((t0.type == lexertk::token::e_number ) && (t1.type == lexertk::token::e_lsqrbracket)) match = true;
+ else if ((t0.type == lexertk::token::e_symbol ) && (t1.type == lexertk::token::e_number )) match = true;
+ else if ((t0.type == lexertk::token::e_rbracket ) && (t1.type == lexertk::token::e_number )) match = true;
+ else if ((t0.type == lexertk::token::e_rcrlbracket) && (t1.type == lexertk::token::e_number )) match = true;
+ else if ((t0.type == lexertk::token::e_rsqrbracket) && (t1.type == lexertk::token::e_number )) match = true;
+ else if ((t0.type == lexertk::token::e_rbracket ) && (t1.type == lexertk::token::e_symbol )) match = true;
+ else if ((t0.type == lexertk::token::e_rcrlbracket) && (t1.type == lexertk::token::e_symbol )) match = true;
+ else if ((t0.type == lexertk::token::e_rsqrbracket) && (t1.type == lexertk::token::e_symbol )) match = true;
+
+ return (match) ? 1 : -1;
+ }
+
+ private:
+
+ std::set<std::string,details::ilesscompare> ignore_set_;
+ };
+
+ class operator_joiner : public token_joiner
+ {
+ public:
+
+ inline bool join(const lexertk::token& t0, const lexertk::token& t1, lexertk::token& t)
+ {
+ //': =' --> ':='
+ if ((t0.type == lexertk::token::e_colon) && (t1.type == lexertk::token::e_eq))
+ {
+ t.type = lexertk::token::e_assign;
+ t.value = ":=";
+ t.position = t0.position;
+
+ return true;
+ }
+ //'> =' --> '>='
+ else if ((t0.type == lexertk::token::e_gt) && (t1.type == lexertk::token::e_eq))
+ {
+ t.type = lexertk::token::e_gte;
+ t.value = ">=";
+ t.position = t0.position;
+
+ return true;
+ }
+ //'< =' --> '<='
+ else if ((t0.type == lexertk::token::e_lt) && (t1.type == lexertk::token::e_eq))
+ {
+ t.type = lexertk::token::e_lte;
+ t.value = "<=";
+ t.position = t0.position;
+
+ return true;
+ }
+ //'= =' --> '=='
+ else if ((t0.type == lexertk::token::e_eq) && (t1.type == lexertk::token::e_eq))
+ {
+ t.type = lexertk::token::e_eq;
+ t.value = "==";
+ t.position = t0.position;
+
+ return true;
+ }
+ //'! =' --> '!='
+ else if ((static_cast<char>(t0.type) == '!') && (t1.type == lexertk::token::e_eq))
+ {
+ t.type = lexertk::token::e_ne;
+ t.value = "!=";
+ t.position = t0.position;
+
+ return true;
+ }
+ //'< >' --> '<>'
+ else if ((t0.type == lexertk::token::e_lt) && (t1.type == lexertk::token::e_gt))
+ {
+ t.type = lexertk::token::e_ne;
+ t.value = "<>";
+ t.position = t0.position;
+
+ return true;
+ }
+ else
+ return false;
+ }
+ };
+
+ class bracket_checker : public token_scanner
+ {
+ public:
+
+ bracket_checker()
+ : token_scanner(1),
+ state_(true)
+ {}
+
+ bool result()
+ {
+ return state_ && stack_.empty();
+ }
+
+ lexertk::token error_token()
+ {
+ return error_token_;
+ }
+
+ void reset()
+ {
+ //why? because msvc doesn't support swap properly.
+ stack_ = std::stack<char>();
+ state_ = true;
+ error_token_.clear();
+ }
+
+ bool operator()(const lexertk::token& t)
+ {
+ if (
+ !t.value.empty() &&
+ (lexertk::token::e_string != t.type) &&
+ (lexertk::token::e_symbol != t.type) &&
+ details::is_bracket(t.value[0])
+ )
+ {
+ char c = t.value[0];
+
+ if (t.type == lexertk::token::e_lbracket) stack_.push(')');
+ else if (t.type == lexertk::token::e_lcrlbracket) stack_.push('}');
+ else if (t.type == lexertk::token::e_lsqrbracket) stack_.push(']');
+ else if (details::is_right_bracket(c))
+ {
+ if (stack_.empty())
+ {
+ state_ = false;
+ error_token_ = t;
+
+ return false;
+ }
+ else if (c != stack_.top())
+ {
+ state_ = false;
+ error_token_ = t;
+
+ return false;
+ }
+ else
+ stack_.pop();
+ }
+ }
+
+ return true;
+ }
+
+ private:
+
+ bool state_;
+ std::stack<char> stack_;
+ lexertk::token error_token_;
+ };
+
+ class symbol_replacer : public token_modifier
+ {
+ private:
+
+ typedef std::map<std::string,std::pair<std::string,token::token_type>,details::ilesscompare> replace_map_t;
+
+ public:
+
+ bool remove(const std::string& target_symbol)
+ {
+ replace_map_t::iterator itr = replace_map_.find(target_symbol);
+
+ if (replace_map_.end() == itr)
+ return false;
+
+ replace_map_.erase(itr);
+
+ return true;
+ }
+
+ bool add_replace(const std::string& target_symbol,
+ const std::string& replace_symbol,
+ const lexertk::token::token_type token_type = lexertk::token::e_symbol)
+ {
+ replace_map_t::iterator itr = replace_map_.find(target_symbol);
+
+ if (replace_map_.end() != itr)
+ {
+ return false;
+ }
+
+ replace_map_[target_symbol] = std::make_pair(replace_symbol,token_type);
+
+ return true;
+ }
+
+ void clear()
+ {
+ replace_map_.clear();
+ }
+
+ private:
+
+ bool modify(lexertk::token& t)
+ {
+ if (lexertk::token::e_symbol == t.type)
+ {
+ if (replace_map_.empty())
+ return false;
+
+ replace_map_t::iterator itr = replace_map_.find(t.value);
+
+ if (replace_map_.end() != itr)
+ {
+ t.value = itr->second.first;
+ t.type = itr->second.second;
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ replace_map_t replace_map_;
+ };
+
+ class sequence_validator : public token_scanner
+ {
+ private:
+
+ typedef std::pair<lexertk::token::token_type,lexertk::token::token_type> token_pair_t;
+ typedef std::set<token_pair_t> set_t;
+
+ public:
+
+ sequence_validator()
+ : lexertk::token_scanner(2)
+ {
+ add_invalid(lexertk::token::e_number,lexertk::token::e_number);
+ add_invalid(lexertk::token::e_string,lexertk::token::e_string);
+ add_invalid(lexertk::token::e_number,lexertk::token::e_string);
+ add_invalid(lexertk::token::e_string,lexertk::token::e_number);
+ add_invalid(lexertk::token::e_string,lexertk::token::e_colon);
+ add_invalid(lexertk::token::e_colon,lexertk::token::e_string);
+ add_invalid_set1(lexertk::token::e_assign);
+ add_invalid_set1(lexertk::token::e_shr );
+ add_invalid_set1(lexertk::token::e_shl );
+ add_invalid_set1(lexertk::token::e_lte );
+ add_invalid_set1(lexertk::token::e_ne );
+ add_invalid_set1(lexertk::token::e_gte );
+ add_invalid_set1(lexertk::token::e_lt );
+ add_invalid_set1(lexertk::token::e_gt );
+ add_invalid_set1(lexertk::token::e_eq );
+ add_invalid_set1(lexertk::token::e_comma );
+ add_invalid_set1(lexertk::token::e_add );
+ add_invalid_set1(lexertk::token::e_sub );
+ add_invalid_set1(lexertk::token::e_div );
+ add_invalid_set1(lexertk::token::e_mul );
+ add_invalid_set1(lexertk::token::e_mod );
+ add_invalid_set1(lexertk::token::e_pow );
+ add_invalid_set1(lexertk::token::e_colon );
+ }
+
+ bool result()
+ {
+ return error_list_.empty();
+ }
+
+ bool operator()(const lexertk::token& t0, const lexertk::token& t1)
+ {
+ set_t::value_type p = std::make_pair(t0.type,t1.type);
+
+ if (invalid_bracket_check(t0.type,t1.type))
+ {
+ error_list_.push_back(std::make_pair(t0,t1));
+ }
+ else if (invalid_comb_.find(p) != invalid_comb_.end())
+ error_list_.push_back(std::make_pair(t0,t1));
+
+ return true;
+ }
+
+ std::size_t error_count()
+ {
+ return error_list_.size();
+ }
+
+ std::pair<lexertk::token,lexertk::token> error(const std::size_t index)
+ {
+ if (index < error_list_.size())
+ {
+ return error_list_[index];
+ }
+ else
+ {
+ static const lexertk::token error_token;
+ return std::make_pair(error_token,error_token);
+ }
+ }
+
+ void clear_errors()
+ {
+ error_list_.clear();
+ }
+
+ private:
+
+ void add_invalid(lexertk::token::token_type base, lexertk::token::token_type t)
+ {
+ invalid_comb_.insert(std::make_pair(base,t));
+ }
+
+ void add_invalid_set1(lexertk::token::token_type t)
+ {
+ add_invalid(t,lexertk::token::e_assign);
+ add_invalid(t,lexertk::token::e_shr );
+ add_invalid(t,lexertk::token::e_shl );
+ add_invalid(t,lexertk::token::e_lte );
+ add_invalid(t,lexertk::token::e_ne );
+ add_invalid(t,lexertk::token::e_gte );
+ add_invalid(t,lexertk::token::e_lt );
+ add_invalid(t,lexertk::token::e_gt );
+ add_invalid(t,lexertk::token::e_eq );
+ add_invalid(t,lexertk::token::e_comma );
+ add_invalid(t,lexertk::token::e_div );
+ add_invalid(t,lexertk::token::e_mul );
+ add_invalid(t,lexertk::token::e_mod );
+ add_invalid(t,lexertk::token::e_pow );
+ add_invalid(t,lexertk::token::e_colon );
+ }
+
+ bool invalid_bracket_check(lexertk::token::token_type base, lexertk::token::token_type t)
+ {
+ if (details::is_right_bracket(static_cast<char>(base)))
+ {
+ switch (t)
+ {
+ case lexertk::token::e_string : return true;
+ case lexertk::token::e_assign : return true;
+ default : return false;
+ }
+ }
+ else if (details::is_left_bracket(static_cast<char>(base)))
+ {
+ if (details::is_right_bracket(static_cast<char>(t)))
+ return false;
+ else if (details::is_left_bracket(static_cast<char>(t)))
+ return false;
+ else
+ {
+ switch (t)
+ {
+ case lexertk::token::e_number : return false;
+ case lexertk::token::e_symbol : return false;
+ case lexertk::token::e_string : return false;
+ case lexertk::token::e_add : return false;
+ case lexertk::token::e_sub : return false;
+ case lexertk::token::e_colon : return false;
+ default : return true;
+ }
+ }
+ }
+ else if (details::is_right_bracket(static_cast<char>(t)))
+ {
+ switch (base)
+ {
+ case lexertk::token::e_number : return false;
+ case lexertk::token::e_symbol : return false;
+ case lexertk::token::e_string : return false;
+ case lexertk::token::e_eof : return false;
+ case lexertk::token::e_colon : return false;
+ default : return true;
+ }
+ }
+ else if (details::is_left_bracket(static_cast<char>(t)))
+ {
+ switch (base)
+ {
+ case lexertk::token::e_rbracket : return true;
+ case lexertk::token::e_rsqrbracket : return true;
+ case lexertk::token::e_rcrlbracket : return true;
+ default : return false;
+ }
+ }
+
+ return false;
+ }
+
+ set_t invalid_comb_;
+ std::deque<std::pair<lexertk::token,lexertk::token> > error_list_;
+
+ };
+
+ struct helper_assembly
+ {
+ inline bool register_scanner(lexertk::token_scanner* scanner)
+ {
+ if (token_scanner_list.end() != std::find(token_scanner_list.begin(),
+ token_scanner_list.end(),
+ scanner))
+ {
+ return false;
+ }
+
+ token_scanner_list.push_back(scanner);
+
+ return true;
+ }
+
+ inline bool register_modifier(lexertk::token_modifier* modifier)
+ {
+ if (token_modifier_list.end() != std::find(token_modifier_list.begin(),
+ token_modifier_list.end(),
+ modifier))
+ {
+ return false;
+ }
+
+ token_modifier_list.push_back(modifier);
+
+ return true;
+ }
+
+ inline bool register_joiner(lexertk::token_joiner* joiner)
+ {
+ if (token_joiner_list.end() != std::find(token_joiner_list.begin(),
+ token_joiner_list.end(),
+ joiner))
+ {
+ return false;
+ }
+
+ token_joiner_list.push_back(joiner);
+
+ return true;
+ }
+
+ inline bool register_inserter(lexertk::token_inserter* inserter)
+ {
+ if (token_inserter_list.end() != std::find(token_inserter_list.begin(),
+ token_inserter_list.end(),
+ inserter))
+ {
+ return false;
+ }
+
+ token_inserter_list.push_back(inserter);
+
+ return true;
+ }
+
+ inline bool run_modifiers(lexertk::generator& g)
+ {
+ error_token_modifier = reinterpret_cast<lexertk::token_modifier*>(0);
+
+ for (std::size_t i = 0; i < token_modifier_list.size(); ++i)
+ {
+ lexertk::token_modifier& modifier = (*token_modifier_list[i]);
+
+ modifier.reset();
+ modifier.process(g);
+
+ if (!modifier.result())
+ {
+ error_token_modifier = token_modifier_list[i];
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline bool run_joiners(lexertk::generator& g)
+ {
+ error_token_joiner = reinterpret_cast<lexertk::token_joiner*>(0);
+
+ for (std::size_t i = 0; i < token_joiner_list.size(); ++i)
+ {
+ lexertk::token_joiner& joiner = (*token_joiner_list[i]);
+
+ joiner.reset();
+ joiner.process(g);
+
+ if (!joiner.result())
+ {
+ error_token_joiner = token_joiner_list[i];
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline bool run_inserters(lexertk::generator& g)
+ {
+ error_token_inserter = reinterpret_cast<lexertk::token_inserter*>(0);
+
+ for (std::size_t i = 0; i < token_inserter_list.size(); ++i)
+ {
+ lexertk::token_inserter& inserter = (*token_inserter_list[i]);
+
+ inserter.reset();
+ inserter.process(g);
+
+ if (!inserter.result())
+ {
+ error_token_inserter = token_inserter_list[i];
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline bool run_scanners(lexertk::generator& g)
+ {
+ error_token_scanner = reinterpret_cast<lexertk::token_scanner*>(0);
+
+ for (std::size_t i = 0; i < token_scanner_list.size(); ++i)
+ {
+ lexertk::token_scanner& scanner = (*token_scanner_list[i]);
+
+ scanner.reset();
+ scanner.process(g);
+
+ if (!scanner.result())
+ {
+ error_token_scanner = token_scanner_list[i];
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ std::deque<lexertk::token_scanner*> token_scanner_list;
+ std::deque<lexertk::token_modifier*> token_modifier_list;
+ std::deque<lexertk::token_joiner*> token_joiner_list;
+ std::deque<lexertk::token_inserter*> token_inserter_list;
+
+ lexertk::token_scanner* error_token_scanner;
+ lexertk::token_modifier* error_token_modifier;
+ lexertk::token_joiner* error_token_joiner;
+ lexertk::token_inserter* error_token_inserter;
+ };
+ }
+
+ class parser_helper
+ {
+ public:
+
+ typedef token token_t;
+ typedef generator generator_t;
+
+ inline bool init(const std::string& str)
+ {
+ if (!lexer_.process(str))
+ {
+ return false;
+ }
+
+ lexer_.begin();
+
+ next_token();
+
+ return true;
+ }
+
+ inline generator_t& lexer()
+ {
+ return lexer_;
+ }
+
+ inline const generator_t& lexer() const
+ {
+ return lexer_;
+ }
+
+ inline void next_token()
+ {
+ current_token_ = lexer_.next_token();
+ }
+
+ inline const token_t& current_token() const
+ {
+ return current_token_;
+ }
+
+ enum token_advance_mode
+ {
+ e_hold = 0,
+ e_advance = 1
+ };
+
+ inline void advance_token(const token_advance_mode mode)
+ {
+ if (e_advance == mode)
+ {
+ next_token();
+ }
+ }
+
+ inline bool token_is(const token_t::token_type& ttype, const token_advance_mode mode = e_advance)
+ {
+ if (current_token().type != ttype)
+ {
+ return false;
+ }
+
+ advance_token(mode);
+
+ return true;
+ }
+
+ inline bool token_is(const token_t::token_type& ttype,
+ const std::string& value,
+ const token_advance_mode mode = e_advance)
+ {
+ if (
+ (current_token().type != ttype) ||
+ !details::imatch(value,current_token().value)
+ )
+ {
+ return false;
+ }
+
+ advance_token(mode);
+
+ return true;
+ }
+
+ inline bool token_is_then_assign(const token_t::token_type& ttype,
+ std::string& token,
+ const token_advance_mode mode = e_advance)
+ {
+ if (current_token_.type != ttype)
+ {
+ return false;
+ }
+
+ token = current_token_.value;
+
+ advance_token(mode);
+
+ return true;
+ }
+
+ template <typename Allocator,
+ template <typename,typename> class Container>
+ inline bool token_is_then_assign(const token_t::token_type& ttype,
+ Container<std::string,Allocator>& token_list,
+ const token_advance_mode mode = e_advance)
+ {
+ if (current_token_.type != ttype)
+ {
+ return false;
+ }
+
+ token_list.push_back(current_token_.value);
+
+ advance_token(mode);
+
+ return true;
+ }
+
+ inline bool peek_token_is(const token_t::token_type& ttype)
+ {
+ return (lexer_.peek_next_token().type == ttype);
+ }
+
+ inline bool peek_token_is(const std::string& s)
+ {
+ return (details::imatch(lexer_.peek_next_token().value,s));
+ }
+
+ private:
+
+ generator_t lexer_;
+ token_t current_token_;
+ };
+#endif
+} // namespace lexertk
+
+#endif
diff --git a/contrib/libs/jinja2cpp/src/out_stream.h b/contrib/libs/jinja2cpp/src/out_stream.h
new file mode 100644
index 0000000000..8e3f001ba9
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/out_stream.h
@@ -0,0 +1,43 @@
+#ifndef JINJA2CPP_SRC_OUT_STREAM_H
+#define JINJA2CPP_SRC_OUT_STREAM_H
+
+#include "internal_value.h"
+
+#include <functional>
+#include <iostream>
+#include <sstream>
+
+namespace jinja2
+{
+class OutStream
+{
+public:
+ struct StreamWriter
+ {
+ virtual ~StreamWriter() {}
+
+ virtual void WriteBuffer(const void* ptr, size_t length) = 0;
+ virtual void WriteValue(const InternalValue &val) = 0;
+ };
+
+ OutStream(std::function<StreamWriter*()> writerGetter)
+ : m_writerGetter(std::move(writerGetter))
+ {}
+
+ void WriteBuffer(const void* ptr, size_t length)
+ {
+ m_writerGetter()->WriteBuffer(ptr, length);
+ }
+
+ void WriteValue(const InternalValue& val)
+ {
+ m_writerGetter()->WriteValue(val);
+ }
+
+private:
+ std::function<StreamWriter*()> m_writerGetter;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_OUT_STREAM_H
diff --git a/contrib/libs/jinja2cpp/src/render_context.h b/contrib/libs/jinja2cpp/src/render_context.h
new file mode 100644
index 0000000000..f6edcf444e
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/render_context.h
@@ -0,0 +1,182 @@
+#ifndef JINJA2CPP_SRC_RENDER_CONTEXT_H
+#define JINJA2CPP_SRC_RENDER_CONTEXT_H
+
+#include "internal_value.h"
+#include <jinja2cpp/error_info.h>
+#include <jinja2cpp/utils/i_comparable.h>
+
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+
+#include <list>
+#include <deque>
+
+namespace jinja2
+{
+template<typename CharT>
+class TemplateImpl;
+
+struct IRendererCallback : IComparable
+{
+ virtual ~IRendererCallback() {}
+ virtual TargetString GetAsTargetString(const InternalValue& val) = 0;
+ virtual OutStream GetStreamOnString(TargetString& str) = 0;
+ virtual std::variant<EmptyValue,
+ nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
+ nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const = 0;
+ virtual std::variant<EmptyValue,
+ nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
+ nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const InternalValue& fileName) const = 0;
+ virtual void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) = 0;
+};
+
+class RenderContext
+{
+public:
+ RenderContext(const InternalValueMap& extValues, const InternalValueMap& globalValues, IRendererCallback* rendererCallback)
+ : m_rendererCallback(rendererCallback)
+ {
+ m_externalScope = &extValues;
+ m_globalScope = &globalValues;
+ EnterScope();
+ (*m_currentScope)["self"] = CreateMapAdapter(InternalValueMap());
+ }
+
+ RenderContext(const RenderContext& other)
+ : m_rendererCallback(other.m_rendererCallback)
+ , m_externalScope(other.m_externalScope)
+ , m_globalScope(other.m_globalScope)
+ , m_boundScope(other.m_boundScope)
+ , m_scopes(other.m_scopes)
+ {
+ m_currentScope = &m_scopes.back();
+ }
+
+ InternalValueMap& EnterScope()
+ {
+ m_scopes.push_back(InternalValueMap());
+ m_currentScope = &m_scopes.back();
+ return *m_currentScope;
+ }
+
+ void ExitScope()
+ {
+ m_scopes.pop_back();
+ if (!m_scopes.empty())
+ m_currentScope = &m_scopes.back();
+ else
+ m_currentScope = nullptr;
+ }
+
+ auto FindValue(const std::string& val, bool& found) const
+ {
+ auto finder = [&val, &found](auto& map) mutable
+ {
+ auto p = map.find(val);
+ if (p != map.end())
+ found = true;
+
+ return p;
+ };
+
+ if (m_boundScope)
+ {
+ auto valP = finder(*m_boundScope);
+ if (found)
+ return valP;
+ }
+
+ for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
+ {
+ auto valP = finder(*p);
+ if (found)
+ return valP;
+ }
+
+ auto valP = finder(*m_externalScope);
+ if (found)
+ return valP;
+
+ return finder(*m_globalScope);
+ }
+
+ auto& GetCurrentScope() const
+ {
+ return *m_currentScope;
+ }
+
+ auto& GetCurrentScope()
+ {
+ return *m_currentScope;
+ }
+ auto& GetGlobalScope()
+ {
+ return m_scopes.front();
+ }
+ auto GetRendererCallback()
+ {
+ return m_rendererCallback;
+ }
+ RenderContext Clone(bool includeCurrentContext) const
+ {
+ if (!includeCurrentContext)
+ return RenderContext(m_emptyScope, *m_globalScope, m_rendererCallback);
+
+ return RenderContext(*this);
+ }
+
+ void BindScope(InternalValueMap* scope)
+ {
+ m_boundScope = scope;
+ }
+
+ bool IsEqual(const RenderContext& other) const
+ {
+ if (!IsEqual(m_rendererCallback, other.m_rendererCallback))
+ return false;
+ if (!IsEqual(this->m_currentScope, other.m_currentScope))
+ return false;
+ if (!IsEqual(m_externalScope, other.m_externalScope))
+ return false;
+ if (!IsEqual(m_globalScope, other.m_globalScope))
+ return false;
+ if (!IsEqual(m_boundScope, other.m_boundScope))
+ return false;
+ if (m_emptyScope != other.m_emptyScope)
+ return false;
+ if (m_scopes != other.m_scopes)
+ return false;
+ return true;
+ }
+
+private:
+
+ bool IsEqual(const IRendererCallback* lhs, const IRendererCallback* rhs) const
+ {
+ if (lhs && rhs)
+ return lhs->IsEqual(*rhs);
+ if ((!lhs && rhs) || (lhs && !rhs))
+ return false;
+ return true;
+ }
+
+ bool IsEqual(const InternalValueMap* lhs, const InternalValueMap* rhs) const
+ {
+ if (lhs && rhs)
+ return *lhs == *rhs;
+ if ((!lhs && rhs) || (lhs && !rhs))
+ return false;
+ return true;
+ }
+
+private:
+ IRendererCallback* m_rendererCallback{};
+ InternalValueMap* m_currentScope{};
+ const InternalValueMap* m_externalScope{};
+ const InternalValueMap* m_globalScope{};
+ const InternalValueMap* m_boundScope{};
+ InternalValueMap m_emptyScope;
+ std::deque<InternalValueMap> m_scopes;
+};
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_RENDER_CONTEXT_H
diff --git a/contrib/libs/jinja2cpp/src/renderer.h b/contrib/libs/jinja2cpp/src/renderer.h
new file mode 100644
index 0000000000..9c08d2379b
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/renderer.h
@@ -0,0 +1,131 @@
+#ifndef JINJA2CPP_SRC_RENDERER_H
+#define JINJA2CPP_SRC_RENDERER_H
+
+#include "out_stream.h"
+#include "lexertk.h"
+#include "expression_evaluator.h"
+#include "render_context.h"
+#include "ast_visitor.h"
+
+#include <jinja2cpp/value.h>
+#include <jinja2cpp/utils/i_comparable.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace jinja2
+{
+class IRendererBase : public virtual IComparable
+{
+public:
+ virtual ~IRendererBase() = default;
+ virtual void Render(OutStream& os, RenderContext& values) = 0;
+};
+
+class VisitableRendererBase : public IRendererBase, public VisitableStatement
+{
+};
+
+using RendererPtr = std::shared_ptr<IRendererBase>;
+
+inline bool operator==(const RendererPtr& lhs, const RendererPtr& rhs)
+{
+ if (lhs && rhs && !lhs->IsEqual(*rhs))
+ return false;
+ if ((lhs && !rhs) || (!lhs && rhs))
+ return false;
+ return true;
+}
+
+inline bool operator!=(const RendererPtr& lhs, const RendererPtr& rhs)
+{
+ return !(lhs == rhs);
+}
+
+class ComposedRenderer : public VisitableRendererBase
+{
+public:
+ VISITABLE_STATEMENT();
+
+ void AddRenderer(RendererPtr r)
+ {
+ m_renderers.push_back(std::move(r));
+ }
+ void Render(OutStream& os, RenderContext& values) override
+ {
+ for (auto& r : m_renderers)
+ r->Render(os, values);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ComposedRenderer*>(&other);
+ if (!val)
+ return false;
+ return m_renderers == val->m_renderers;
+ }
+
+private:
+ std::vector<RendererPtr> m_renderers;
+};
+
+class RawTextRenderer : public VisitableRendererBase
+{
+public:
+ VISITABLE_STATEMENT();
+
+ RawTextRenderer(const void* ptr, size_t len)
+ : m_ptr(ptr)
+ , m_length(len)
+ {
+ }
+
+ void Render(OutStream& os, RenderContext&) override
+ {
+ os.WriteBuffer(m_ptr, m_length);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const RawTextRenderer*>(&other);
+ if (!val)
+ return false;
+ if (m_ptr != val->m_ptr)
+ return false;
+ return m_length == val->m_length;
+ }
+private:
+ const void* m_ptr{};
+ size_t m_length{};
+};
+
+class ExpressionRenderer : public VisitableRendererBase
+{
+public:
+ VISITABLE_STATEMENT();
+
+ explicit ExpressionRenderer(ExpressionEvaluatorPtr<> expr)
+ : m_expression(std::move(expr))
+ {
+ }
+
+ void Render(OutStream& os, RenderContext& values) override
+ {
+ m_expression->Render(os, values);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ExpressionRenderer*>(&other);
+ if (!val)
+ return false;
+ return m_expression == val->m_expression;
+ }
+private:
+ ExpressionEvaluatorPtr<> m_expression;
+};
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_RENDERER_H
diff --git a/contrib/libs/jinja2cpp/src/robin_hood.h b/contrib/libs/jinja2cpp/src/robin_hood.h
new file mode 100644
index 0000000000..b4e0fbc56a
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/robin_hood.h
@@ -0,0 +1,2544 @@
+// ______ _____ ______ _________
+// ______________ ___ /_ ___(_)_______ ___ /_ ______ ______ ______ /
+// __ ___/_ __ \__ __ \__ / __ __ \ __ __ \_ __ \_ __ \_ __ /
+// _ / / /_/ /_ /_/ /_ / _ / / / _ / / // /_/ // /_/ // /_/ /
+// /_/ \____/ /_.___/ /_/ /_/ /_/ ________/_/ /_/ \____/ \____/ \__,_/
+// _/_____/
+//
+// Fast & memory efficient hashtable based on robin hood hashing for C++11/14/17/20
+// https://github.com/martinus/robin-hood-hashing
+//
+// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2018-2021 Martin Ankerl <http://martin.ankerl.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#ifndef ROBIN_HOOD_H_INCLUDED
+#define ROBIN_HOOD_H_INCLUDED
+
+// see https://semver.org/
+#define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes
+#define ROBIN_HOOD_VERSION_MINOR 11 // for adding functionality in a backwards-compatible manner
+#define ROBIN_HOOD_VERSION_PATCH 5 // for backwards-compatible bug fixes
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <limits>
+#include <memory> // only to support hash of smart pointers
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#if __cplusplus >= 201703L
+# include <string_view>
+#endif
+
+// #define ROBIN_HOOD_LOG_ENABLED
+#ifdef ROBIN_HOOD_LOG_ENABLED
+# include <iostream>
+# define ROBIN_HOOD_LOG(...) \
+ std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << __VA_ARGS__ << std::endl;
+#else
+# define ROBIN_HOOD_LOG(x)
+#endif
+
+// #define ROBIN_HOOD_TRACE_ENABLED
+#ifdef ROBIN_HOOD_TRACE_ENABLED
+# include <iostream>
+# define ROBIN_HOOD_TRACE(...) \
+ std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << __VA_ARGS__ << std::endl;
+#else
+# define ROBIN_HOOD_TRACE(x)
+#endif
+
+// #define ROBIN_HOOD_COUNT_ENABLED
+#ifdef ROBIN_HOOD_COUNT_ENABLED
+# include <iostream>
+# define ROBIN_HOOD_COUNT(x) ++counts().x;
+namespace robin_hood {
+struct Counts {
+ uint64_t shiftUp{};
+ uint64_t shiftDown{};
+};
+inline std::ostream& operator<<(std::ostream& os, Counts const& c) {
+ return os << c.shiftUp << " shiftUp" << std::endl << c.shiftDown << " shiftDown" << std::endl;
+}
+
+static Counts& counts() {
+ static Counts counts{};
+ return counts;
+}
+} // namespace robin_hood
+#else
+# define ROBIN_HOOD_COUNT(x)
+#endif
+
+// all non-argument macros should use this facility. See
+// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/
+#define ROBIN_HOOD(x) ROBIN_HOOD_PRIVATE_DEFINITION_##x()
+
+// mark unused members with this macro
+#define ROBIN_HOOD_UNUSED(identifier)
+
+// bitness
+#if SIZE_MAX == UINT32_MAX
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 32
+#elif SIZE_MAX == UINT64_MAX
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 64
+#else
+# error Unsupported bitness
+#endif
+
+// endianess
+#ifdef _MSC_VER
+# define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() 1
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() 0
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() \
+ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#endif
+
+// inline
+#ifdef _MSC_VER
+# define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __declspec(noinline)
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __attribute__((noinline))
+#endif
+
+// exceptions
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 0
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 1
+#endif
+
+// count leading/trailing bits
+#if !defined(ROBIN_HOOD_DISABLE_INTRINSICS)
+# ifdef _MSC_VER
+# if ROBIN_HOOD(BITNESS) == 32
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward
+# else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward64
+# endif
+# include <intrin.h>
+# pragma intrinsic(ROBIN_HOOD(BITSCANFORWARD))
+# define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x) \
+ [](size_t mask) noexcept -> int { \
+ unsigned long index; \
+ return ROBIN_HOOD(BITSCANFORWARD)(&index, mask) ? static_cast<int>(index) \
+ : ROBIN_HOOD(BITNESS); \
+ }(x)
+# else
+# if ROBIN_HOOD(BITNESS) == 32
+# define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzl
+# define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzl
+# else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzll
+# define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzll
+# endif
+# define ROBIN_HOOD_COUNT_LEADING_ZEROES(x) ((x) ? ROBIN_HOOD(CLZ)(x) : ROBIN_HOOD(BITNESS))
+# define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x) ((x) ? ROBIN_HOOD(CTZ)(x) : ROBIN_HOOD(BITNESS))
+# endif
+#endif
+
+// fallthrough
+#ifndef __has_cpp_attribute // For backwards compatibility
+# define __has_cpp_attribute(x) 0
+#endif
+#if __has_cpp_attribute(clang::fallthrough)
+# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[clang::fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[gnu::fallthrough]]
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH()
+#endif
+
+// likely/unlikely
+#ifdef _MSC_VER
+# define ROBIN_HOOD_LIKELY(condition) condition
+# define ROBIN_HOOD_UNLIKELY(condition) condition
+#else
+# define ROBIN_HOOD_LIKELY(condition) __builtin_expect(condition, 1)
+# define ROBIN_HOOD_UNLIKELY(condition) __builtin_expect(condition, 0)
+#endif
+
+// detect if native wchar_t type is availiable in MSVC
+#ifdef _MSC_VER
+# ifdef _NATIVE_WCHAR_T_DEFINED
+# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 1
+# else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 0
+# endif
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 1
+#endif
+
+// detect if MSVC supports the pair(std::piecewise_construct_t,...) consructor being constexpr
+#ifdef _MSC_VER
+# if _MSC_VER <= 1900
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 1
+# else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 0
+# endif
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 0
+#endif
+
+// workaround missing "is_trivially_copyable" in g++ < 5.0
+// See https://stackoverflow.com/a/31798726/48181
+#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
+# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
+#else
+# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
+#endif
+
+// helpers for C++ versions, see https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX() __cplusplus
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX98() 199711L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX11() 201103L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX14() 201402L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX17() 201703L
+
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX17)
+# define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD() [[nodiscard]]
+#else
+# define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD()
+#endif
+
+namespace robin_hood {
+
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14)
+# define ROBIN_HOOD_STD std
+#else
+
+// c++11 compatibility layer
+namespace ROBIN_HOOD_STD {
+template <class T>
+struct alignment_of
+ : std::integral_constant<std::size_t, alignof(typename std::remove_all_extents<T>::type)> {};
+
+template <class T, T... Ints>
+class integer_sequence {
+public:
+ using value_type = T;
+ static_assert(std::is_integral<value_type>::value, "not integral type");
+ static constexpr std::size_t size() noexcept {
+ return sizeof...(Ints);
+ }
+};
+template <std::size_t... Inds>
+using index_sequence = integer_sequence<std::size_t, Inds...>;
+
+namespace detail_ {
+template <class T, T Begin, T End, bool>
+struct IntSeqImpl {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0 && Begin < End, "unexpected argument (Begin<0 || Begin<=End)");
+
+ template <class, class>
+ struct IntSeqCombiner;
+
+ template <TValue... Inds0, TValue... Inds1>
+ struct IntSeqCombiner<integer_sequence<TValue, Inds0...>, integer_sequence<TValue, Inds1...>> {
+ using TResult = integer_sequence<TValue, Inds0..., Inds1...>;
+ };
+
+ using TResult =
+ typename IntSeqCombiner<typename IntSeqImpl<TValue, Begin, Begin + (End - Begin) / 2,
+ (End - Begin) / 2 == 1>::TResult,
+ typename IntSeqImpl<TValue, Begin + (End - Begin) / 2, End,
+ (End - Begin + 1) / 2 == 1>::TResult>::TResult;
+};
+
+template <class T, T Begin>
+struct IntSeqImpl<T, Begin, Begin, false> {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+ using TResult = integer_sequence<TValue>;
+};
+
+template <class T, T Begin, T End>
+struct IntSeqImpl<T, Begin, End, true> {
+ using TValue = T;
+ static_assert(std::is_integral<TValue>::value, "not integral type");
+ static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+ using TResult = integer_sequence<TValue, Begin>;
+};
+} // namespace detail_
+
+template <class T, T N>
+using make_integer_sequence = typename detail_::IntSeqImpl<T, 0, N, (N - 0) == 1>::TResult;
+
+template <std::size_t N>
+using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+template <class... T>
+using index_sequence_for = make_index_sequence<sizeof...(T)>;
+
+} // namespace ROBIN_HOOD_STD
+
+#endif
+
+namespace detail {
+
+// make sure we static_cast to the correct type for hash_int
+#if ROBIN_HOOD(BITNESS) == 64
+using SizeT = uint64_t;
+#else
+using SizeT = uint32_t;
+#endif
+
+template <typename T>
+T rotr(T x, unsigned k) {
+ return (x >> k) | (x << (8U * sizeof(T) - k));
+}
+
+// This cast gets rid of warnings like "cast from 'uint8_t*' {aka 'unsigned char*'} to
+// 'uint64_t*' {aka 'long unsigned int*'} increases required alignment of target type". Use with
+// care!
+template <typename T>
+inline T reinterpret_cast_no_cast_align_warning(void* ptr) noexcept {
+ return reinterpret_cast<T>(ptr);
+}
+
+template <typename T>
+inline T reinterpret_cast_no_cast_align_warning(void const* ptr) noexcept {
+ return reinterpret_cast<T>(ptr);
+}
+
+// make sure this is not inlined as it is slow and dramatically enlarges code, thus making other
+// inlinings more difficult. Throws are also generally the slow path.
+template <typename E, typename... Args>
+[[noreturn]] ROBIN_HOOD(NOINLINE)
+#if ROBIN_HOOD(HAS_EXCEPTIONS)
+ void doThrow(Args&&... args) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
+ throw E(std::forward<Args>(args)...);
+}
+#else
+ void doThrow(Args&&... ROBIN_HOOD_UNUSED(args) /*unused*/) {
+ abort();
+}
+#endif
+
+template <typename E, typename T, typename... Args>
+T* assertNotNull(T* t, Args&&... args) {
+ if (ROBIN_HOOD_UNLIKELY(nullptr == t)) {
+ doThrow<E>(std::forward<Args>(args)...);
+ }
+ return t;
+}
+
+template <typename T>
+inline T unaligned_load(void const* ptr) noexcept {
+ // using memcpy so we don't get into unaligned load problems.
+ // compiler should optimize this very well anyways.
+ T t;
+ std::memcpy(&t, ptr, sizeof(T));
+ return t;
+}
+
+// Allocates bulks of memory for objects of type T. This deallocates the memory in the destructor,
+// and keeps a linked list of the allocated memory around. Overhead per allocation is the size of a
+// pointer.
+template <typename T, size_t MinNumAllocs = 4, size_t MaxNumAllocs = 256>
+class BulkPoolAllocator {
+public:
+ BulkPoolAllocator() noexcept = default;
+
+ // does not copy anything, just creates a new allocator.
+ BulkPoolAllocator(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept
+ : mHead(nullptr)
+ , mListForFree(nullptr) {}
+
+ BulkPoolAllocator(BulkPoolAllocator&& o) noexcept
+ : mHead(o.mHead)
+ , mListForFree(o.mListForFree) {
+ o.mListForFree = nullptr;
+ o.mHead = nullptr;
+ }
+
+ BulkPoolAllocator& operator=(BulkPoolAllocator&& o) noexcept {
+ reset();
+ mHead = o.mHead;
+ mListForFree = o.mListForFree;
+ o.mListForFree = nullptr;
+ o.mHead = nullptr;
+ return *this;
+ }
+
+ BulkPoolAllocator&
+ // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
+ operator=(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept {
+ // does not do anything
+ return *this;
+ }
+
+ ~BulkPoolAllocator() noexcept {
+ reset();
+ }
+
+ // Deallocates all allocated memory.
+ void reset() noexcept {
+ while (mListForFree) {
+ T* tmp = *mListForFree;
+ ROBIN_HOOD_LOG("std::free")
+ std::free(mListForFree);
+ mListForFree = reinterpret_cast_no_cast_align_warning<T**>(tmp);
+ }
+ mHead = nullptr;
+ }
+
+ // allocates, but does NOT initialize. Use in-place new constructor, e.g.
+ // T* obj = pool.allocate();
+ // ::new (static_cast<void*>(obj)) T();
+ T* allocate() {
+ T* tmp = mHead;
+ if (!tmp) {
+ tmp = performAllocation();
+ }
+
+ mHead = *reinterpret_cast_no_cast_align_warning<T**>(tmp);
+ return tmp;
+ }
+
+ // does not actually deallocate but puts it in store.
+ // make sure you have already called the destructor! e.g. with
+ // obj->~T();
+ // pool.deallocate(obj);
+ void deallocate(T* obj) noexcept {
+ *reinterpret_cast_no_cast_align_warning<T**>(obj) = mHead;
+ mHead = obj;
+ }
+
+ // Adds an already allocated block of memory to the allocator. This allocator is from now on
+ // responsible for freeing the data (with free()). If the provided data is not large enough to
+ // make use of, it is immediately freed. Otherwise it is reused and freed in the destructor.
+ void addOrFree(void* ptr, const size_t numBytes) noexcept {
+ // calculate number of available elements in ptr
+ if (numBytes < ALIGNMENT + ALIGNED_SIZE) {
+ // not enough data for at least one element. Free and return.
+ ROBIN_HOOD_LOG("std::free")
+ std::free(ptr);
+ } else {
+ ROBIN_HOOD_LOG("add to buffer")
+ add(ptr, numBytes);
+ }
+ }
+
+ void swap(BulkPoolAllocator<T, MinNumAllocs, MaxNumAllocs>& other) noexcept {
+ using std::swap;
+ swap(mHead, other.mHead);
+ swap(mListForFree, other.mListForFree);
+ }
+
+private:
+ // iterates the list of allocated memory to calculate how many to alloc next.
+ // Recalculating this each time saves us a size_t member.
+ // This ignores the fact that memory blocks might have been added manually with addOrFree. In
+ // practice, this should not matter much.
+ ROBIN_HOOD(NODISCARD) size_t calcNumElementsToAlloc() const noexcept {
+ auto tmp = mListForFree;
+ size_t numAllocs = MinNumAllocs;
+
+ while (numAllocs * 2 <= MaxNumAllocs && tmp) {
+ auto x = reinterpret_cast<T***>(tmp);
+ tmp = *x;
+ numAllocs *= 2;
+ }
+
+ return numAllocs;
+ }
+
+ // WARNING: Underflow if numBytes < ALIGNMENT! This is guarded in addOrFree().
+ void add(void* ptr, const size_t numBytes) noexcept {
+ const size_t numElements = (numBytes - ALIGNMENT) / ALIGNED_SIZE;
+
+ auto data = reinterpret_cast<T**>(ptr);
+
+ // link free list
+ auto x = reinterpret_cast<T***>(data);
+ *x = mListForFree;
+ mListForFree = data;
+
+ // create linked list for newly allocated data
+ auto* const headT =
+ reinterpret_cast_no_cast_align_warning<T*>(reinterpret_cast<char*>(ptr) + ALIGNMENT);
+
+ auto* const head = reinterpret_cast<char*>(headT);
+
+ // Visual Studio compiler automatically unrolls this loop, which is pretty cool
+ for (size_t i = 0; i < numElements; ++i) {
+ *reinterpret_cast_no_cast_align_warning<char**>(head + i * ALIGNED_SIZE) =
+ head + (i + 1) * ALIGNED_SIZE;
+ }
+
+ // last one points to 0
+ *reinterpret_cast_no_cast_align_warning<T**>(head + (numElements - 1) * ALIGNED_SIZE) =
+ mHead;
+ mHead = headT;
+ }
+
+ // Called when no memory is available (mHead == 0).
+ // Don't inline this slow path.
+ ROBIN_HOOD(NOINLINE) T* performAllocation() {
+ size_t const numElementsToAlloc = calcNumElementsToAlloc();
+
+ // alloc new memory: [prev |T, T, ... T]
+ size_t const bytes = ALIGNMENT + ALIGNED_SIZE * numElementsToAlloc;
+ ROBIN_HOOD_LOG("std::malloc " << bytes << " = " << ALIGNMENT << " + " << ALIGNED_SIZE
+ << " * " << numElementsToAlloc)
+ add(assertNotNull<std::bad_alloc>(std::malloc(bytes)), bytes);
+ return mHead;
+ }
+
+ // enforce byte alignment of the T's
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14)
+ static constexpr size_t ALIGNMENT =
+ (std::max)(std::alignment_of<T>::value, std::alignment_of<T*>::value);
+#else
+ static const size_t ALIGNMENT =
+ (ROBIN_HOOD_STD::alignment_of<T>::value > ROBIN_HOOD_STD::alignment_of<T*>::value)
+ ? ROBIN_HOOD_STD::alignment_of<T>::value
+ : +ROBIN_HOOD_STD::alignment_of<T*>::value; // the + is for walkarround
+#endif
+
+ static constexpr size_t ALIGNED_SIZE = ((sizeof(T) - 1) / ALIGNMENT + 1) * ALIGNMENT;
+
+ static_assert(MinNumAllocs >= 1, "MinNumAllocs");
+ static_assert(MaxNumAllocs >= MinNumAllocs, "MaxNumAllocs");
+ static_assert(ALIGNED_SIZE >= sizeof(T*), "ALIGNED_SIZE");
+ static_assert(0 == (ALIGNED_SIZE % sizeof(T*)), "ALIGNED_SIZE mod");
+ static_assert(ALIGNMENT >= sizeof(T*), "ALIGNMENT");
+
+ T* mHead{nullptr};
+ T** mListForFree{nullptr};
+};
+
+template <typename T, size_t MinSize, size_t MaxSize, bool IsFlat>
+struct NodeAllocator;
+
+// dummy allocator that does nothing
+template <typename T, size_t MinSize, size_t MaxSize>
+struct NodeAllocator<T, MinSize, MaxSize, true> {
+
+ // we are not using the data, so just free it.
+ void addOrFree(void* ptr, size_t ROBIN_HOOD_UNUSED(numBytes) /*unused*/) noexcept {
+ ROBIN_HOOD_LOG("std::free")
+ std::free(ptr);
+ }
+};
+
+template <typename T, size_t MinSize, size_t MaxSize>
+struct NodeAllocator<T, MinSize, MaxSize, false> : public BulkPoolAllocator<T, MinSize, MaxSize> {};
+
+// c++14 doesn't have is_nothrow_swappable, and clang++ 6.0.1 doesn't like it either, so I'm making
+// my own here.
+namespace swappable {
+#if ROBIN_HOOD(CXX) < ROBIN_HOOD(CXX17)
+using std::swap;
+template <typename T>
+struct nothrow {
+ static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>()));
+};
+#else
+template <typename T>
+struct nothrow {
+ static const bool value = std::is_nothrow_swappable<T>::value;
+};
+#endif
+} // namespace swappable
+
+} // namespace detail
+
+struct is_transparent_tag {};
+
+// A custom pair implementation is used in the map because std::pair is not is_trivially_copyable,
+// which means it would not be allowed to be used in std::memcpy. This struct is copyable, which is
+// also tested.
+template <typename T1, typename T2>
+struct pair {
+ using first_type = T1;
+ using second_type = T2;
+
+ template <typename U1 = T1, typename U2 = T2,
+ typename = typename std::enable_if<std::is_default_constructible<U1>::value &&
+ std::is_default_constructible<U2>::value>::type>
+ constexpr pair() noexcept(noexcept(U1()) && noexcept(U2()))
+ : first()
+ , second() {}
+
+ // pair constructors are explicit so we don't accidentally call this ctor when we don't have to.
+ explicit constexpr pair(std::pair<T1, T2> const& o) noexcept(
+ noexcept(T1(std::declval<T1 const&>())) && noexcept(T2(std::declval<T2 const&>())))
+ : first(o.first)
+ , second(o.second) {}
+
+ // pair constructors are explicit so we don't accidentally call this ctor when we don't have to.
+ explicit constexpr pair(std::pair<T1, T2>&& o) noexcept(noexcept(
+ T1(std::move(std::declval<T1&&>()))) && noexcept(T2(std::move(std::declval<T2&&>()))))
+ : first(std::move(o.first))
+ , second(std::move(o.second)) {}
+
+ constexpr pair(T1&& a, T2&& b) noexcept(noexcept(
+ T1(std::move(std::declval<T1&&>()))) && noexcept(T2(std::move(std::declval<T2&&>()))))
+ : first(std::move(a))
+ , second(std::move(b)) {}
+
+ template <typename U1, typename U2>
+ constexpr pair(U1&& a, U2&& b) noexcept(noexcept(T1(std::forward<U1>(
+ std::declval<U1&&>()))) && noexcept(T2(std::forward<U2>(std::declval<U2&&>()))))
+ : first(std::forward<U1>(a))
+ , second(std::forward<U2>(b)) {}
+
+ template <typename... U1, typename... U2>
+ // MSVC 2015 produces error "C2476: ‘constexpr’ constructor does not initialize all members"
+ // if this constructor is constexpr
+#if !ROBIN_HOOD(BROKEN_CONSTEXPR)
+ constexpr
+#endif
+ pair(std::piecewise_construct_t /*unused*/, std::tuple<U1...> a,
+ std::tuple<U2...>
+ b) noexcept(noexcept(pair(std::declval<std::tuple<U1...>&>(),
+ std::declval<std::tuple<U2...>&>(),
+ ROBIN_HOOD_STD::index_sequence_for<U1...>(),
+ ROBIN_HOOD_STD::index_sequence_for<U2...>())))
+ : pair(a, b, ROBIN_HOOD_STD::index_sequence_for<U1...>(),
+ ROBIN_HOOD_STD::index_sequence_for<U2...>()) {
+ }
+
+ // constructor called from the std::piecewise_construct_t ctor
+ template <typename... U1, size_t... I1, typename... U2, size_t... I2>
+ pair(std::tuple<U1...>& a, std::tuple<U2...>& b, ROBIN_HOOD_STD::index_sequence<I1...> /*unused*/, ROBIN_HOOD_STD::index_sequence<I2...> /*unused*/) noexcept(
+ noexcept(T1(std::forward<U1>(std::get<I1>(
+ std::declval<std::tuple<
+ U1...>&>()))...)) && noexcept(T2(std::
+ forward<U2>(std::get<I2>(
+ std::declval<std::tuple<U2...>&>()))...)))
+ : first(std::forward<U1>(std::get<I1>(a))...)
+ , second(std::forward<U2>(std::get<I2>(b))...) {
+ // make visual studio compiler happy about warning about unused a & b.
+ // Visual studio's pair implementation disables warning 4100.
+ (void)a;
+ (void)b;
+ }
+
+ void swap(pair<T1, T2>& o) noexcept((detail::swappable::nothrow<T1>::value) &&
+ (detail::swappable::nothrow<T2>::value)) {
+ using std::swap;
+ swap(first, o.first);
+ swap(second, o.second);
+ }
+
+ T1 first; // NOLINT(misc-non-private-member-variables-in-classes)
+ T2 second; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+template <typename A, typename B>
+inline void swap(pair<A, B>& a, pair<A, B>& b) noexcept(
+ noexcept(std::declval<pair<A, B>&>().swap(std::declval<pair<A, B>&>()))) {
+ a.swap(b);
+}
+
+template <typename A, typename B>
+inline constexpr bool operator==(pair<A, B> const& x, pair<A, B> const& y) {
+ return (x.first == y.first) && (x.second == y.second);
+}
+template <typename A, typename B>
+inline constexpr bool operator!=(pair<A, B> const& x, pair<A, B> const& y) {
+ return !(x == y);
+}
+template <typename A, typename B>
+inline constexpr bool operator<(pair<A, B> const& x, pair<A, B> const& y) noexcept(noexcept(
+ std::declval<A const&>() < std::declval<A const&>()) && noexcept(std::declval<B const&>() <
+ std::declval<B const&>())) {
+ return x.first < y.first || (!(y.first < x.first) && x.second < y.second);
+}
+template <typename A, typename B>
+inline constexpr bool operator>(pair<A, B> const& x, pair<A, B> const& y) {
+ return y < x;
+}
+template <typename A, typename B>
+inline constexpr bool operator<=(pair<A, B> const& x, pair<A, B> const& y) {
+ return !(x > y);
+}
+template <typename A, typename B>
+inline constexpr bool operator>=(pair<A, B> const& x, pair<A, B> const& y) {
+ return !(x < y);
+}
+
+inline size_t hash_bytes(void const* ptr, size_t len) noexcept {
+ static constexpr uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
+ static constexpr uint64_t seed = UINT64_C(0xe17a1465);
+ static constexpr unsigned int r = 47;
+
+ auto const* const data64 = static_cast<uint64_t const*>(ptr);
+ uint64_t h = seed ^ (len * m);
+
+ size_t const n_blocks = len / 8;
+ for (size_t i = 0; i < n_blocks; ++i) {
+ auto k = detail::unaligned_load<uint64_t>(data64 + i);
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h ^= k;
+ h *= m;
+ }
+
+ auto const* const data8 = reinterpret_cast<uint8_t const*>(data64 + n_blocks);
+ switch (len & 7U) {
+ case 7:
+ h ^= static_cast<uint64_t>(data8[6]) << 48U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 6:
+ h ^= static_cast<uint64_t>(data8[5]) << 40U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 5:
+ h ^= static_cast<uint64_t>(data8[4]) << 32U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 4:
+ h ^= static_cast<uint64_t>(data8[3]) << 24U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 3:
+ h ^= static_cast<uint64_t>(data8[2]) << 16U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 2:
+ h ^= static_cast<uint64_t>(data8[1]) << 8U;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ case 1:
+ h ^= static_cast<uint64_t>(data8[0]);
+ h *= m;
+ ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+ default:
+ break;
+ }
+
+ h ^= h >> r;
+
+ // not doing the final step here, because this will be done by keyToIdx anyways
+ // h *= m;
+ // h ^= h >> r;
+ return static_cast<size_t>(h);
+}
+
+inline size_t hash_int(uint64_t x) noexcept {
+ // tried lots of different hashes, let's stick with murmurhash3. It's simple, fast, well tested,
+ // and doesn't need any special 128bit operations.
+ x ^= x >> 33U;
+ x *= UINT64_C(0xff51afd7ed558ccd);
+ x ^= x >> 33U;
+
+ // not doing the final step here, because this will be done by keyToIdx anyways
+ // x *= UINT64_C(0xc4ceb9fe1a85ec53);
+ // x ^= x >> 33U;
+ return static_cast<size_t>(x);
+}
+
+// A thin wrapper around std::hash, performing an additional simple mixing step of the result.
+template <typename T, typename Enable = void>
+struct hash : public std::hash<T> {
+ size_t operator()(T const& obj) const
+ noexcept(noexcept(std::declval<std::hash<T>>().operator()(std::declval<T const&>()))) {
+ // call base hash
+ auto result = std::hash<T>::operator()(obj);
+ // return mixed of that, to be save against identity has
+ return hash_int(static_cast<detail::SizeT>(result));
+ }
+};
+
+template <typename CharT>
+struct hash<std::basic_string<CharT>> {
+ size_t operator()(std::basic_string<CharT> const& str) const noexcept {
+ return hash_bytes(str.data(), sizeof(CharT) * str.size());
+ }
+};
+
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX17)
+template <typename CharT>
+struct hash<std::basic_string_view<CharT>> {
+ size_t operator()(std::basic_string_view<CharT> const& sv) const noexcept {
+ return hash_bytes(sv.data(), sizeof(CharT) * sv.size());
+ }
+};
+#endif
+
+template <class T>
+struct hash<T*> {
+ size_t operator()(T* ptr) const noexcept {
+ return hash_int(reinterpret_cast<detail::SizeT>(ptr));
+ }
+};
+
+template <class T>
+struct hash<std::unique_ptr<T>> {
+ size_t operator()(std::unique_ptr<T> const& ptr) const noexcept {
+ return hash_int(reinterpret_cast<detail::SizeT>(ptr.get()));
+ }
+};
+
+template <class T>
+struct hash<std::shared_ptr<T>> {
+ size_t operator()(std::shared_ptr<T> const& ptr) const noexcept {
+ return hash_int(reinterpret_cast<detail::SizeT>(ptr.get()));
+ }
+};
+
+template <typename Enum>
+struct hash<Enum, typename std::enable_if<std::is_enum<Enum>::value>::type> {
+ size_t operator()(Enum e) const noexcept {
+ using Underlying = typename std::underlying_type<Enum>::type;
+ return hash<Underlying>{}(static_cast<Underlying>(e));
+ }
+};
+
+#define ROBIN_HOOD_HASH_INT(T) \
+ template <> \
+ struct hash<T> { \
+ size_t operator()(T const& obj) const noexcept { \
+ return hash_int(static_cast<uint64_t>(obj)); \
+ } \
+ }
+
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif
+// see https://en.cppreference.com/w/cpp/utility/hash
+ROBIN_HOOD_HASH_INT(bool);
+ROBIN_HOOD_HASH_INT(char);
+ROBIN_HOOD_HASH_INT(signed char);
+ROBIN_HOOD_HASH_INT(unsigned char);
+ROBIN_HOOD_HASH_INT(char16_t);
+ROBIN_HOOD_HASH_INT(char32_t);
+#if ROBIN_HOOD(HAS_NATIVE_WCHART)
+ROBIN_HOOD_HASH_INT(wchar_t);
+#endif
+ROBIN_HOOD_HASH_INT(short);
+ROBIN_HOOD_HASH_INT(unsigned short);
+ROBIN_HOOD_HASH_INT(int);
+ROBIN_HOOD_HASH_INT(unsigned int);
+ROBIN_HOOD_HASH_INT(long);
+ROBIN_HOOD_HASH_INT(long long);
+ROBIN_HOOD_HASH_INT(unsigned long);
+ROBIN_HOOD_HASH_INT(unsigned long long);
+#if defined(__GNUC__) && !defined(__clang__)
+# pragma GCC diagnostic pop
+#endif
+namespace detail {
+
+template <typename T>
+struct void_type {
+ using type = void;
+};
+
+template <typename T, typename = void>
+struct has_is_transparent : public std::false_type {};
+
+template <typename T>
+struct has_is_transparent<T, typename void_type<typename T::is_transparent>::type>
+ : public std::true_type {};
+
+// using wrapper classes for hash and key_equal prevents the diamond problem when the same type
+// is used. see https://stackoverflow.com/a/28771920/48181
+template <typename T>
+struct WrapHash : public T {
+ WrapHash() = default;
+ explicit WrapHash(T const& o) noexcept(noexcept(T(std::declval<T const&>())))
+ : T(o) {}
+};
+
+template <typename T>
+struct WrapKeyEqual : public T {
+ WrapKeyEqual() = default;
+ explicit WrapKeyEqual(T const& o) noexcept(noexcept(T(std::declval<T const&>())))
+ : T(o) {}
+};
+
+// A highly optimized hashmap implementation, using the Robin Hood algorithm.
+//
+// In most cases, this map should be usable as a drop-in replacement for std::unordered_map, but
+// be about 2x faster in most cases and require much less allocations.
+//
+// This implementation uses the following memory layout:
+//
+// [Node, Node, ... Node | info, info, ... infoSentinel ]
+//
+// * Node: either a DataNode that directly has the std::pair<key, val> as member,
+// or a DataNode with a pointer to std::pair<key,val>. Which DataNode representation to use
+// depends on how fast the swap() operation is. Heuristically, this is automatically choosen
+// based on sizeof(). there are always 2^n Nodes.
+//
+// * info: Each Node in the map has a corresponding info byte, so there are 2^n info bytes.
+// Each byte is initialized to 0, meaning the corresponding Node is empty. Set to 1 means the
+// corresponding node contains data. Set to 2 means the corresponding Node is filled, but it
+// actually belongs to the previous position and was pushed out because that place is already
+// taken.
+//
+// * infoSentinel: Sentinel byte set to 1, so that iterator's ++ can stop at end() without the
+// need for a idx variable.
+//
+// According to STL, order of templates has effect on throughput. That's why I've moved the
+// boolean to the front.
+// https://www.reddit.com/r/cpp/comments/ahp6iu/compile_time_binary_size_reductions_and_cs_future/eeguck4/
+template <bool IsFlat, size_t MaxLoadFactor100, typename Key, typename T, typename Hash,
+ typename KeyEqual>
+class Table
+ : public WrapHash<Hash>,
+ public WrapKeyEqual<KeyEqual>,
+ detail::NodeAllocator<
+ typename std::conditional<
+ std::is_void<T>::value, Key,
+ robin_hood::pair<typename std::conditional<IsFlat, Key, Key const>::type, T>>::type,
+ 4, 16384, IsFlat> {
+public:
+ static constexpr bool is_flat = IsFlat;
+ static constexpr bool is_map = !std::is_void<T>::value;
+ static constexpr bool is_set = !is_map;
+ static constexpr bool is_transparent =
+ has_is_transparent<Hash>::value && has_is_transparent<KeyEqual>::value;
+
+ using key_type = Key;
+ using mapped_type = T;
+ using value_type = typename std::conditional<
+ is_set, Key,
+ robin_hood::pair<typename std::conditional<is_flat, Key, Key const>::type, T>>::type;
+ using size_type = size_t;
+ using hasher = Hash;
+ using key_equal = KeyEqual;
+ using Self = Table<IsFlat, MaxLoadFactor100, key_type, mapped_type, hasher, key_equal>;
+
+private:
+ static_assert(MaxLoadFactor100 > 10 && MaxLoadFactor100 < 100,
+ "MaxLoadFactor100 needs to be >10 && < 100");
+
+ using WHash = WrapHash<Hash>;
+ using WKeyEqual = WrapKeyEqual<KeyEqual>;
+
+ // configuration defaults
+
+ // make sure we have 8 elements, needed to quickly rehash mInfo
+ static constexpr size_t InitialNumElements = sizeof(uint64_t);
+ static constexpr uint32_t InitialInfoNumBits = 5;
+ static constexpr uint8_t InitialInfoInc = 1U << InitialInfoNumBits;
+ static constexpr size_t InfoMask = InitialInfoInc - 1U;
+ static constexpr uint8_t InitialInfoHashShift = 0;
+ using DataPool = detail::NodeAllocator<value_type, 4, 16384, IsFlat>;
+
+ // type needs to be wider than uint8_t.
+ using InfoType = uint32_t;
+
+ // DataNode ////////////////////////////////////////////////////////
+
+ // Primary template for the data node. We have special implementations for small and big
+ // objects. For large objects it is assumed that swap() is fairly slow, so we allocate these
+ // on the heap so swap merely swaps a pointer.
+ template <typename M, bool>
+ class DataNode {};
+
+ // Small: just allocate on the stack.
+ template <typename M>
+ class DataNode<M, true> final {
+ public:
+ template <typename... Args>
+ explicit DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, Args&&... args) noexcept(
+ noexcept(value_type(std::forward<Args>(args)...)))
+ : mData(std::forward<Args>(args)...) {}
+
+ DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, true>&& n) noexcept(
+ std::is_nothrow_move_constructible<value_type>::value)
+ : mData(std::move(n.mData)) {}
+
+ // doesn't do anything
+ void destroy(M& ROBIN_HOOD_UNUSED(map) /*unused*/) noexcept {}
+ void destroyDoNotDeallocate() noexcept {}
+
+ value_type const* operator->() const noexcept {
+ return &mData;
+ }
+ value_type* operator->() noexcept {
+ return &mData;
+ }
+
+ const value_type& operator*() const noexcept {
+ return mData;
+ }
+
+ value_type& operator*() noexcept {
+ return mData;
+ }
+
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, typename VT::first_type&>::type getFirst() noexcept {
+ return mData.first;
+ }
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_set, VT&>::type getFirst() noexcept {
+ return mData;
+ }
+
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, typename VT::first_type const&>::type
+ getFirst() const noexcept {
+ return mData.first;
+ }
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_set, VT const&>::type getFirst() const noexcept {
+ return mData;
+ }
+
+ template <typename MT = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, MT&>::type getSecond() noexcept {
+ return mData.second;
+ }
+
+ template <typename MT = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_set, MT const&>::type getSecond() const noexcept {
+ return mData.second;
+ }
+
+ void swap(DataNode<M, true>& o) noexcept(
+ noexcept(std::declval<value_type>().swap(std::declval<value_type>()))) {
+ mData.swap(o.mData);
+ }
+
+ private:
+ value_type mData;
+ };
+
+ // big object: allocate on heap.
+ template <typename M>
+ class DataNode<M, false> {
+ public:
+ template <typename... Args>
+ explicit DataNode(M& map, Args&&... args)
+ : mData(map.allocate()) {
+ ::new (static_cast<void*>(mData)) value_type(std::forward<Args>(args)...);
+ }
+
+ DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, false>&& n) noexcept
+ : mData(std::move(n.mData)) {}
+
+ void destroy(M& map) noexcept {
+ // don't deallocate, just put it into list of datapool.
+ mData->~value_type();
+ map.deallocate(mData);
+ }
+
+ void destroyDoNotDeallocate() noexcept {
+ mData->~value_type();
+ }
+
+ value_type const* operator->() const noexcept {
+ return mData;
+ }
+
+ value_type* operator->() noexcept {
+ return mData;
+ }
+
+ const value_type& operator*() const {
+ return *mData;
+ }
+
+ value_type& operator*() {
+ return *mData;
+ }
+
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, typename VT::first_type&>::type getFirst() noexcept {
+ return mData->first;
+ }
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_set, VT&>::type getFirst() noexcept {
+ return *mData;
+ }
+
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, typename VT::first_type const&>::type
+ getFirst() const noexcept {
+ return mData->first;
+ }
+ template <typename VT = value_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_set, VT const&>::type getFirst() const noexcept {
+ return *mData;
+ }
+
+ template <typename MT = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, MT&>::type getSecond() noexcept {
+ return mData->second;
+ }
+
+ template <typename MT = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<is_map, MT const&>::type getSecond() const noexcept {
+ return mData->second;
+ }
+
+ void swap(DataNode<M, false>& o) noexcept {
+ using std::swap;
+ swap(mData, o.mData);
+ }
+
+ private:
+ value_type* mData;
+ };
+
+ using Node = DataNode<Self, IsFlat>;
+
+ // helpers for insertKeyPrepareEmptySpot: extract first entry (only const required)
+ ROBIN_HOOD(NODISCARD) key_type const& getFirstConst(Node const& n) const noexcept {
+ return n.getFirst();
+ }
+
+ // in case we have void mapped_type, we are not using a pair, thus we just route k through.
+ // No need to disable this because it's just not used if not applicable.
+ ROBIN_HOOD(NODISCARD) key_type const& getFirstConst(key_type const& k) const noexcept {
+ return k;
+ }
+
+ // in case we have non-void mapped_type, we have a standard robin_hood::pair
+ template <typename Q = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<!std::is_void<Q>::value, key_type const&>::type
+ getFirstConst(value_type const& vt) const noexcept {
+ return vt.first;
+ }
+
+ // Cloner //////////////////////////////////////////////////////////
+
+ template <typename M, bool UseMemcpy>
+ struct Cloner;
+
+ // fast path: Just copy data, without allocating anything.
+ template <typename M>
+ struct Cloner<M, true> {
+ void operator()(M const& source, M& target) const {
+ auto const* const src = reinterpret_cast<char const*>(source.mKeyVals);
+ auto* tgt = reinterpret_cast<char*>(target.mKeyVals);
+ auto const numElementsWithBuffer = target.calcNumElementsWithBuffer(target.mMask + 1);
+ std::copy(src, src + target.calcNumBytesTotal(numElementsWithBuffer), tgt);
+ }
+ };
+
+ template <typename M>
+ struct Cloner<M, false> {
+ void operator()(M const& s, M& t) const {
+ auto const numElementsWithBuffer = t.calcNumElementsWithBuffer(t.mMask + 1);
+ std::copy(s.mInfo, s.mInfo + t.calcNumBytesInfo(numElementsWithBuffer), t.mInfo);
+
+ for (size_t i = 0; i < numElementsWithBuffer; ++i) {
+ if (t.mInfo[i]) {
+ ::new (static_cast<void*>(t.mKeyVals + i)) Node(t, *s.mKeyVals[i]);
+ }
+ }
+ }
+ };
+
+ // Destroyer ///////////////////////////////////////////////////////
+
+ template <typename M, bool IsFlatAndTrivial>
+ struct Destroyer {};
+
+ template <typename M>
+ struct Destroyer<M, true> {
+ void nodes(M& m) const noexcept {
+ m.mNumElements = 0;
+ }
+
+ void nodesDoNotDeallocate(M& m) const noexcept {
+ m.mNumElements = 0;
+ }
+ };
+
+ template <typename M>
+ struct Destroyer<M, false> {
+ void nodes(M& m) const noexcept {
+ m.mNumElements = 0;
+ // clear also resets mInfo to 0, that's sometimes not necessary.
+ auto const numElementsWithBuffer = m.calcNumElementsWithBuffer(m.mMask + 1);
+
+ for (size_t idx = 0; idx < numElementsWithBuffer; ++idx) {
+ if (0 != m.mInfo[idx]) {
+ Node& n = m.mKeyVals[idx];
+ n.destroy(m);
+ n.~Node();
+ }
+ }
+ }
+
+ void nodesDoNotDeallocate(M& m) const noexcept {
+ m.mNumElements = 0;
+ // clear also resets mInfo to 0, that's sometimes not necessary.
+ auto const numElementsWithBuffer = m.calcNumElementsWithBuffer(m.mMask + 1);
+ for (size_t idx = 0; idx < numElementsWithBuffer; ++idx) {
+ if (0 != m.mInfo[idx]) {
+ Node& n = m.mKeyVals[idx];
+ n.destroyDoNotDeallocate();
+ n.~Node();
+ }
+ }
+ }
+ };
+
+ // Iter ////////////////////////////////////////////////////////////
+
+ struct fast_forward_tag {};
+
+ // generic iterator for both const_iterator and iterator.
+ template <bool IsConst>
+ // NOLINTNEXTLINE(hicpp-special-member-functions,cppcoreguidelines-special-member-functions)
+ class Iter {
+ private:
+ using NodePtr = typename std::conditional<IsConst, Node const*, Node*>::type;
+
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = typename Self::value_type;
+ using reference = typename std::conditional<IsConst, value_type const&, value_type&>::type;
+ using pointer = typename std::conditional<IsConst, value_type const*, value_type*>::type;
+ using iterator_category = std::forward_iterator_tag;
+
+ // default constructed iterator can be compared to itself, but WON'T return true when
+ // compared to end().
+ Iter() = default;
+
+ // Rule of zero: nothing specified. The conversion constructor is only enabled for
+ // iterator to const_iterator, so it doesn't accidentally work as a copy ctor.
+
+ // Conversion constructor from iterator to const_iterator.
+ template <bool OtherIsConst,
+ typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ Iter(Iter<OtherIsConst> const& other) noexcept
+ : mKeyVals(other.mKeyVals)
+ , mInfo(other.mInfo) {}
+
+ Iter(NodePtr valPtr, uint8_t const* infoPtr) noexcept
+ : mKeyVals(valPtr)
+ , mInfo(infoPtr) {}
+
+ Iter(NodePtr valPtr, uint8_t const* infoPtr,
+ fast_forward_tag ROBIN_HOOD_UNUSED(tag) /*unused*/) noexcept
+ : mKeyVals(valPtr)
+ , mInfo(infoPtr) {
+ fastForward();
+ }
+
+ template <bool OtherIsConst,
+ typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
+ Iter& operator=(Iter<OtherIsConst> const& other) noexcept {
+ mKeyVals = other.mKeyVals;
+ mInfo = other.mInfo;
+ return *this;
+ }
+
+ // prefix increment. Undefined behavior if we are at end()!
+ Iter& operator++() noexcept {
+ mInfo++;
+ mKeyVals++;
+ fastForward();
+ return *this;
+ }
+
+ Iter operator++(int) noexcept {
+ Iter tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ reference operator*() const {
+ return **mKeyVals;
+ }
+
+ pointer operator->() const {
+ return &**mKeyVals;
+ }
+
+ template <bool O>
+ bool operator==(Iter<O> const& o) const noexcept {
+ return mKeyVals == o.mKeyVals;
+ }
+
+ template <bool O>
+ bool operator!=(Iter<O> const& o) const noexcept {
+ return mKeyVals != o.mKeyVals;
+ }
+
+ private:
+ // fast forward to the next non-free info byte
+ // I've tried a few variants that don't depend on intrinsics, but unfortunately they are
+ // quite a bit slower than this one. So I've reverted that change again. See map_benchmark.
+ void fastForward() noexcept {
+ size_t n = 0;
+ while (0U == (n = detail::unaligned_load<size_t>(mInfo))) {
+ mInfo += sizeof(size_t);
+ mKeyVals += sizeof(size_t);
+ }
+#if defined(ROBIN_HOOD_DISABLE_INTRINSICS)
+ // we know for certain that within the next 8 bytes we'll find a non-zero one.
+ if (ROBIN_HOOD_UNLIKELY(0U == detail::unaligned_load<uint32_t>(mInfo))) {
+ mInfo += 4;
+ mKeyVals += 4;
+ }
+ if (ROBIN_HOOD_UNLIKELY(0U == detail::unaligned_load<uint16_t>(mInfo))) {
+ mInfo += 2;
+ mKeyVals += 2;
+ }
+ if (ROBIN_HOOD_UNLIKELY(0U == *mInfo)) {
+ mInfo += 1;
+ mKeyVals += 1;
+ }
+#else
+# if ROBIN_HOOD(LITTLE_ENDIAN)
+ auto inc = ROBIN_HOOD_COUNT_TRAILING_ZEROES(n) / 8;
+# else
+ auto inc = ROBIN_HOOD_COUNT_LEADING_ZEROES(n) / 8;
+# endif
+ mInfo += inc;
+ mKeyVals += inc;
+#endif
+ }
+
+ friend class Table<IsFlat, MaxLoadFactor100, key_type, mapped_type, hasher, key_equal>;
+ NodePtr mKeyVals{nullptr};
+ uint8_t const* mInfo{nullptr};
+ };
+
+ ////////////////////////////////////////////////////////////////////
+
+ // highly performance relevant code.
+ // Lower bits are used for indexing into the array (2^n size)
+ // The upper 1-5 bits need to be a reasonable good hash, to save comparisons.
+ template <typename HashKey>
+ void keyToIdx(HashKey&& key, size_t* idx, InfoType* info) const {
+ // In addition to whatever hash is used, add another mul & shift so we get better hashing.
+ // This serves as a bad hash prevention, if the given data is
+ // badly mixed.
+ auto h = static_cast<uint64_t>(WHash::operator()(key));
+
+ h *= mHashMultiplier;
+ h ^= h >> 33U;
+
+ // the lower InitialInfoNumBits are reserved for info.
+ *info = mInfoInc + static_cast<InfoType>((h & InfoMask) >> mInfoHashShift);
+ *idx = (static_cast<size_t>(h) >> InitialInfoNumBits) & mMask;
+ }
+
+ // forwards the index by one, wrapping around at the end
+ void next(InfoType* info, size_t* idx) const noexcept {
+ *idx = *idx + 1;
+ *info += mInfoInc;
+ }
+
+ void nextWhileLess(InfoType* info, size_t* idx) const noexcept {
+ // unrolling this by hand did not bring any speedups.
+ while (*info < mInfo[*idx]) {
+ next(info, idx);
+ }
+ }
+
+ // Shift everything up by one element. Tries to move stuff around.
+ void
+ shiftUp(size_t startIdx,
+ size_t const insertion_idx) noexcept(std::is_nothrow_move_assignable<Node>::value) {
+ auto idx = startIdx;
+ ::new (static_cast<void*>(mKeyVals + idx)) Node(std::move(mKeyVals[idx - 1]));
+ while (--idx != insertion_idx) {
+ mKeyVals[idx] = std::move(mKeyVals[idx - 1]);
+ }
+
+ idx = startIdx;
+ while (idx != insertion_idx) {
+ ROBIN_HOOD_COUNT(shiftUp)
+ mInfo[idx] = static_cast<uint8_t>(mInfo[idx - 1] + mInfoInc);
+ if (ROBIN_HOOD_UNLIKELY(mInfo[idx] + mInfoInc > 0xFF)) {
+ mMaxNumElementsAllowed = 0;
+ }
+ --idx;
+ }
+ }
+
+ void shiftDown(size_t idx) noexcept(std::is_nothrow_move_assignable<Node>::value) {
+ // until we find one that is either empty or has zero offset.
+ // TODO(martinus) we don't need to move everything, just the last one for the same
+ // bucket.
+ mKeyVals[idx].destroy(*this);
+
+ // until we find one that is either empty or has zero offset.
+ while (mInfo[idx + 1] >= 2 * mInfoInc) {
+ ROBIN_HOOD_COUNT(shiftDown)
+ mInfo[idx] = static_cast<uint8_t>(mInfo[idx + 1] - mInfoInc);
+ mKeyVals[idx] = std::move(mKeyVals[idx + 1]);
+ ++idx;
+ }
+
+ mInfo[idx] = 0;
+ // don't destroy, we've moved it
+ // mKeyVals[idx].destroy(*this);
+ mKeyVals[idx].~Node();
+ }
+
+ // copy of find(), except that it returns iterator instead of const_iterator.
+ template <typename Other>
+ ROBIN_HOOD(NODISCARD)
+ size_t findIdx(Other const& key) const {
+ size_t idx{};
+ InfoType info{};
+ keyToIdx(key, &idx, &info);
+
+ do {
+ // unrolling this twice gives a bit of a speedup. More unrolling did not help.
+ if (info == mInfo[idx] &&
+ ROBIN_HOOD_LIKELY(WKeyEqual::operator()(key, mKeyVals[idx].getFirst()))) {
+ return idx;
+ }
+ next(&info, &idx);
+ if (info == mInfo[idx] &&
+ ROBIN_HOOD_LIKELY(WKeyEqual::operator()(key, mKeyVals[idx].getFirst()))) {
+ return idx;
+ }
+ next(&info, &idx);
+ } while (info <= mInfo[idx]);
+
+ // nothing found!
+ return mMask == 0 ? 0
+ : static_cast<size_t>(std::distance(
+ mKeyVals, reinterpret_cast_no_cast_align_warning<Node*>(mInfo)));
+ }
+
+ void cloneData(const Table& o) {
+ Cloner<Table, IsFlat && ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(Node)>()(o, *this);
+ }
+
+ // inserts a keyval that is guaranteed to be new, e.g. when the hashmap is resized.
+ // @return True on success, false if something went wrong
+ void insert_move(Node&& keyval) {
+ // we don't retry, fail if overflowing
+ // don't need to check max num elements
+ if (0 == mMaxNumElementsAllowed && !try_increase_info()) {
+ throwOverflowError();
+ }
+
+ size_t idx{};
+ InfoType info{};
+ keyToIdx(keyval.getFirst(), &idx, &info);
+
+ // skip forward. Use <= because we are certain that the element is not there.
+ while (info <= mInfo[idx]) {
+ idx = idx + 1;
+ info += mInfoInc;
+ }
+
+ // key not found, so we are now exactly where we want to insert it.
+ auto const insertion_idx = idx;
+ auto const insertion_info = static_cast<uint8_t>(info);
+ if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) {
+ mMaxNumElementsAllowed = 0;
+ }
+
+ // find an empty spot
+ while (0 != mInfo[idx]) {
+ next(&info, &idx);
+ }
+
+ auto& l = mKeyVals[insertion_idx];
+ if (idx == insertion_idx) {
+ ::new (static_cast<void*>(&l)) Node(std::move(keyval));
+ } else {
+ shiftUp(idx, insertion_idx);
+ l = std::move(keyval);
+ }
+
+ // put at empty spot
+ mInfo[insertion_idx] = insertion_info;
+
+ ++mNumElements;
+ }
+
+public:
+ using iterator = Iter<false>;
+ using const_iterator = Iter<true>;
+
+ Table() noexcept(noexcept(Hash()) && noexcept(KeyEqual()))
+ : WHash()
+ , WKeyEqual() {
+ ROBIN_HOOD_TRACE(this)
+ }
+
+ // Creates an empty hash map. Nothing is allocated yet, this happens at the first insert.
+ // This tremendously speeds up ctor & dtor of a map that never receives an element. The
+ // penalty is payed at the first insert, and not before. Lookup of this empty map works
+ // because everybody points to DummyInfoByte::b. parameter bucket_count is dictated by the
+ // standard, but we can ignore it.
+ explicit Table(
+ size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/, const Hash& h = Hash{},
+ const KeyEqual& equal = KeyEqual{}) noexcept(noexcept(Hash(h)) && noexcept(KeyEqual(equal)))
+ : WHash(h)
+ , WKeyEqual(equal) {
+ ROBIN_HOOD_TRACE(this)
+ }
+
+ template <typename Iter>
+ Table(Iter first, Iter last, size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0,
+ const Hash& h = Hash{}, const KeyEqual& equal = KeyEqual{})
+ : WHash(h)
+ , WKeyEqual(equal) {
+ ROBIN_HOOD_TRACE(this)
+ insert(first, last);
+ }
+
+ Table(std::initializer_list<value_type> initlist,
+ size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0, const Hash& h = Hash{},
+ const KeyEqual& equal = KeyEqual{})
+ : WHash(h)
+ , WKeyEqual(equal) {
+ ROBIN_HOOD_TRACE(this)
+ insert(initlist.begin(), initlist.end());
+ }
+
+ Table(Table&& o) noexcept
+ : WHash(std::move(static_cast<WHash&>(o)))
+ , WKeyEqual(std::move(static_cast<WKeyEqual&>(o)))
+ , DataPool(std::move(static_cast<DataPool&>(o))) {
+ ROBIN_HOOD_TRACE(this)
+ if (o.mMask) {
+ mHashMultiplier = std::move(o.mHashMultiplier);
+ mKeyVals = std::move(o.mKeyVals);
+ mInfo = std::move(o.mInfo);
+ mNumElements = std::move(o.mNumElements);
+ mMask = std::move(o.mMask);
+ mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed);
+ mInfoInc = std::move(o.mInfoInc);
+ mInfoHashShift = std::move(o.mInfoHashShift);
+ // set other's mask to 0 so its destructor won't do anything
+ o.init();
+ }
+ }
+
+ Table& operator=(Table&& o) noexcept {
+ ROBIN_HOOD_TRACE(this)
+ if (&o != this) {
+ if (o.mMask) {
+ // only move stuff if the other map actually has some data
+ destroy();
+ mHashMultiplier = std::move(o.mHashMultiplier);
+ mKeyVals = std::move(o.mKeyVals);
+ mInfo = std::move(o.mInfo);
+ mNumElements = std::move(o.mNumElements);
+ mMask = std::move(o.mMask);
+ mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed);
+ mInfoInc = std::move(o.mInfoInc);
+ mInfoHashShift = std::move(o.mInfoHashShift);
+ WHash::operator=(std::move(static_cast<WHash&>(o)));
+ WKeyEqual::operator=(std::move(static_cast<WKeyEqual&>(o)));
+ DataPool::operator=(std::move(static_cast<DataPool&>(o)));
+
+ o.init();
+
+ } else {
+ // nothing in the other map => just clear us.
+ clear();
+ }
+ }
+ return *this;
+ }
+
+ Table(const Table& o)
+ : WHash(static_cast<const WHash&>(o))
+ , WKeyEqual(static_cast<const WKeyEqual&>(o))
+ , DataPool(static_cast<const DataPool&>(o)) {
+ ROBIN_HOOD_TRACE(this)
+ if (!o.empty()) {
+ // not empty: create an exact copy. it is also possible to just iterate through all
+ // elements and insert them, but copying is probably faster.
+
+ auto const numElementsWithBuffer = calcNumElementsWithBuffer(o.mMask + 1);
+ auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer);
+
+ ROBIN_HOOD_LOG("std::malloc " << numBytesTotal << " = calcNumBytesTotal("
+ << numElementsWithBuffer << ")")
+ mHashMultiplier = o.mHashMultiplier;
+ mKeyVals = static_cast<Node*>(
+ detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal)));
+ // no need for calloc because clonData does memcpy
+ mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer);
+ mNumElements = o.mNumElements;
+ mMask = o.mMask;
+ mMaxNumElementsAllowed = o.mMaxNumElementsAllowed;
+ mInfoInc = o.mInfoInc;
+ mInfoHashShift = o.mInfoHashShift;
+ cloneData(o);
+ }
+ }
+
+ // Creates a copy of the given map. Copy constructor of each entry is used.
+ // Not sure why clang-tidy thinks this doesn't handle self assignment, it does
+ // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp)
+ Table& operator=(Table const& o) {
+ ROBIN_HOOD_TRACE(this)
+ if (&o == this) {
+ // prevent assigning of itself
+ return *this;
+ }
+
+ // we keep using the old allocator and not assign the new one, because we want to keep
+ // the memory available. when it is the same size.
+ if (o.empty()) {
+ if (0 == mMask) {
+ // nothing to do, we are empty too
+ return *this;
+ }
+
+ // not empty: destroy what we have there
+ // clear also resets mInfo to 0, that's sometimes not necessary.
+ destroy();
+ init();
+ WHash::operator=(static_cast<const WHash&>(o));
+ WKeyEqual::operator=(static_cast<const WKeyEqual&>(o));
+ DataPool::operator=(static_cast<DataPool const&>(o));
+
+ return *this;
+ }
+
+ // clean up old stuff
+ Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{}.nodes(*this);
+
+ if (mMask != o.mMask) {
+ // no luck: we don't have the same array size allocated, so we need to realloc.
+ if (0 != mMask) {
+ // only deallocate if we actually have data!
+ ROBIN_HOOD_LOG("std::free")
+ std::free(mKeyVals);
+ }
+
+ auto const numElementsWithBuffer = calcNumElementsWithBuffer(o.mMask + 1);
+ auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer);
+ ROBIN_HOOD_LOG("std::malloc " << numBytesTotal << " = calcNumBytesTotal("
+ << numElementsWithBuffer << ")")
+ mKeyVals = static_cast<Node*>(
+ detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal)));
+
+ // no need for calloc here because cloneData performs a memcpy.
+ mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer);
+ // sentinel is set in cloneData
+ }
+ WHash::operator=(static_cast<const WHash&>(o));
+ WKeyEqual::operator=(static_cast<const WKeyEqual&>(o));
+ DataPool::operator=(static_cast<DataPool const&>(o));
+ mHashMultiplier = o.mHashMultiplier;
+ mNumElements = o.mNumElements;
+ mMask = o.mMask;
+ mMaxNumElementsAllowed = o.mMaxNumElementsAllowed;
+ mInfoInc = o.mInfoInc;
+ mInfoHashShift = o.mInfoHashShift;
+ cloneData(o);
+
+ return *this;
+ }
+
+ // Swaps everything between the two maps.
+ void swap(Table& o) {
+ ROBIN_HOOD_TRACE(this)
+ using std::swap;
+ swap(o, *this);
+ }
+
+ // Clears all data, without resizing.
+ void clear() {
+ ROBIN_HOOD_TRACE(this)
+ if (empty()) {
+ // don't do anything! also important because we don't want to write to
+ // DummyInfoByte::b, even though we would just write 0 to it.
+ return;
+ }
+
+ Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{}.nodes(*this);
+
+ auto const numElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1);
+ // clear everything, then set the sentinel again
+ uint8_t const z = 0;
+ std::fill(mInfo, mInfo + calcNumBytesInfo(numElementsWithBuffer), z);
+ mInfo[numElementsWithBuffer] = 1;
+
+ mInfoInc = InitialInfoInc;
+ mInfoHashShift = InitialInfoHashShift;
+ }
+
+ // Destroys the map and all it's contents.
+ ~Table() {
+ ROBIN_HOOD_TRACE(this)
+ destroy();
+ }
+
+ // Checks if both tables contain the same entries. Order is irrelevant.
+ bool operator==(const Table& other) const {
+ ROBIN_HOOD_TRACE(this)
+ if (other.size() != size()) {
+ return false;
+ }
+ for (auto const& otherEntry : other) {
+ if (!has(otherEntry)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool operator!=(const Table& other) const {
+ ROBIN_HOOD_TRACE(this)
+ return !operator==(other);
+ }
+
+ template <typename Q = mapped_type>
+ typename std::enable_if<!std::is_void<Q>::value, Q&>::type operator[](const key_type& key) {
+ ROBIN_HOOD_TRACE(this)
+ auto idxAndState = insertKeyPrepareEmptySpot(key);
+ switch (idxAndState.second) {
+ case InsertionState::key_found:
+ break;
+
+ case InsertionState::new_node:
+ ::new (static_cast<void*>(&mKeyVals[idxAndState.first]))
+ Node(*this, std::piecewise_construct, std::forward_as_tuple(key),
+ std::forward_as_tuple());
+ break;
+
+ case InsertionState::overwrite_node:
+ mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct,
+ std::forward_as_tuple(key), std::forward_as_tuple());
+ break;
+
+ case InsertionState::overflow_error:
+ throwOverflowError();
+ }
+
+ return mKeyVals[idxAndState.first].getSecond();
+ }
+
+ template <typename Q = mapped_type>
+ typename std::enable_if<!std::is_void<Q>::value, Q&>::type operator[](key_type&& key) {
+ ROBIN_HOOD_TRACE(this)
+ auto idxAndState = insertKeyPrepareEmptySpot(key);
+ switch (idxAndState.second) {
+ case InsertionState::key_found:
+ break;
+
+ case InsertionState::new_node:
+ ::new (static_cast<void*>(&mKeyVals[idxAndState.first]))
+ Node(*this, std::piecewise_construct, std::forward_as_tuple(std::move(key)),
+ std::forward_as_tuple());
+ break;
+
+ case InsertionState::overwrite_node:
+ mKeyVals[idxAndState.first] =
+ Node(*this, std::piecewise_construct, std::forward_as_tuple(std::move(key)),
+ std::forward_as_tuple());
+ break;
+
+ case InsertionState::overflow_error:
+ throwOverflowError();
+ }
+
+ return mKeyVals[idxAndState.first].getSecond();
+ }
+
+ template <typename Iter>
+ void insert(Iter first, Iter last) {
+ for (; first != last; ++first) {
+ // value_type ctor needed because this might be called with std::pair's
+ insert(value_type(*first));
+ }
+ }
+
+ void insert(std::initializer_list<value_type> ilist) {
+ for (auto&& vt : ilist) {
+ insert(std::move(vt));
+ }
+ }
+
+ template <typename... Args>
+ std::pair<iterator, bool> emplace(Args&&... args) {
+ ROBIN_HOOD_TRACE(this)
+ Node n{*this, std::forward<Args>(args)...};
+ auto idxAndState = insertKeyPrepareEmptySpot(getFirstConst(n));
+ switch (idxAndState.second) {
+ case InsertionState::key_found:
+ n.destroy(*this);
+ break;
+
+ case InsertionState::new_node:
+ ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node(*this, std::move(n));
+ break;
+
+ case InsertionState::overwrite_node:
+ mKeyVals[idxAndState.first] = std::move(n);
+ break;
+
+ case InsertionState::overflow_error:
+ n.destroy(*this);
+ throwOverflowError();
+ break;
+ }
+
+ return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first),
+ InsertionState::key_found != idxAndState.second);
+ }
+
+ template <typename... Args>
+ iterator emplace_hint(const_iterator position, Args&&... args) {
+ (void)position;
+ return emplace(std::forward<Args>(args)...).first;
+ }
+
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) {
+ return try_emplace_impl(key, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args) {
+ return try_emplace_impl(std::move(key), std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args) {
+ (void)hint;
+ return try_emplace_impl(key, std::forward<Args>(args)...).first;
+ }
+
+ template <typename... Args>
+ iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) {
+ (void)hint;
+ return try_emplace_impl(std::move(key), std::forward<Args>(args)...).first;
+ }
+
+ template <typename Mapped>
+ std::pair<iterator, bool> insert_or_assign(const key_type& key, Mapped&& obj) {
+ return insertOrAssignImpl(key, std::forward<Mapped>(obj));
+ }
+
+ template <typename Mapped>
+ std::pair<iterator, bool> insert_or_assign(key_type&& key, Mapped&& obj) {
+ return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj));
+ }
+
+ template <typename Mapped>
+ iterator insert_or_assign(const_iterator hint, const key_type& key, Mapped&& obj) {
+ (void)hint;
+ return insertOrAssignImpl(key, std::forward<Mapped>(obj)).first;
+ }
+
+ template <typename Mapped>
+ iterator insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) {
+ (void)hint;
+ return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj)).first;
+ }
+
+ std::pair<iterator, bool> insert(const value_type& keyval) {
+ ROBIN_HOOD_TRACE(this)
+ return emplace(keyval);
+ }
+
+ iterator insert(const_iterator hint, const value_type& keyval) {
+ (void)hint;
+ return emplace(keyval).first;
+ }
+
+ std::pair<iterator, bool> insert(value_type&& keyval) {
+ return emplace(std::move(keyval));
+ }
+
+ iterator insert(const_iterator hint, value_type&& keyval) {
+ (void)hint;
+ return emplace(std::move(keyval)).first;
+ }
+
+ // Returns 1 if key is found, 0 otherwise.
+ size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ auto kv = mKeyVals + findIdx(key);
+ if (kv != reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ template <typename OtherKey, typename Self_ = Self>
+ // NOLINTNEXTLINE(modernize-use-nodiscard)
+ typename std::enable_if<Self_::is_transparent, size_t>::type count(const OtherKey& key) const {
+ ROBIN_HOOD_TRACE(this)
+ auto kv = mKeyVals + findIdx(key);
+ if (kv != reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ bool contains(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
+ return 1U == count(key);
+ }
+
+ template <typename OtherKey, typename Self_ = Self>
+ // NOLINTNEXTLINE(modernize-use-nodiscard)
+ typename std::enable_if<Self_::is_transparent, bool>::type contains(const OtherKey& key) const {
+ return 1U == count(key);
+ }
+
+ // Returns a reference to the value found for key.
+ // Throws std::out_of_range if element cannot be found
+ template <typename Q = mapped_type>
+ // NOLINTNEXTLINE(modernize-use-nodiscard)
+ typename std::enable_if<!std::is_void<Q>::value, Q&>::type at(key_type const& key) {
+ ROBIN_HOOD_TRACE(this)
+ auto kv = mKeyVals + findIdx(key);
+ if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+ doThrow<std::out_of_range>("key not found");
+ }
+ return kv->getSecond();
+ }
+
+ // Returns a reference to the value found for key.
+ // Throws std::out_of_range if element cannot be found
+ template <typename Q = mapped_type>
+ // NOLINTNEXTLINE(modernize-use-nodiscard)
+ typename std::enable_if<!std::is_void<Q>::value, Q const&>::type at(key_type const& key) const {
+ ROBIN_HOOD_TRACE(this)
+ auto kv = mKeyVals + findIdx(key);
+ if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+ doThrow<std::out_of_range>("key not found");
+ }
+ return kv->getSecond();
+ }
+
+ const_iterator find(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return const_iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ template <typename OtherKey>
+ const_iterator find(const OtherKey& key, is_transparent_tag /*unused*/) const {
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return const_iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ template <typename OtherKey, typename Self_ = Self>
+ typename std::enable_if<Self_::is_transparent, // NOLINT(modernize-use-nodiscard)
+ const_iterator>::type // NOLINT(modernize-use-nodiscard)
+ find(const OtherKey& key) const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return const_iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ iterator find(const key_type& key) {
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ template <typename OtherKey>
+ iterator find(const OtherKey& key, is_transparent_tag /*unused*/) {
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ template <typename OtherKey, typename Self_ = Self>
+ typename std::enable_if<Self_::is_transparent, iterator>::type find(const OtherKey& key) {
+ ROBIN_HOOD_TRACE(this)
+ const size_t idx = findIdx(key);
+ return iterator{mKeyVals + idx, mInfo + idx};
+ }
+
+ iterator begin() {
+ ROBIN_HOOD_TRACE(this)
+ if (empty()) {
+ return end();
+ }
+ return iterator(mKeyVals, mInfo, fast_forward_tag{});
+ }
+ const_iterator begin() const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return cbegin();
+ }
+ const_iterator cbegin() const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ if (empty()) {
+ return cend();
+ }
+ return const_iterator(mKeyVals, mInfo, fast_forward_tag{});
+ }
+
+ iterator end() {
+ ROBIN_HOOD_TRACE(this)
+ // no need to supply valid info pointer: end() must not be dereferenced, and only node
+ // pointer is compared.
+ return iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr};
+ }
+ const_iterator end() const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return cend();
+ }
+ const_iterator cend() const { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return const_iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr};
+ }
+
+ iterator erase(const_iterator pos) {
+ ROBIN_HOOD_TRACE(this)
+ // its safe to perform const cast here
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ return erase(iterator{const_cast<Node*>(pos.mKeyVals), const_cast<uint8_t*>(pos.mInfo)});
+ }
+
+ // Erases element at pos, returns iterator to the next element.
+ iterator erase(iterator pos) {
+ ROBIN_HOOD_TRACE(this)
+ // we assume that pos always points to a valid entry, and not end().
+ auto const idx = static_cast<size_t>(pos.mKeyVals - mKeyVals);
+
+ shiftDown(idx);
+ --mNumElements;
+
+ if (*pos.mInfo) {
+ // we've backward shifted, return this again
+ return pos;
+ }
+
+ // no backward shift, return next element
+ return ++pos;
+ }
+
+ size_t erase(const key_type& key) {
+ ROBIN_HOOD_TRACE(this)
+ size_t idx{};
+ InfoType info{};
+ keyToIdx(key, &idx, &info);
+
+ // check while info matches with the source idx
+ do {
+ if (info == mInfo[idx] && WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+ shiftDown(idx);
+ --mNumElements;
+ return 1;
+ }
+ next(&info, &idx);
+ } while (info <= mInfo[idx]);
+
+ // nothing found to delete
+ return 0;
+ }
+
+ // reserves space for the specified number of elements. Makes sure the old data fits.
+ // exactly the same as reserve(c).
+ void rehash(size_t c) {
+ // forces a reserve
+ reserve(c, true);
+ }
+
+ // reserves space for the specified number of elements. Makes sure the old data fits.
+ // Exactly the same as rehash(c). Use rehash(0) to shrink to fit.
+ void reserve(size_t c) {
+ // reserve, but don't force rehash
+ reserve(c, false);
+ }
+
+ // If possible reallocates the map to a smaller one. This frees the underlying table.
+ // Does not do anything if load_factor is too large for decreasing the table's size.
+ void compact() {
+ ROBIN_HOOD_TRACE(this)
+ auto newSize = InitialNumElements;
+ while (calcMaxNumElementsAllowed(newSize) < mNumElements && newSize != 0) {
+ newSize *= 2;
+ }
+ if (ROBIN_HOOD_UNLIKELY(newSize == 0)) {
+ throwOverflowError();
+ }
+
+ ROBIN_HOOD_LOG("newSize > mMask + 1: " << newSize << " > " << mMask << " + 1")
+
+ // only actually do anything when the new size is bigger than the old one. This prevents to
+ // continuously allocate for each reserve() call.
+ if (newSize < mMask + 1) {
+ rehashPowerOfTwo(newSize, true);
+ }
+ }
+
+ size_type size() const noexcept { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return mNumElements;
+ }
+
+ size_type max_size() const noexcept { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return static_cast<size_type>(-1);
+ }
+
+ ROBIN_HOOD(NODISCARD) bool empty() const noexcept {
+ ROBIN_HOOD_TRACE(this)
+ return 0 == mNumElements;
+ }
+
+ float max_load_factor() const noexcept { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return MaxLoadFactor100 / 100.0F;
+ }
+
+ // Average number of elements per bucket. Since we allow only 1 per bucket
+ float load_factor() const noexcept { // NOLINT(modernize-use-nodiscard)
+ ROBIN_HOOD_TRACE(this)
+ return static_cast<float>(size()) / static_cast<float>(mMask + 1);
+ }
+
+ ROBIN_HOOD(NODISCARD) size_t mask() const noexcept {
+ ROBIN_HOOD_TRACE(this)
+ return mMask;
+ }
+
+ ROBIN_HOOD(NODISCARD) size_t calcMaxNumElementsAllowed(size_t maxElements) const noexcept {
+ if (ROBIN_HOOD_LIKELY(maxElements <= (std::numeric_limits<size_t>::max)() / 100)) {
+ return maxElements * MaxLoadFactor100 / 100;
+ }
+
+ // we might be a bit inprecise, but since maxElements is quite large that doesn't matter
+ return (maxElements / 100) * MaxLoadFactor100;
+ }
+
+ ROBIN_HOOD(NODISCARD) size_t calcNumBytesInfo(size_t numElements) const noexcept {
+ // we add a uint64_t, which houses the sentinel (first byte) and padding so we can load
+ // 64bit types.
+ return numElements + sizeof(uint64_t);
+ }
+
+ ROBIN_HOOD(NODISCARD)
+ size_t calcNumElementsWithBuffer(size_t numElements) const noexcept {
+ auto maxNumElementsAllowed = calcMaxNumElementsAllowed(numElements);
+ return numElements + (std::min)(maxNumElementsAllowed, (static_cast<size_t>(0xFF)));
+ }
+
+ // calculation only allowed for 2^n values
+ ROBIN_HOOD(NODISCARD) size_t calcNumBytesTotal(size_t numElements) const {
+#if ROBIN_HOOD(BITNESS) == 64
+ return numElements * sizeof(Node) + calcNumBytesInfo(numElements);
+#else
+ // make sure we're doing 64bit operations, so we are at least safe against 32bit overflows.
+ auto const ne = static_cast<uint64_t>(numElements);
+ auto const s = static_cast<uint64_t>(sizeof(Node));
+ auto const infos = static_cast<uint64_t>(calcNumBytesInfo(numElements));
+
+ auto const total64 = ne * s + infos;
+ auto const total = static_cast<size_t>(total64);
+
+ if (ROBIN_HOOD_UNLIKELY(static_cast<uint64_t>(total) != total64)) {
+ throwOverflowError();
+ }
+ return total;
+#endif
+ }
+
+private:
+ template <typename Q = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<!std::is_void<Q>::value, bool>::type has(const value_type& e) const {
+ ROBIN_HOOD_TRACE(this)
+ auto it = find(e.first);
+ return it != end() && it->second == e.second;
+ }
+
+ template <typename Q = mapped_type>
+ ROBIN_HOOD(NODISCARD)
+ typename std::enable_if<std::is_void<Q>::value, bool>::type has(const value_type& e) const {
+ ROBIN_HOOD_TRACE(this)
+ return find(e) != end();
+ }
+
+ void reserve(size_t c, bool forceRehash) {
+ ROBIN_HOOD_TRACE(this)
+ auto const minElementsAllowed = (std::max)(c, mNumElements);
+ auto newSize = InitialNumElements;
+ while (calcMaxNumElementsAllowed(newSize) < minElementsAllowed && newSize != 0) {
+ newSize *= 2;
+ }
+ if (ROBIN_HOOD_UNLIKELY(newSize == 0)) {
+ throwOverflowError();
+ }
+
+ ROBIN_HOOD_LOG("newSize > mMask + 1: " << newSize << " > " << mMask << " + 1")
+
+ // only actually do anything when the new size is bigger than the old one. This prevents to
+ // continuously allocate for each reserve() call.
+ if (forceRehash || newSize > mMask + 1) {
+ rehashPowerOfTwo(newSize, false);
+ }
+ }
+
+ // reserves space for at least the specified number of elements.
+ // only works if numBuckets if power of two
+ // True on success, false otherwise
+ void rehashPowerOfTwo(size_t numBuckets, bool forceFree) {
+ ROBIN_HOOD_TRACE(this)
+
+ Node* const oldKeyVals = mKeyVals;
+ uint8_t const* const oldInfo = mInfo;
+
+ const size_t oldMaxElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1);
+
+ // resize operation: move stuff
+ initData(numBuckets);
+ if (oldMaxElementsWithBuffer > 1) {
+ for (size_t i = 0; i < oldMaxElementsWithBuffer; ++i) {
+ if (oldInfo[i] != 0) {
+ // might throw an exception, which is really bad since we are in the middle of
+ // moving stuff.
+ insert_move(std::move(oldKeyVals[i]));
+ // destroy the node but DON'T destroy the data.
+ oldKeyVals[i].~Node();
+ }
+ }
+
+ // this check is not necessary as it's guarded by the previous if, but it helps
+ // silence g++'s overeager "attempt to free a non-heap object 'map'
+ // [-Werror=free-nonheap-object]" warning.
+ if (oldKeyVals != reinterpret_cast_no_cast_align_warning<Node*>(&mMask)) {
+ // don't destroy old data: put it into the pool instead
+ if (forceFree) {
+ std::free(oldKeyVals);
+ } else {
+ DataPool::addOrFree(oldKeyVals, calcNumBytesTotal(oldMaxElementsWithBuffer));
+ }
+ }
+ }
+ }
+
+ ROBIN_HOOD(NOINLINE) void throwOverflowError() const {
+#if ROBIN_HOOD(HAS_EXCEPTIONS)
+ throw std::overflow_error("robin_hood::map overflow");
+#else
+ abort();
+#endif
+ }
+
+ template <typename OtherKey, typename... Args>
+ std::pair<iterator, bool> try_emplace_impl(OtherKey&& key, Args&&... args) {
+ ROBIN_HOOD_TRACE(this)
+ auto idxAndState = insertKeyPrepareEmptySpot(key);
+ switch (idxAndState.second) {
+ case InsertionState::key_found:
+ break;
+
+ case InsertionState::new_node:
+ ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node(
+ *this, std::piecewise_construct, std::forward_as_tuple(std::forward<OtherKey>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ break;
+
+ case InsertionState::overwrite_node:
+ mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<OtherKey>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ break;
+
+ case InsertionState::overflow_error:
+ throwOverflowError();
+ break;
+ }
+
+ return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first),
+ InsertionState::key_found != idxAndState.second);
+ }
+
+ template <typename OtherKey, typename Mapped>
+ std::pair<iterator, bool> insertOrAssignImpl(OtherKey&& key, Mapped&& obj) {
+ ROBIN_HOOD_TRACE(this)
+ auto idxAndState = insertKeyPrepareEmptySpot(key);
+ switch (idxAndState.second) {
+ case InsertionState::key_found:
+ mKeyVals[idxAndState.first].getSecond() = std::forward<Mapped>(obj);
+ break;
+
+ case InsertionState::new_node:
+ ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node(
+ *this, std::piecewise_construct, std::forward_as_tuple(std::forward<OtherKey>(key)),
+ std::forward_as_tuple(std::forward<Mapped>(obj)));
+ break;
+
+ case InsertionState::overwrite_node:
+ mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<OtherKey>(key)),
+ std::forward_as_tuple(std::forward<Mapped>(obj)));
+ break;
+
+ case InsertionState::overflow_error:
+ throwOverflowError();
+ break;
+ }
+
+ return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first),
+ InsertionState::key_found != idxAndState.second);
+ }
+
+ void initData(size_t max_elements) {
+ mNumElements = 0;
+ mMask = max_elements - 1;
+ mMaxNumElementsAllowed = calcMaxNumElementsAllowed(max_elements);
+
+ auto const numElementsWithBuffer = calcNumElementsWithBuffer(max_elements);
+
+ // malloc & zero mInfo. Faster than calloc everything.
+ auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer);
+ ROBIN_HOOD_LOG("std::calloc " << numBytesTotal << " = calcNumBytesTotal("
+ << numElementsWithBuffer << ")")
+ mKeyVals = reinterpret_cast<Node*>(
+ detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal)));
+ mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer);
+ std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node));
+
+ // set sentinel
+ mInfo[numElementsWithBuffer] = 1;
+
+ mInfoInc = InitialInfoInc;
+ mInfoHashShift = InitialInfoHashShift;
+ }
+
+ enum class InsertionState { overflow_error, key_found, new_node, overwrite_node };
+
+ // Finds key, and if not already present prepares a spot where to pot the key & value.
+ // This potentially shifts nodes out of the way, updates mInfo and number of inserted
+ // elements, so the only operation left to do is create/assign a new node at that spot.
+ template <typename OtherKey>
+ std::pair<size_t, InsertionState> insertKeyPrepareEmptySpot(OtherKey&& key) {
+ for (int i = 0; i < 256; ++i) {
+ size_t idx{};
+ InfoType info{};
+ keyToIdx(key, &idx, &info);
+ nextWhileLess(&info, &idx);
+
+ // while we potentially have a match
+ while (info == mInfo[idx]) {
+ if (WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+ // key already exists, do NOT insert.
+ // see http://en.cppreference.com/w/cpp/container/unordered_map/insert
+ return std::make_pair(idx, InsertionState::key_found);
+ }
+ next(&info, &idx);
+ }
+
+ // unlikely that this evaluates to true
+ if (ROBIN_HOOD_UNLIKELY(mNumElements >= mMaxNumElementsAllowed)) {
+ if (!increase_size()) {
+ return std::make_pair(size_t(0), InsertionState::overflow_error);
+ }
+ continue;
+ }
+
+ // key not found, so we are now exactly where we want to insert it.
+ auto const insertion_idx = idx;
+ auto const insertion_info = info;
+ if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) {
+ mMaxNumElementsAllowed = 0;
+ }
+
+ // find an empty spot
+ while (0 != mInfo[idx]) {
+ next(&info, &idx);
+ }
+
+ if (idx != insertion_idx) {
+ shiftUp(idx, insertion_idx);
+ }
+ // put at empty spot
+ mInfo[insertion_idx] = static_cast<uint8_t>(insertion_info);
+ ++mNumElements;
+ return std::make_pair(insertion_idx, idx == insertion_idx
+ ? InsertionState::new_node
+ : InsertionState::overwrite_node);
+ }
+
+ // enough attempts failed, so finally give up.
+ return std::make_pair(size_t(0), InsertionState::overflow_error);
+ }
+
+ bool try_increase_info() {
+ ROBIN_HOOD_LOG("mInfoInc=" << mInfoInc << ", numElements=" << mNumElements
+ << ", maxNumElementsAllowed="
+ << calcMaxNumElementsAllowed(mMask + 1))
+ if (mInfoInc <= 2) {
+ // need to be > 2 so that shift works (otherwise undefined behavior!)
+ return false;
+ }
+ // we got space left, try to make info smaller
+ mInfoInc = static_cast<uint8_t>(mInfoInc >> 1U);
+
+ // remove one bit of the hash, leaving more space for the distance info.
+ // This is extremely fast because we can operate on 8 bytes at once.
+ ++mInfoHashShift;
+ auto const numElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1);
+
+ for (size_t i = 0; i < numElementsWithBuffer; i += 8) {
+ auto val = unaligned_load<uint64_t>(mInfo + i);
+ val = (val >> 1U) & UINT64_C(0x7f7f7f7f7f7f7f7f);
+ std::memcpy(mInfo + i, &val, sizeof(val));
+ }
+ // update sentinel, which might have been cleared out!
+ mInfo[numElementsWithBuffer] = 1;
+
+ mMaxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1);
+ return true;
+ }
+
+ // True if resize was possible, false otherwise
+ bool increase_size() {
+ // nothing allocated yet? just allocate InitialNumElements
+ if (0 == mMask) {
+ initData(InitialNumElements);
+ return true;
+ }
+
+ auto const maxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1);
+ if (mNumElements < maxNumElementsAllowed && try_increase_info()) {
+ return true;
+ }
+
+ ROBIN_HOOD_LOG("mNumElements=" << mNumElements << ", maxNumElementsAllowed="
+ << maxNumElementsAllowed << ", load="
+ << (static_cast<double>(mNumElements) * 100.0 /
+ (static_cast<double>(mMask) + 1)))
+
+ if (mNumElements * 2 < calcMaxNumElementsAllowed(mMask + 1)) {
+ // we have to resize, even though there would still be plenty of space left!
+ // Try to rehash instead. Delete freed memory so we don't steadyily increase mem in case
+ // we have to rehash a few times
+ nextHashMultiplier();
+ rehashPowerOfTwo(mMask + 1, true);
+ } else {
+ // we've reached the capacity of the map, so the hash seems to work nice. Keep using it.
+ rehashPowerOfTwo((mMask + 1) * 2, false);
+ }
+ return true;
+ }
+
+ void nextHashMultiplier() {
+ // adding an *even* number, so that the multiplier will always stay odd. This is necessary
+ // so that the hash stays a mixing function (and thus doesn't have any information loss).
+ mHashMultiplier += UINT64_C(0xc4ceb9fe1a85ec54);
+ }
+
+ void destroy() {
+ if (0 == mMask) {
+ // don't deallocate!
+ return;
+ }
+
+ Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{}
+ .nodesDoNotDeallocate(*this);
+
+ // This protection against not deleting mMask shouldn't be needed as it's sufficiently
+ // protected with the 0==mMask check, but I have this anyways because g++ 7 otherwise
+ // reports a compile error: attempt to free a non-heap object 'fm'
+ // [-Werror=free-nonheap-object]
+ if (mKeyVals != reinterpret_cast_no_cast_align_warning<Node*>(&mMask)) {
+ ROBIN_HOOD_LOG("std::free")
+ std::free(mKeyVals);
+ }
+ }
+
+ void init() noexcept {
+ mKeyVals = reinterpret_cast_no_cast_align_warning<Node*>(&mMask);
+ mInfo = reinterpret_cast<uint8_t*>(&mMask);
+ mNumElements = 0;
+ mMask = 0;
+ mMaxNumElementsAllowed = 0;
+ mInfoInc = InitialInfoInc;
+ mInfoHashShift = InitialInfoHashShift;
+ }
+
+ // members are sorted so no padding occurs
+ uint64_t mHashMultiplier = UINT64_C(0xc4ceb9fe1a85ec53); // 8 byte 8
+ Node* mKeyVals = reinterpret_cast_no_cast_align_warning<Node*>(&mMask); // 8 byte 16
+ uint8_t* mInfo = reinterpret_cast<uint8_t*>(&mMask); // 8 byte 24
+ size_t mNumElements = 0; // 8 byte 32
+ size_t mMask = 0; // 8 byte 40
+ size_t mMaxNumElementsAllowed = 0; // 8 byte 48
+ InfoType mInfoInc = InitialInfoInc; // 4 byte 52
+ InfoType mInfoHashShift = InitialInfoHashShift; // 4 byte 56
+ // 16 byte 56 if NodeAllocator
+};
+
+} // namespace detail
+
+// map
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+ typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_flat_map = detail::Table<true, MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+ typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_node_map = detail::Table<false, MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+ typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_map =
+ detail::Table<sizeof(robin_hood::pair<Key, T>) <= sizeof(size_t) * 6 &&
+ std::is_nothrow_move_constructible<robin_hood::pair<Key, T>>::value &&
+ std::is_nothrow_move_assignable<robin_hood::pair<Key, T>>::value,
+ MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+// set
+
+template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>,
+ size_t MaxLoadFactor100 = 80>
+using unordered_flat_set = detail::Table<true, MaxLoadFactor100, Key, void, Hash, KeyEqual>;
+
+template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>,
+ size_t MaxLoadFactor100 = 80>
+using unordered_node_set = detail::Table<false, MaxLoadFactor100, Key, void, Hash, KeyEqual>;
+
+template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>,
+ size_t MaxLoadFactor100 = 80>
+using unordered_set = detail::Table<sizeof(Key) <= sizeof(size_t) * 6 &&
+ std::is_nothrow_move_constructible<Key>::value &&
+ std::is_nothrow_move_assignable<Key>::value,
+ MaxLoadFactor100, Key, void, Hash, KeyEqual>;
+
+} // namespace robin_hood
+
+#endif
diff --git a/contrib/libs/jinja2cpp/src/serialize_filters.cpp b/contrib/libs/jinja2cpp/src/serialize_filters.cpp
new file mode 100644
index 0000000000..f373492967
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/serialize_filters.cpp
@@ -0,0 +1,442 @@
+#include "filters.h"
+#include "generic_adapters.h"
+#include "out_stream.h"
+#include "testers.h"
+#include "value_helpers.h"
+#include "value_visitors.h"
+
+#include <fmt/args.h>
+
+#include <algorithm>
+#include <numeric>
+#include <random>
+#include <sstream>
+#include <string>
+
+#ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST
+#error #include "binding/boost_json_serializer.h"
+using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper;
+#else
+#include "binding/rapid_json_serializer.h"
+using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper;
+#endif
+
+
+using namespace std::string_literals;
+
+namespace jinja2
+{
+namespace filters
+{
+struct PrettyPrinter : visitors::BaseVisitor<std::string>
+{
+ using BaseVisitor::operator();
+
+ PrettyPrinter(const RenderContext* context)
+ : m_context(context)
+ {
+ }
+
+ std::string operator()(const ListAdapter& list) const
+ {
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ fmt::format_to(os, "[");
+ bool isFirst = true;
+
+ for (auto& v : list)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ fmt::format_to(os, ", ");
+ fmt::format_to(os, "{}", Apply<PrettyPrinter>(v, m_context));
+ }
+ fmt::format_to(os, "]");
+
+ return str;
+ }
+
+ std::string operator()(const MapAdapter& map) const
+ {
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ fmt::format_to(os, "{{");
+
+ const auto& keys = map.GetKeys();
+
+ bool isFirst = true;
+ for (auto& k : keys)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ fmt::format_to(os, ", ");
+
+ fmt::format_to(os, "'{}': ", k);
+ fmt::format_to(os, "{}", Apply<PrettyPrinter>(map.GetValueByName(k), m_context));
+ }
+
+ fmt::format_to(os, "}}");
+
+ return str;
+ }
+
+ std::string operator()(const KeyValuePair& kwPair) const
+ {
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ fmt::format_to(os, "'{}': ", kwPair.key);
+ fmt::format_to(os, "{}", Apply<PrettyPrinter>(kwPair.value, m_context));
+
+ return str;
+ }
+
+ std::string operator()(const std::string& str) const { return fmt::format("'{}'", str); }
+
+ std::string operator()(const std::string_view& str) const { return fmt::format("'{}'", fmt::basic_string_view<char>(str.data(), str.size())); }
+
+ std::string operator()(const std::wstring& str) const { return fmt::format("'{}'", ConvertString<std::string>(str)); }
+
+ std::string operator()(const std::wstring_view& str) const { return fmt::format("'{}'", ConvertString<std::string>(str)); }
+
+ std::string operator()(bool val) const { return val ? "true"s : "false"s; }
+
+ std::string operator()(EmptyValue) const { return "none"s; }
+
+ std::string operator()(const Callable&) const { return "<callable>"s; }
+
+ std::string operator()(double val) const
+ {
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ fmt::format_to(os, "{:.8g}", val);
+
+ return str;
+ }
+
+ std::string operator()(int64_t val) const { return fmt::format("{}", val); }
+
+ const RenderContext* m_context;
+};
+
+PrettyPrint::PrettyPrint(FilterParams params) {}
+
+InternalValue PrettyPrint::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ return Apply<PrettyPrinter>(baseVal, &context);
+}
+
+Serialize::Serialize(const FilterParams params, const Serialize::Mode mode)
+ : m_mode(mode)
+{
+ switch (mode)
+ {
+ case JsonMode:
+ ParseParams({ { "indent", false, static_cast<int64_t>(0) } }, params);
+ break;
+ default:
+ break;
+ }
+}
+
+InternalValue Serialize::Filter(const InternalValue& value, RenderContext& context)
+{
+ if (m_mode == JsonMode)
+ {
+ const auto indent = ConvertToInt(this->GetArgumentValue("indent", context));
+ DocumentWrapper jsonDoc;
+ const auto jsonValue = jsonDoc.CreateValue(value);
+ const auto jsonString = jsonValue.AsString(static_cast<uint8_t>(indent));
+ std::string result = ""s;
+ for (char c : jsonString) {
+ if (c == '<') {
+ result.append("\\u003c");
+ } else if (c == '>') {
+ result.append("\\u003e");
+ } else if (c == '&') {
+ result.append("\\u0026");
+ } else if (c == '\'') {
+ result.append("\\u0027");
+ } else {
+ result.push_back(c);
+ }
+ }
+
+ return result;
+ }
+
+ return InternalValue();
+}
+
+namespace
+{
+
+using FormatContext = fmt::format_context;
+using FormatArgument = fmt::basic_format_arg<FormatContext>;
+using FormatDynamicArgsStore = fmt::dynamic_format_arg_store<FormatContext>;
+
+struct FormatArgumentConverter : visitors::BaseVisitor<FormatArgument>
+{
+ using result_t = FormatArgument;
+
+ using BaseVisitor::operator();
+
+ FormatArgumentConverter(const RenderContext* context, FormatDynamicArgsStore& store)
+ : m_context(context)
+ , m_store(store)
+ {
+ }
+
+ FormatArgumentConverter(const RenderContext* context, FormatDynamicArgsStore& store, const std::string& name)
+ : m_context(context)
+ , m_store(store)
+ , m_name(name)
+ , m_named(true)
+ {
+ }
+
+ result_t operator()(const ListAdapter& list) const { return make_result(Apply<PrettyPrinter>(list, m_context)); }
+
+ result_t operator()(const MapAdapter& map) const { return make_result(Apply<PrettyPrinter>(map, m_context)); }
+
+ result_t operator()(const std::string& str) const { return make_result(str); }
+
+ result_t operator()(const std::string_view& str) const { return make_result(std::string(str.data(), str.size())); }
+
+ result_t operator()(const std::wstring& str) const { return make_result(ConvertString<std::string>(str)); }
+
+ result_t operator()(const std::wstring_view& str) const { return make_result(ConvertString<std::string>(str)); }
+
+ result_t operator()(double val) const { return make_result(val); }
+
+ result_t operator()(int64_t val) const { return make_result(val); }
+
+ result_t operator()(bool val) const { return make_result(val ? "true"s : "false"s); }
+
+ result_t operator()(EmptyValue) const { return make_result("none"s); }
+
+ result_t operator()(const Callable&) const { return make_result("<callable>"s); }
+
+ template<typename T>
+ result_t make_result(const T& t) const
+ {
+ if (!m_named)
+ {
+ m_store.push_back(t);
+ }
+ else
+ {
+ m_store.push_back(fmt::arg(m_name.c_str(), t));
+ }
+ return fmt::detail::make_arg<FormatContext>(t);
+ }
+
+ const RenderContext* m_context;
+ FormatDynamicArgsStore& m_store;
+ const std::string m_name;
+ bool m_named = false;
+};
+
+} // namespace
+
+InternalValue StringFormat::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ // Format library internally likes using non-owning views to complex arguments.
+ // In order to ensure proper lifetime of values and named args,
+ // helper buffer is created and passed to visitors.
+ FormatDynamicArgsStore store;
+ for (auto& arg : m_params.posParams)
+ {
+ Apply<FormatArgumentConverter>(arg->Evaluate(context), &context, store);
+ }
+
+ for (auto& arg : m_params.kwParams)
+ {
+ Apply<FormatArgumentConverter>(arg.second->Evaluate(context), &context, store, arg.first);
+ }
+
+ return InternalValue(fmt::vformat(AsString(baseVal), store));
+}
+
+class XmlAttrPrinter : public visitors::BaseVisitor<std::string>
+{
+public:
+ using BaseVisitor::operator();
+
+ explicit XmlAttrPrinter(RenderContext* context, bool isFirstLevel = false)
+ : m_context(context)
+ , m_isFirstLevel(isFirstLevel)
+ {
+ }
+
+ std::string operator()(const ListAdapter& list) const
+ {
+ EnforceThatNested();
+
+ return EscapeHtml(Apply<PrettyPrinter>(list, m_context));
+ }
+
+ std::string operator()(const MapAdapter& map) const
+ {
+ if (!m_isFirstLevel)
+ {
+ return EscapeHtml(Apply<PrettyPrinter>(map, m_context));
+ }
+
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ const auto& keys = map.GetKeys();
+
+ bool isFirst = true;
+ for (auto& k : keys)
+ {
+ const auto& v = map.GetValueByName(k);
+ const auto item = Apply<XmlAttrPrinter>(v, m_context, false);
+ if (item.length() > 0)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ fmt::format_to(os, " ");
+
+ fmt::format_to(os, "{}=\"{}\"", k, item);
+ }
+ }
+
+ return str;
+ }
+
+ std::string operator()(const KeyValuePair& kwPair) const
+ {
+ EnforceThatNested();
+
+ return EscapeHtml(Apply<PrettyPrinter>(kwPair, m_context));
+ }
+
+ std::string operator()(const std::string& str) const
+ {
+ EnforceThatNested();
+
+ return EscapeHtml(str);
+ }
+
+ std::string operator()(const std::string_view& str) const
+ {
+ EnforceThatNested();
+
+ const auto result = fmt::format("{}", fmt::basic_string_view<char>(str.data(), str.size()));
+ return EscapeHtml(result);
+ }
+
+ std::string operator()(const std::wstring& str) const
+ {
+ EnforceThatNested();
+
+ return EscapeHtml(ConvertString<std::string>(str));
+ }
+
+ std::string operator()(const std::wstring_view& str) const
+ {
+ EnforceThatNested();
+
+ const auto result = fmt::format("{}", ConvertString<std::string>(str));
+ return EscapeHtml(result);
+ }
+
+ std::string operator()(bool val) const
+ {
+ EnforceThatNested();
+
+ return val ? "true"s : "false"s;
+ }
+
+ std::string operator()(EmptyValue) const
+ {
+ EnforceThatNested();
+
+ return ""s;
+ }
+
+ std::string operator()(const Callable&) const
+ {
+ EnforceThatNested();
+
+ return ""s;
+ }
+
+ std::string operator()(double val) const
+ {
+ EnforceThatNested();
+
+ std::string str;
+ auto os = std::back_inserter(str);
+
+ fmt::format_to(os, "{:.8g}", val);
+
+ return str;
+ }
+
+ std::string operator()(int64_t val) const
+ {
+ EnforceThatNested();
+
+ return fmt::format("{}", val);
+ }
+
+private:
+ void EnforceThatNested() const
+ {
+ if (m_isFirstLevel)
+ m_context->GetRendererCallback()->ThrowRuntimeError(ErrorCode::InvalidValueType, ValuesList{});
+ }
+
+ std::string EscapeHtml(const std::string &str) const
+ {
+ const auto result = std::accumulate(str.begin(), str.end(), ""s, [](const auto &str, const auto &c)
+ {
+ switch (c)
+ {
+ case '<':
+ return str + "&lt;";
+ break;
+ case '>':
+ return str +"&gt;";
+ break;
+ case '&':
+ return str +"&amp;";
+ break;
+ case '\'':
+ return str +"&#39;";
+ break;
+ case '\"':
+ return str +"&#34;";
+ break;
+ default:
+ return str + c;
+ break;
+ }
+ });
+
+ return result;
+ }
+
+private:
+ RenderContext* m_context;
+ bool m_isFirstLevel;
+};
+
+XmlAttrFilter::XmlAttrFilter(FilterParams) {}
+
+InternalValue XmlAttrFilter::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ return Apply<XmlAttrPrinter>(baseVal, &context, true);
+}
+
+} // namespace filters
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/statements.cpp b/contrib/libs/jinja2cpp/src/statements.cpp
new file mode 100644
index 0000000000..bb4718d2e4
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/statements.cpp
@@ -0,0 +1,839 @@
+#include "statements.h"
+
+#include "expression_evaluator.h"
+#include "template_impl.h"
+#include "value_visitors.h"
+
+#include <boost/core/null_deleter.hpp>
+
+#include <string>
+
+using namespace std::string_literals;
+
+namespace jinja2
+{
+
+void ForStatement::Render(OutStream& os, RenderContext& values)
+{
+ InternalValue loopVal = m_value->Evaluate(values);
+
+ RenderLoop(loopVal, os, values, 0);
+}
+
+void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, RenderContext& values, int level)
+{
+ auto& context = values.EnterScope();
+
+ InternalValueMap loopVar;
+ context["loop"s] = CreateMapAdapter(&loopVar);
+ if (m_isRecursive)
+ {
+ loopVar["operator()"s] = Callable(Callable::GlobalFunc, [this, level](const CallParams& params, OutStream& stream, RenderContext& context) {
+ bool isSucceeded = false;
+ auto parsedParams = helpers::ParseCallParams({ { "var", true } }, params, isSucceeded);
+ if (!isSucceeded)
+ return;
+
+ auto var = parsedParams["var"];
+ if (var.IsEmpty())
+ return;
+
+ RenderLoop(var, stream, context, level + 1);
+ });
+ loopVar["depth"s] = static_cast<int64_t>(level + 1);
+ loopVar["depth0"s] = static_cast<int64_t>(level);
+ }
+
+ bool isConverted = false;
+ auto loopItems = ConvertToList(loopVal, isConverted, false);
+ ListAdapter filteredList;
+ ListAdapter indexedList;
+ ListAccessorEnumeratorPtr enumerator;
+ size_t itemIdx = 0;
+ if (!isConverted)
+ {
+ if (m_elseBody)
+ m_elseBody->Render(os, values);
+ values.ExitScope();
+ return;
+ }
+
+ std::optional<size_t> listSize;
+ if (m_ifExpr)
+ {
+ filteredList = CreateFilteredAdapter(loopItems, values);
+ enumerator = filteredList.GetEnumerator();
+ }
+ else
+ {
+ enumerator = loopItems.GetEnumerator();
+ listSize = loopItems.GetSize();
+ }
+
+ bool isLast = false;
+ auto makeIndexedList = [&enumerator, &listSize, &indexedList, &itemIdx, &isLast] {
+ if (isLast)
+ listSize = itemIdx;
+
+ InternalValueList items;
+ do
+ {
+ items.push_back(enumerator->GetCurrent());
+ } while (enumerator->MoveNext());
+
+ listSize = itemIdx + items.size() + 1;
+ indexedList = ListAdapter::CreateAdapter(std::move(items));
+ enumerator = indexedList.GetEnumerator();
+ isLast = !enumerator->MoveNext();
+ };
+
+ if (listSize)
+ {
+ int64_t itemsNum = static_cast<int64_t>(listSize.value());
+ loopVar["length"s] = InternalValue(itemsNum);
+ }
+ else
+ {
+ loopVar["length"s] = MakeDynamicProperty([&listSize, &makeIndexedList](const CallParams& /*params*/, RenderContext & /*context*/) -> InternalValue {
+ if (!listSize)
+ makeIndexedList();
+ return static_cast<int64_t>(listSize.value());
+ });
+ }
+ bool loopRendered = false;
+ isLast = !enumerator->MoveNext();
+ InternalValue prevValue;
+ InternalValue curValue;
+ InternalValue nextValue;
+ loopVar["cycle"s] = static_cast<int64_t>(LoopCycleFn);
+ for (; !isLast; ++itemIdx)
+ {
+ prevValue = std::move(curValue);
+ if (itemIdx != 0)
+ {
+ std::swap(curValue, nextValue);
+ loopVar["previtem"s] = prevValue;
+ }
+ else
+ curValue = enumerator->GetCurrent();
+
+ isLast = !enumerator->MoveNext();
+ if (!isLast)
+ {
+ nextValue = enumerator->GetCurrent();
+ loopVar["nextitem"s] = nextValue;
+ }
+ else
+ loopVar.erase("nextitem"s);
+
+ loopRendered = true;
+ loopVar["index"s] = static_cast<int64_t>(itemIdx + 1);
+ loopVar["index0"s] = static_cast<int64_t>(itemIdx);
+ loopVar["first"s] = itemIdx == 0;
+ loopVar["last"s] = isLast;
+
+ if (m_vars.size() > 1)
+ {
+ const auto& valList = ConvertToList(curValue, isConverted);
+ if (!isConverted)
+ continue;
+
+ auto b = valList.begin();
+ auto e = valList.end();
+
+ for (auto& varName : m_vars)
+ {
+ if (b == e)
+ continue;
+ context[varName] = *b;
+ ++ b;
+ }
+ }
+ else
+ context[m_vars[0]] = curValue;
+
+ values.EnterScope();
+ m_mainBody->Render(os, values);
+ values.ExitScope();
+ }
+
+ if (!loopRendered && m_elseBody)
+ m_elseBody->Render(os, values);
+
+ values.ExitScope();
+}
+
+ListAdapter ForStatement::CreateFilteredAdapter(const ListAdapter& loopItems, RenderContext& values) const
+{
+ return ListAdapter::CreateAdapter([e = loopItems.GetEnumerator(), this, &values]() mutable {
+ using ResultType = std::optional<InternalValue>;
+
+ auto& tempContext = values.EnterScope();
+ for (bool finish = !e->MoveNext(); !finish; finish = !e->MoveNext())
+ {
+ auto curValue = e->GetCurrent();
+ if (m_vars.size() > 1)
+ {
+ for (auto& varName : m_vars)
+ tempContext[varName] = Subscript(curValue, varName, &values);
+ }
+ else
+ {
+ tempContext[m_vars[0]] = curValue;
+ }
+
+ if (ConvertToBool(m_ifExpr->Evaluate(values)))
+ {
+ values.ExitScope();
+ return ResultType(std::move(curValue));
+ }
+ }
+ values.ExitScope();
+
+ return ResultType();
+ });
+}
+
+void IfStatement::Render(OutStream& os, RenderContext& values)
+{
+ InternalValue val = m_expr->Evaluate(values);
+ bool isTrue = Apply<visitors::BooleanEvaluator>(val);
+
+ if (isTrue)
+ {
+ m_mainBody->Render(os, values);
+ return;
+ }
+
+ for (auto& b : m_elseBranches)
+ {
+ if (b->ShouldRender(values))
+ {
+ b->Render(os, values);
+ break;
+ }
+ }
+}
+
+bool ElseBranchStatement::ShouldRender(RenderContext& values) const
+{
+ if (!m_expr)
+ return true;
+
+ return Apply<visitors::BooleanEvaluator>(m_expr->Evaluate(values));
+}
+
+void ElseBranchStatement::Render(OutStream& os, RenderContext& values)
+{
+ m_mainBody->Render(os, values);
+}
+
+void SetStatement::AssignBody(InternalValue body, RenderContext& values)
+{
+ auto& scope = values.GetCurrentScope();
+ if (m_fields.size() == 1)
+ scope[m_fields.front()] = std::move(body);
+ else
+ {
+ for (const auto& name : m_fields)
+ scope[name] = Subscript(body, name, &values);
+ }
+}
+
+void SetLineStatement::Render(OutStream&, RenderContext& values)
+{
+ if (!m_expr)
+ return;
+ AssignBody(m_expr->Evaluate(values), values);
+}
+
+InternalValue SetBlockStatement::RenderBody(RenderContext& values)
+{
+ TargetString result;
+ auto stream = values.GetRendererCallback()->GetStreamOnString(result);
+ auto innerValues = values.Clone(true);
+ m_body->Render(stream, innerValues);
+ return result;
+}
+
+void SetRawBlockStatement::Render(OutStream&, RenderContext& values)
+{
+ AssignBody(RenderBody(values), values);
+}
+
+void SetFilteredBlockStatement::Render(OutStream&, RenderContext& values)
+{
+ if (!m_expr)
+ return;
+ AssignBody(m_expr->Evaluate(RenderBody(values), values), values);
+}
+
+class IBlocksRenderer : public IRendererBase
+{
+public:
+ virtual bool HasBlock(const std::string& blockName) = 0;
+ virtual void RenderBlock(const std::string& blockName, OutStream& os, RenderContext& values) = 0;
+};
+
+void ParentBlockStatement::Render(OutStream& os, RenderContext& values)
+{
+ RenderContext innerContext = values.Clone(m_isScoped);
+ bool found = false;
+ auto parentTplVal = values.FindValue("$$__parent_template", found);
+ if (!found)
+ {
+ m_mainBody->Render(os, values);
+ return;
+ }
+
+ bool isConverted = false;
+ auto parentTplsList = ConvertToList(parentTplVal->second, isConverted);
+ if (!isConverted)
+ return;
+
+ IBlocksRenderer* blockRenderer = nullptr; // static_cast<BlocksRenderer*>(*parentTplPtr);
+ for (auto& tplVal : parentTplsList)
+ {
+ auto ptr = GetIf<RendererPtr>(&tplVal);
+ if (!ptr)
+ continue;
+
+ auto parentTplPtr = static_cast<IBlocksRenderer*>(ptr->get());
+
+ if (parentTplPtr->HasBlock(m_name))
+ {
+ blockRenderer = parentTplPtr;
+ break;
+ }
+ }
+
+ if (!blockRenderer)
+ {
+ m_mainBody->Render(os, values);
+ return;
+ }
+
+ auto& scope = innerContext.EnterScope();
+ scope["$$__super_block"] = RendererPtr(this, boost::null_deleter());
+ scope["super"] =
+ Callable(Callable::SpecialFunc, [this](const CallParams&, OutStream& stream, RenderContext& context) { m_mainBody->Render(stream, context); });
+ if (!m_isScoped)
+ scope["$$__parent_template"] = parentTplsList;
+
+ blockRenderer->RenderBlock(m_name, os, innerContext);
+ innerContext.ExitScope();
+
+ auto& globalScope = values.GetGlobalScope();
+ auto selfMap = GetIf<MapAdapter>(&globalScope[std::string("self")]);
+ if (!selfMap->HasValue(m_name))
+ selfMap->SetValue(m_name, MakeWrapped(Callable(Callable::SpecialFunc, [this](const CallParams&, OutStream& stream, RenderContext& context) {
+ Render(stream, context);
+ })));
+}
+
+void BlockStatement::Render(OutStream& os, RenderContext& values)
+{
+ m_mainBody->Render(os, values);
+}
+
+template<typename CharT>
+class ParentTemplateRenderer : public IBlocksRenderer
+{
+public:
+ ParentTemplateRenderer(std::shared_ptr<TemplateImpl<CharT>> tpl, ExtendsStatement::BlocksCollection* blocks)
+ : m_template(tpl)
+ , m_blocks(blocks)
+ {
+ }
+
+ void Render(OutStream& os, RenderContext& values) override
+ {
+ auto& scope = values.GetCurrentScope();
+ InternalValueList parentTemplates;
+ parentTemplates.push_back(InternalValue(RendererPtr(this, boost::null_deleter())));
+ bool isFound = false;
+ auto p = values.FindValue("$$__parent_template", isFound);
+ if (isFound)
+ {
+ bool isConverted = false;
+ auto prevTplsList = ConvertToList(p->second, isConverted);
+ if (isConverted)
+ {
+ for (auto& tpl : prevTplsList)
+ parentTemplates.push_back(tpl);
+ }
+ }
+ scope["$$__parent_template"] = ListAdapter::CreateAdapter(std::move(parentTemplates));
+ m_template->GetRenderer()->Render(os, values);
+ }
+
+ void RenderBlock(const std::string& blockName, OutStream& os, RenderContext& values) override
+ {
+ auto p = m_blocks->find(blockName);
+ if (p == m_blocks->end())
+ return;
+
+ p->second->Render(os, values);
+ }
+
+ bool HasBlock(const std::string& blockName) override { return m_blocks->count(blockName) != 0; }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ParentTemplateRenderer*>(&other);
+ if (!val)
+ return false;
+ if (m_template != val->m_template)
+ return false;
+ if (m_blocks && val->m_blocks && *m_blocks != *(val->m_blocks))
+ return false;
+ if ((m_blocks && !val->m_blocks) || (!m_blocks && val->m_blocks))
+ return false;
+ return true;
+ }
+
+private:
+ std::shared_ptr<TemplateImpl<CharT>> m_template;
+ ExtendsStatement::BlocksCollection* m_blocks;
+};
+
+template<typename Result, typename Fn>
+struct TemplateImplVisitor
+{
+ // ExtendsStatement::BlocksCollection* m_blocks;
+ const Fn& m_fn;
+ bool m_throwError{};
+
+ explicit TemplateImplVisitor(const Fn& fn, bool throwError)
+ : m_fn(fn)
+ , m_throwError(throwError)
+ {
+ }
+
+ template<typename CharT>
+ Result operator()(nonstd::expected<std::shared_ptr<TemplateImpl<CharT>>, ErrorInfoTpl<CharT>> tpl) const
+ {
+ if (!m_throwError && !tpl)
+ {
+ return Result{};
+ }
+ else if (!tpl)
+ {
+ throw tpl.error();
+ }
+ return m_fn(tpl.value());
+ }
+
+ Result operator()(EmptyValue) const { return Result(); }
+};
+
+template<typename Result, typename Fn, typename Arg>
+Result VisitTemplateImpl(Arg&& tpl, bool throwError, Fn&& fn)
+{
+ return visit(TemplateImplVisitor<Result, Fn>(fn, throwError), tpl);
+}
+
+template<template<typename T> class RendererTpl, typename CharT, typename... Args>
+auto CreateTemplateRenderer(std::shared_ptr<TemplateImpl<CharT>> tpl, Args&&... args)
+{
+ return std::make_shared<RendererTpl<CharT>>(tpl, std::forward<Args>(args)...);
+}
+
+void ExtendsStatement::Render(OutStream& os, RenderContext& values)
+{
+ if (!m_isPath)
+ {
+ // FIXME: Implement processing of templates
+ return;
+ }
+ auto tpl = values.GetRendererCallback()->LoadTemplate(m_templateName);
+ auto renderer =
+ VisitTemplateImpl<RendererPtr>(tpl, true, [this](auto tplPtr) { return CreateTemplateRenderer<ParentTemplateRenderer>(tplPtr, &m_blocks); });
+ if (renderer)
+ renderer->Render(os, values);
+}
+
+template<typename CharT>
+class IncludedTemplateRenderer : public IRendererBase
+{
+public:
+ IncludedTemplateRenderer(std::shared_ptr<TemplateImpl<CharT>> tpl, bool withContext)
+ : m_template(tpl)
+ , m_withContext(withContext)
+ {
+ }
+
+ void Render(OutStream& os, RenderContext& values) override
+ {
+ RenderContext innerContext = values.Clone(m_withContext);
+ if (m_withContext)
+ innerContext.EnterScope();
+
+ m_template->GetRenderer()->Render(os, innerContext);
+ if (m_withContext)
+ {
+ auto& innerScope = innerContext.GetCurrentScope();
+ auto& scope = values.GetCurrentScope();
+ for (auto& v : innerScope)
+ {
+ scope[v.first] = std::move(v.second);
+ }
+ }
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const IncludedTemplateRenderer<CharT>*>(&other);
+ if (!val)
+ return false;
+ if (m_template != val->m_template)
+ return false;
+ if (m_withContext != val->m_withContext)
+ return false;
+ return true;
+ }
+
+private:
+ std::shared_ptr<TemplateImpl<CharT>> m_template;
+ bool m_withContext{};
+};
+
+void IncludeStatement::Render(OutStream& os, RenderContext& values)
+{
+ auto templateNames = m_expr->Evaluate(values);
+ bool isConverted = false;
+ ListAdapter list = ConvertToList(templateNames, isConverted);
+
+ auto doRender = [this, &values, &os](auto&& name) -> bool {
+ auto tpl = values.GetRendererCallback()->LoadTemplate(name);
+
+ try
+ {
+ auto renderer = VisitTemplateImpl<RendererPtr>(
+ tpl, true, [this](auto tplPtr) { return CreateTemplateRenderer<IncludedTemplateRenderer>(tplPtr, m_withContext); });
+
+ if (renderer)
+ {
+ renderer->Render(os, values);
+ return true;
+ }
+ }
+ catch (const ErrorInfoTpl<char>& err)
+ {
+ if (err.GetCode() != ErrorCode::FileNotFound)
+ throw;
+ }
+ catch (const ErrorInfoTpl<wchar_t>& err)
+ {
+ if (err.GetCode() != ErrorCode::FileNotFound)
+ throw;
+ }
+
+ return false;
+ };
+
+ bool rendered = false;
+ if (isConverted)
+ {
+ for (auto& name : list)
+ {
+ rendered = doRender(name);
+ if (rendered)
+ break;
+ }
+ }
+ else
+ {
+ rendered = doRender(templateNames);
+ }
+
+ if (!rendered && !m_ignoreMissing)
+ {
+ InternalValueList files;
+ ValuesList extraParams;
+ if (isConverted)
+ {
+ extraParams.push_back(IntValue2Value(templateNames));
+ }
+ else
+ {
+ files.push_back(templateNames);
+ extraParams.push_back(IntValue2Value(ListAdapter::CreateAdapter(std::move(files))));
+ }
+
+ values.GetRendererCallback()->ThrowRuntimeError(ErrorCode::TemplateNotFound, std::move(extraParams));
+ }
+}
+
+class ImportedMacroRenderer : public IRendererBase
+{
+public:
+ explicit ImportedMacroRenderer(InternalValueMap&& map, bool withContext)
+ : m_importedContext(std::move(map))
+ , m_withContext(withContext)
+ {
+ }
+
+ void Render(OutStream& /*os*/, RenderContext& /*values*/) override {}
+
+ void InvokeMacro(const Callable& callable, const CallParams& params, OutStream& stream, RenderContext& context)
+ {
+ auto ctx = context.Clone(m_withContext);
+ ctx.BindScope(&m_importedContext);
+ callable.GetStatementCallable()(params, stream, ctx);
+ }
+
+ static void InvokeMacro(const std::string& contextName, const Callable& callable, const CallParams& params, OutStream& stream, RenderContext& context)
+ {
+ bool contextValFound = false;
+ auto contextVal = context.FindValue(contextName, contextValFound);
+ if (!contextValFound)
+ return;
+
+ auto rendererPtr = GetIf<RendererPtr>(&contextVal->second);
+ if (!rendererPtr)
+ return;
+
+ auto renderer = static_cast<ImportedMacroRenderer*>(rendererPtr->get());
+ renderer->InvokeMacro(callable, params, stream, context);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ImportedMacroRenderer*>(&other);
+ if (!val)
+ return false;
+ if (m_importedContext != val->m_importedContext)
+ return false;
+ if (m_withContext != val->m_withContext)
+ return false;
+ return true;
+ }
+
+private:
+ InternalValueMap m_importedContext;
+ bool m_withContext{};
+};
+
+void ImportStatement::Render(OutStream& /*os*/, RenderContext& values)
+{
+ auto name = m_nameExpr->Evaluate(values);
+
+ if (!m_renderer)
+ {
+ auto tpl = values.GetRendererCallback()->LoadTemplate(name);
+ m_renderer = VisitTemplateImpl<RendererPtr>(tpl, true, [](auto tplPtr) { return CreateTemplateRenderer<IncludedTemplateRenderer>(tplPtr, true); });
+ }
+
+ if (!m_renderer)
+ return;
+
+ std::string scopeName;
+ {
+ TargetString tsScopeName = values.GetRendererCallback()->GetAsTargetString(name);
+ scopeName = "$$_imported_" + GetAsSameString(scopeName, tsScopeName).value();
+ }
+
+ TargetString str;
+ auto tmpStream = values.GetRendererCallback()->GetStreamOnString(str);
+
+ RenderContext newContext = values.Clone(m_withContext);
+ InternalValueMap importedScope;
+ {
+ auto& intImportedScope = newContext.EnterScope();
+ m_renderer->Render(tmpStream, newContext);
+ importedScope = std::move(intImportedScope);
+ }
+
+ ImportNames(values, importedScope, scopeName);
+ values.GetCurrentScope()[scopeName] =
+ std::static_pointer_cast<IRendererBase>(std::make_shared<ImportedMacroRenderer>(std::move(importedScope), m_withContext));
+}
+
+void ImportStatement::ImportNames(RenderContext& values, InternalValueMap& importedScope, const std::string& scopeName) const
+{
+ InternalValueMap importedNs;
+
+ for (auto& var : importedScope)
+ {
+ if (var.first.empty())
+ continue;
+
+ if (var.first[0] == '_')
+ continue;
+
+ auto mappedP = m_namesToImport.find(var.first);
+ if (!m_namespace && mappedP == m_namesToImport.end())
+ continue;
+
+ InternalValue imported;
+ auto callable = GetIf<Callable>(&var.second);
+ if (!callable)
+ {
+ imported = std::move(var.second);
+ }
+ else if (callable->GetKind() == Callable::Macro)
+ {
+ imported = Callable(Callable::Macro, [fn = std::move(*callable), scopeName](const CallParams& params, OutStream& stream, RenderContext& context) {
+ ImportedMacroRenderer::InvokeMacro(scopeName, fn, params, stream, context);
+ });
+ }
+ else
+ {
+ continue;
+ }
+
+ if (m_namespace)
+ importedNs[var.first] = std::move(imported);
+ else
+ values.GetCurrentScope()[mappedP->second] = std::move(imported);
+ }
+
+ if (m_namespace)
+ values.GetCurrentScope()[m_namespace.value()] = CreateMapAdapter(std::move(importedNs));
+}
+
+std::vector<ArgumentInfo> MacroStatement::PrepareMacroParams(RenderContext& values)
+{
+ std::vector<ArgumentInfo> preparedParams;
+
+ for (auto& p : m_params)
+ {
+ ArgumentInfo info(p.paramName, !p.defaultValue);
+ if (p.defaultValue)
+ info.defaultVal = p.defaultValue->Evaluate(values);
+ preparedParams.push_back(std::move(info));
+ }
+
+ return preparedParams;
+}
+
+void MacroStatement::Render(OutStream&, RenderContext& values)
+{
+ auto p = PrepareMacroParams(values);
+
+ values.GetCurrentScope()[m_name] = Callable(Callable::Macro, [this, params = std::move(p)](const CallParams& callParams, OutStream& stream, RenderContext& context) {
+ InvokeMacroRenderer(params, callParams, stream, context);
+ });
+}
+
+void MacroStatement::InvokeMacroRenderer(const std::vector<ArgumentInfo>& params, const CallParams& callParams, OutStream& stream, RenderContext& context)
+{
+ InternalValueMap callArgs;
+ InternalValueMap kwArgs;
+ InternalValueList varArgs;
+
+ SetupCallArgs(params, callParams, context, callArgs, kwArgs, varArgs);
+ InternalValueList arguments;
+ InternalValueList defaults;
+ for (auto& a : params)
+ {
+ arguments.emplace_back(a.name);
+ defaults.emplace_back(a.defaultVal);
+ }
+
+ auto& scope = context.EnterScope();
+ for (auto& a : callArgs)
+ scope[a.first] = std::move(a.second);
+
+ scope["kwargs"s] = CreateMapAdapter(std::move(kwArgs));
+ scope["varargs"s] = ListAdapter::CreateAdapter(std::move(varArgs));
+
+ scope["name"s] = static_cast<std::string>(m_name);
+ scope["arguments"s] = ListAdapter::CreateAdapter(std::move(arguments));
+ scope["defaults"s] = ListAdapter::CreateAdapter(std::move(defaults));
+
+ m_mainBody->Render(stream, context);
+
+ context.ExitScope();
+}
+
+void MacroStatement::SetupCallArgs(const std::vector<ArgumentInfo>& argsInfo,
+ const CallParams& callParams,
+ RenderContext& /* context */,
+ InternalValueMap& callArgs,
+ InternalValueMap& kwArgs,
+ InternalValueList& varArgs)
+{
+ bool isSucceeded = true;
+ ParsedArguments args = helpers::ParseCallParams(argsInfo, callParams, isSucceeded);
+
+ for (auto& a : args.args)
+ callArgs[a.first] = std::move(a.second);
+
+ for (auto& a : args.extraKwArgs)
+ kwArgs[a.first] = std::move(a.second);
+
+ for (auto& a : args.extraPosArgs)
+ varArgs.push_back(std::move(a));
+}
+
+void MacroStatement::SetupMacroScope(InternalValueMap&)
+{
+ ;
+}
+
+void MacroCallStatement::Render(OutStream& os, RenderContext& values)
+{
+ bool isMacroFound = false;
+ auto macroPtr = values.FindValue(m_macroName, isMacroFound);
+ if (!isMacroFound)
+ return;
+
+ auto& fnVal = macroPtr->second;
+ const Callable* callable = GetIf<Callable>(&fnVal);
+ if (callable == nullptr || callable->GetType() == Callable::Type::Expression)
+ return;
+
+ auto& curScope = values.GetCurrentScope();
+ auto callerP = curScope.find("caller");
+ bool hasCallerVal = callerP != curScope.end();
+ InternalValue prevCaller;
+ if (hasCallerVal)
+ prevCaller = callerP->second;
+
+ auto p = PrepareMacroParams(values);
+
+ curScope["caller"] = Callable(Callable::Macro, [this, params = std::move(p)](const CallParams& callParams, OutStream& stream, RenderContext& context) {
+ InvokeMacroRenderer(params, callParams, stream, context);
+ });
+
+ auto callParams = helpers::EvaluateCallParams(m_callParams, values);
+ callable->GetStatementCallable()(callParams, os, values);
+
+ if (hasCallerVal)
+ curScope["caller"] = prevCaller;
+ else
+ values.GetCurrentScope().erase("caller");
+}
+
+void MacroCallStatement::SetupMacroScope(InternalValueMap&) {}
+
+void DoStatement::Render(OutStream& /*os*/, RenderContext& values)
+{
+ m_expr->Evaluate(values);
+}
+
+void WithStatement::Render(OutStream& os, RenderContext& values)
+{
+ auto innerValues = values.Clone(true);
+ auto& scope = innerValues.EnterScope();
+
+ for (auto& var : m_scopeVars)
+ scope[var.first] = var.second->Evaluate(values);
+
+ m_mainBody->Render(os, innerValues);
+
+ innerValues.ExitScope();
+}
+
+void FilterStatement::Render(OutStream& os, RenderContext& values)
+{
+ TargetString arg;
+ auto argStream = values.GetRendererCallback()->GetStreamOnString(arg);
+ auto innerValues = values.Clone(true);
+ m_body->Render(argStream, innerValues);
+ const auto result = m_expr->Evaluate(std::move(arg), values);
+ os.WriteValue(result);
+}
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/statements.h b/contrib/libs/jinja2cpp/src/statements.h
new file mode 100644
index 0000000000..2a7db94a87
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/statements.h
@@ -0,0 +1,661 @@
+#ifndef JINJA2CPP_SRC_STATEMENTS_H
+#define JINJA2CPP_SRC_STATEMENTS_H
+
+#include "renderer.h"
+#include "expression_evaluator.h"
+
+#include <string>
+#include <vector>
+
+namespace jinja2
+{
+class Statement : public VisitableRendererBase
+{
+public:
+ VISITABLE_STATEMENT();
+};
+
+template<typename T = Statement>
+using StatementPtr = std::shared_ptr<T>;
+
+template<typename CharT>
+class TemplateImpl;
+
+struct MacroParam
+{
+ std::string paramName;
+ ExpressionEvaluatorPtr<> defaultValue;
+};
+inline bool operator==(const MacroParam& lhs, const MacroParam& rhs)
+{
+ if (lhs.paramName != rhs.paramName)
+ return false;
+ if (lhs.defaultValue != rhs.defaultValue)
+ return false;
+ return true;
+}
+
+using MacroParams = std::vector<MacroParam>;
+
+class ForStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ ForStatement(std::vector<std::string> vars, ExpressionEvaluatorPtr<> expr, ExpressionEvaluatorPtr<> ifExpr, bool isRecursive)
+ : m_vars(std::move(vars))
+ , m_value(expr)
+ , m_ifExpr(ifExpr)
+ , m_isRecursive(isRecursive)
+ {
+ }
+
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+
+ void SetElseBody(RendererPtr renderer)
+ {
+ m_elseBody = std::move(renderer);
+ }
+
+ void Render(OutStream& os, RenderContext& values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ForStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_vars != val->m_vars)
+ return false;
+ if (m_value != val->m_value)
+ return false;
+ if (m_ifExpr != val->m_ifExpr)
+ return false;
+ if (m_isRecursive != val->m_isRecursive)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ if (m_elseBody != val->m_elseBody)
+ return false;
+ return true;
+ }
+
+private:
+ void RenderLoop(const InternalValue &loopVal, OutStream &os,
+ RenderContext &values, int level);
+ ListAdapter CreateFilteredAdapter(const ListAdapter& loopItems, RenderContext& values) const;
+
+private:
+ std::vector<std::string> m_vars;
+ ExpressionEvaluatorPtr<> m_value;
+ ExpressionEvaluatorPtr<> m_ifExpr;
+ bool m_isRecursive{};
+ RendererPtr m_mainBody;
+ RendererPtr m_elseBody;
+};
+
+class ElseBranchStatement;
+
+class IfStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ IfStatement(ExpressionEvaluatorPtr<> expr)
+ : m_expr(expr)
+ {
+ }
+
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+
+ void AddElseBranch(StatementPtr<ElseBranchStatement> branch)
+ {
+ m_elseBranches.push_back(branch);
+ }
+
+ void Render(OutStream& os, RenderContext& values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const IfStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ if (m_elseBranches != val->m_elseBranches)
+ return false;
+ return true;
+ }
+private:
+ ExpressionEvaluatorPtr<> m_expr;
+ RendererPtr m_mainBody;
+ std::vector<StatementPtr<ElseBranchStatement>> m_elseBranches;
+};
+
+
+class ElseBranchStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ ElseBranchStatement(ExpressionEvaluatorPtr<> expr)
+ : m_expr(expr)
+ {
+ }
+
+ bool ShouldRender(RenderContext& values) const;
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+ void Render(OutStream& os, RenderContext& values) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ElseBranchStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ return true;
+ }
+
+private:
+ ExpressionEvaluatorPtr<> m_expr;
+ RendererPtr m_mainBody;
+};
+
+class SetStatement : public Statement
+{
+public:
+ SetStatement(std::vector<std::string> fields)
+ : m_fields(std::move(fields))
+ {
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const SetStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_fields != val->m_fields)
+ return false;
+ return true;
+ }
+protected:
+ void AssignBody(InternalValue, RenderContext&);
+
+private:
+ const std::vector<std::string> m_fields;
+};
+
+class SetLineStatement final : public SetStatement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ SetLineStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<> expr)
+ : SetStatement(std::move(fields)), m_expr(std::move(expr))
+ {
+ }
+
+ void Render(OutStream& os, RenderContext& values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const SetLineStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ return true;
+ }
+private:
+ const ExpressionEvaluatorPtr<> m_expr;
+};
+
+class SetBlockStatement : public SetStatement
+{
+public:
+ using SetStatement::SetStatement;
+
+ void SetBody(RendererPtr renderer)
+ {
+ m_body = std::move(renderer);
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const SetBlockStatement*>(&other);
+ if (!val)
+ return false;
+ if (!SetStatement::IsEqual(*val))
+ return false;
+ if (m_body != val->m_body)
+ return false;
+ return true;
+ }
+protected:
+ InternalValue RenderBody(RenderContext&);
+
+private:
+ RendererPtr m_body;
+};
+
+class SetRawBlockStatement final : public SetBlockStatement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ using SetBlockStatement::SetBlockStatement;
+
+ void Render(OutStream&, RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const SetRawBlockStatement*>(&other);
+ if (!val)
+ return false;
+ if (!SetBlockStatement::IsEqual(*val))
+ return false;
+ return true;
+ }
+};
+
+class SetFilteredBlockStatement final : public SetBlockStatement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ explicit SetFilteredBlockStatement(std::vector<std::string> fields, ExpressionEvaluatorPtr<ExpressionFilter> expr)
+ : SetBlockStatement(std::move(fields)), m_expr(std::move(expr))
+ {
+ }
+
+ void Render(OutStream&, RenderContext&) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const SetFilteredBlockStatement*>(&other);
+ if (!val)
+ return false;
+ if (!SetBlockStatement::IsEqual(*val))
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ return true;
+ }
+
+private:
+ const ExpressionEvaluatorPtr<ExpressionFilter> m_expr;
+};
+
+class ParentBlockStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ ParentBlockStatement(std::string name, bool isScoped)
+ : m_name(std::move(name))
+ , m_isScoped(isScoped)
+ {
+ }
+
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+ void Render(OutStream &os, RenderContext &values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ParentBlockStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_name != val->m_name)
+ return false;
+ if (m_isScoped != val->m_isScoped)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ return true;
+ }
+
+private:
+ std::string m_name;
+ bool m_isScoped{};
+ RendererPtr m_mainBody;
+};
+
+class BlockStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ BlockStatement(std::string name)
+ : m_name(std::move(name))
+ {
+ }
+
+ auto& GetName() const {return m_name;}
+
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+ void Render(OutStream &os, RenderContext &values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const BlockStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_name != val->m_name)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ return true;
+ }
+private:
+ std::string m_name;
+ RendererPtr m_mainBody;
+};
+
+class ExtendsStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ using BlocksCollection = std::unordered_map<std::string, StatementPtr<BlockStatement>>;
+
+ ExtendsStatement(std::string name, bool isPath)
+ : m_templateName(std::move(name))
+ , m_isPath(isPath)
+ {
+ }
+
+ void Render(OutStream &os, RenderContext &values) override;
+ void AddBlock(StatementPtr<BlockStatement> block)
+ {
+ m_blocks[block->GetName()] = block;
+ }
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ExtendsStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_templateName != val->m_templateName)
+ return false;
+ if (m_isPath != val->m_isPath)
+ return false;
+ if (m_blocks != val->m_blocks)
+ return false;
+ return true;
+ }
+private:
+ std::string m_templateName;
+ bool m_isPath{};
+ BlocksCollection m_blocks;
+ void DoRender(OutStream &os, RenderContext &values);
+};
+
+class IncludeStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ IncludeStatement(bool ignoreMissing, bool withContext)
+ : m_ignoreMissing(ignoreMissing)
+ , m_withContext(withContext)
+ {}
+
+ void SetIncludeNamesExpr(ExpressionEvaluatorPtr<> expr)
+ {
+ m_expr = std::move(expr);
+ }
+
+ void Render(OutStream& os, RenderContext& values) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const IncludeStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_ignoreMissing != val->m_ignoreMissing)
+ return false;
+ if (m_withContext != val->m_withContext)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ return true;
+ }
+private:
+ bool m_ignoreMissing{};
+ bool m_withContext{};
+ ExpressionEvaluatorPtr<> m_expr;
+};
+
+class ImportStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ explicit ImportStatement(bool withContext)
+ : m_withContext(withContext)
+ {}
+
+ void SetImportNameExpr(ExpressionEvaluatorPtr<> expr)
+ {
+ m_nameExpr = std::move(expr);
+ }
+
+ void SetNamespace(std::string name)
+ {
+ m_namespace = std::move(name);
+ }
+
+ void AddNameToImport(std::string name, std::string alias)
+ {
+ m_namesToImport[std::move(name)] = std::move(alias);
+ }
+
+ void Render(OutStream& os, RenderContext& values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ImportStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_namespace != val->m_namespace)
+ return false;
+ if (m_withContext != val->m_withContext)
+ return false;
+ if (m_namesToImport != val->m_namesToImport)
+ return false;
+ if (m_nameExpr != val->m_nameExpr)
+ return false;
+ if (m_renderer != val->m_renderer)
+ return false;
+ return true;
+ }
+private:
+ void ImportNames(RenderContext& values, InternalValueMap& importedScope, const std::string& scopeName) const;
+
+private:
+ bool m_withContext{};
+ RendererPtr m_renderer;
+ ExpressionEvaluatorPtr<> m_nameExpr;
+ std::optional<std::string> m_namespace;
+ std::unordered_map<std::string, std::string> m_namesToImport;
+};
+
+class MacroStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ MacroStatement(std::string name, MacroParams params)
+ : m_name(std::move(name))
+ , m_params(std::move(params))
+ {
+ }
+
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+
+ void Render(OutStream &os, RenderContext &values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const MacroStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_name != val->m_name)
+ return false;
+ if (m_params != val->m_params)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ return true;
+ }
+
+protected:
+ void InvokeMacroRenderer(const std::vector<ArgumentInfo>& params, const CallParams& callParams, OutStream& stream, RenderContext& context);
+ void SetupCallArgs(const std::vector<ArgumentInfo>& argsInfo, const CallParams& callParams, RenderContext& context, InternalValueMap& callArgs, InternalValueMap& kwArgs, InternalValueList& varArgs);
+ virtual void SetupMacroScope(InternalValueMap& scope);
+ std::vector<ArgumentInfo> PrepareMacroParams(RenderContext& values);
+
+protected:
+ std::string m_name;
+ MacroParams m_params;
+ RendererPtr m_mainBody;
+};
+
+class MacroCallStatement : public MacroStatement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ MacroCallStatement(std::string macroName, CallParamsInfo callParams, MacroParams callbackParams)
+ : MacroStatement("$call$", std::move(callbackParams))
+ , m_macroName(std::move(macroName))
+ , m_callParams(std::move(callParams))
+ {
+ }
+
+ void Render(OutStream &os, RenderContext &values) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const MacroCallStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_macroName != val->m_macroName)
+ return false;
+ if (m_callParams != val->m_callParams)
+ return false;
+ return true;
+ }
+protected:
+ void SetupMacroScope(InternalValueMap& scope) override;
+
+protected:
+ std::string m_macroName;
+ CallParamsInfo m_callParams;
+};
+
+class DoStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ DoStatement(ExpressionEvaluatorPtr<> expr) : m_expr(expr) {}
+
+ void Render(OutStream &os, RenderContext &values) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const DoStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ return true;
+ }
+private:
+ ExpressionEvaluatorPtr<> m_expr;
+};
+
+class WithStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ void SetScopeVars(std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> vars)
+ {
+ m_scopeVars = std::move(vars);
+ }
+ void SetMainBody(RendererPtr renderer)
+ {
+ m_mainBody = std::move(renderer);
+ }
+
+ void Render(OutStream &os, RenderContext &values) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const WithStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_scopeVars != val->m_scopeVars)
+ return false;
+ if (m_mainBody != val->m_mainBody)
+ return false;
+ return true;
+ }
+private:
+ std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> m_scopeVars;
+ RendererPtr m_mainBody;
+};
+
+class FilterStatement : public Statement
+{
+public:
+ VISITABLE_STATEMENT();
+
+ explicit FilterStatement(ExpressionEvaluatorPtr<ExpressionFilter> expr)
+ : m_expr(std::move(expr)) {}
+
+ void SetBody(RendererPtr renderer)
+ {
+ m_body = std::move(renderer);
+ }
+
+ void Render(OutStream &, RenderContext &) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const FilterStatement*>(&other);
+ if (!val)
+ return false;
+ if (m_expr != val->m_expr)
+ return false;
+ if (m_body != val->m_body)
+ return false;
+ return true;
+ }
+private:
+ ExpressionEvaluatorPtr<ExpressionFilter> m_expr;
+ RendererPtr m_body;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_STATEMENTS_H
diff --git a/contrib/libs/jinja2cpp/src/string_converter_filter.cpp b/contrib/libs/jinja2cpp/src/string_converter_filter.cpp
new file mode 100644
index 0000000000..574fbe0464
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/string_converter_filter.cpp
@@ -0,0 +1,395 @@
+#include "filters.h"
+#include "testers.h"
+#include "value_visitors.h"
+#include "value_helpers.h"
+
+#include <algorithm>
+#include <numeric>
+#include <regex>
+#include <sstream>
+
+#include <boost/algorithm/string/trim_all.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+namespace ba = boost::algorithm;
+
+namespace jinja2
+{
+
+namespace filters
+{
+
+template<typename D>
+struct StringEncoder : public visitors::BaseVisitor<TargetString>
+{
+ using BaseVisitor::operator();
+
+ template<typename CharT>
+ TargetString operator() (const std::basic_string<CharT>& str) const
+ {
+ std::basic_string<CharT> result;
+
+ for (auto& ch : str)
+ {
+ static_cast<const D*>(this)->EncodeChar(ch, [&result](auto ... chs) {AppendChar(result, chs...);});
+ }
+
+ return TargetString(std::move(result));
+ }
+
+ template<typename CharT>
+ TargetString operator() (const std::basic_string_view<CharT>& str) const
+ {
+ std::basic_string<CharT> result;
+
+ for (auto& ch : str)
+ {
+ static_cast<const D*>(this)->EncodeChar(ch, [&result](auto ... chs) {AppendChar(result, chs...);});
+ }
+
+ return TargetString(std::move(result));
+ }
+
+ template<typename Str, typename CharT>
+ static void AppendChar(Str& str, CharT ch)
+ {
+ str.push_back(static_cast<typename Str::value_type>(ch));
+ }
+ template<typename Str, typename CharT, typename ... Args>
+ static void AppendChar(Str& str, CharT ch, Args ... chs)
+ {
+ str.push_back(static_cast<typename Str::value_type>(ch));
+ AppendChar(str, chs...);
+ }
+};
+
+template<typename Fn>
+struct GenericStringEncoder : public StringEncoder<GenericStringEncoder<Fn>>
+{
+ GenericStringEncoder(Fn fn) : m_fn(std::move(fn)) {}
+
+ template<typename CharT, typename AppendFn>
+ void EncodeChar(CharT ch, AppendFn&& fn) const
+ {
+ m_fn(ch, std::forward<AppendFn>(fn));
+ }
+
+ mutable Fn m_fn;
+};
+
+struct UrlStringEncoder : public StringEncoder<UrlStringEncoder>
+{
+ template<typename CharT, typename Fn>
+ void EncodeChar(CharT ch, Fn&& fn) const
+ {
+ enum EncodeStyle
+ {
+ None,
+ Percent
+ };
+
+ EncodeStyle encStyle = None;
+ switch (ch)
+ {
+ case ' ':
+ fn('+');
+ return;
+ case '+': case '\"': case '%': case '-':
+ case '!': case '#': case '$': case '&':
+ case '\'': case '(': case ')': case '*':
+ case ',': case '/': case ':': case ';':
+ case '=': case '?': case '@': case '[':
+ case ']':
+ encStyle = Percent;
+ break;
+ default:
+ if (AsUnsigned(ch) > 0x7f)
+ encStyle = Percent;
+ break;
+ }
+
+ if (encStyle == None)
+ {
+ fn(ch);
+ return;
+ }
+ union
+ {
+ uint32_t intCh;
+ uint8_t chars[4];
+ };
+ intCh = AsUnsigned(ch);
+ if (intCh > 0xffffff)
+ DoPercentEncoding(chars[3], fn);
+ if (intCh > 0xffff)
+ DoPercentEncoding(chars[2], fn);
+ if (intCh > 0xff)
+ DoPercentEncoding(chars[1], fn);
+ DoPercentEncoding(chars[0], fn);
+ }
+
+ template<typename Fn>
+ void DoPercentEncoding(uint8_t ch, Fn&& fn) const
+ {
+ char chars[] = "0123456789ABCDEF";
+ int ch1 = static_cast<int>(chars[(ch & 0xf0) >> 4]);
+ int ch2 = static_cast<int>(chars[ch & 0x0f]);
+ fn('%', ch1, ch2);
+ }
+
+ template<typename Ch, size_t SZ>
+ struct ToUnsigned;
+
+ template<typename Ch>
+ struct ToUnsigned<Ch, 1>
+ {
+ static auto Cast(Ch ch) {return static_cast<uint8_t>(ch);}
+ };
+
+ template<typename Ch>
+ struct ToUnsigned<Ch, 2>
+ {
+ static auto Cast(Ch ch) {return static_cast<uint16_t>(ch);}
+ };
+
+ template<typename Ch>
+ struct ToUnsigned<Ch, 4>
+ {
+ static auto Cast(Ch ch) {return static_cast<uint32_t>(ch);}
+ };
+
+ template<typename Ch>
+ auto AsUnsigned(Ch ch) const
+ {
+ return static_cast<uint32_t>(ToUnsigned<Ch, sizeof(Ch)>::Cast(ch));
+ }
+};
+
+StringConverter::StringConverter(FilterParams params, StringConverter::Mode mode)
+ : m_mode(mode)
+{
+ switch (m_mode)
+ {
+ case ReplaceMode:
+ ParseParams({{"old", true}, {"new", true}, {"count", false, static_cast<int64_t>(0)}}, params);
+ break;
+ case TruncateMode:
+ ParseParams({{"length", false, static_cast<int64_t>(255)}, {"killwords", false, false}, {"end", false, std::string("...")}, {"leeway", false}}, params);
+ break;
+ case CenterMode:
+ ParseParams({{"width", false, static_cast<int64_t>(80)}}, params);
+ break;
+ default: break;
+ }
+}
+
+InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContext& context)
+{
+ TargetString result;
+
+ auto isAlpha = ba::is_alpha();
+ auto isAlNum = ba::is_alnum();
+
+ switch (m_mode)
+ {
+ case TrimMode:
+ result = ApplyStringConverter(baseVal, [](auto strView) -> TargetString {
+ auto str = sv_to_string(strView);
+ ba::trim_all(str);
+ return TargetString(str);
+ });
+ break;
+ case TitleMode:
+ result = ApplyStringConverter<GenericStringEncoder>(baseVal, [isDelim = true, &isAlpha, &isAlNum](auto ch, auto&& fn) mutable {
+ if (isDelim && isAlpha(ch))
+ {
+ isDelim = false;
+ fn(std::toupper(ch, std::locale()));
+ return;
+ }
+
+ isDelim = !isAlNum(ch);
+ fn(ch);
+ });
+ break;
+ case WordCountMode:
+ {
+ int64_t wc = 0;
+ ApplyStringConverter<GenericStringEncoder>(baseVal, [isDelim = true, &wc, &isAlNum](auto ch, auto&&) mutable {
+ if (isDelim && isAlNum(ch))
+ {
+ isDelim = false;
+ wc ++;
+ return;
+ }
+ isDelim = !isAlNum(ch);
+ });
+ return InternalValue(wc);
+ }
+ case UpperMode:
+ result = ApplyStringConverter<GenericStringEncoder>(baseVal, [&isAlpha](auto ch, auto&& fn) mutable {
+ if (isAlpha(ch))
+ fn(std::toupper(ch, std::locale()));
+ else
+ fn(ch);
+ });
+ break;
+ case LowerMode:
+ result = ApplyStringConverter<GenericStringEncoder>(baseVal, [&isAlpha](auto ch, auto&& fn) mutable {
+ if (isAlpha(ch))
+ fn(std::tolower(ch, std::locale()));
+ else
+ fn(ch);
+ });
+ break;
+ case ReplaceMode:
+ result = ApplyStringConverter(baseVal, [this, &context](auto srcStr) -> TargetString {
+ std::decay_t<decltype(srcStr)> emptyStrView;
+ using CharT = typename decltype(emptyStrView)::value_type;
+ std::basic_string<CharT> emptyStr;
+ auto oldStr = GetAsSameString(srcStr, this->GetArgumentValue("old", context)).value_or(emptyStr);
+ auto newStr = GetAsSameString(srcStr, this->GetArgumentValue("new", context)).value_or(emptyStr);
+ auto count = ConvertToInt(this->GetArgumentValue("count", context));
+ auto str = sv_to_string(srcStr);
+ if (count == 0)
+ ba::replace_all(str, oldStr, newStr);
+ else
+ {
+ for (int64_t n = 0; n < count; ++ n)
+ ba::replace_first(str, oldStr, newStr);
+ }
+ return str;
+ });
+ break;
+ case TruncateMode:
+ result = ApplyStringConverter(baseVal, [this, &context, &isAlNum](auto srcStr) -> TargetString {
+ std::decay_t<decltype(srcStr)> emptyStrView;
+ using CharT = typename decltype(emptyStrView)::value_type;
+ std::basic_string<CharT> emptyStr;
+ auto length = ConvertToInt(this->GetArgumentValue("length", context));
+ auto killWords = ConvertToBool(this->GetArgumentValue("killwords", context));
+ auto end = GetAsSameString(srcStr, this->GetArgumentValue("end", context));
+ auto leeway = ConvertToInt(this->GetArgumentValue("leeway", context), 5);
+ if (static_cast<long long int>(srcStr.size()) <= length)
+ return sv_to_string(srcStr);
+
+ auto str = sv_to_string(srcStr);
+
+ if (killWords)
+ {
+ if (static_cast<long long int>(str.size()) > (length + leeway))
+ {
+ str.erase(str.begin() + static_cast<std::ptrdiff_t>(length), str.end());
+ str += end.value_or(emptyStr);
+ }
+ return str;
+ }
+
+ auto p = str.begin() + static_cast<std::ptrdiff_t>(length);
+ if (leeway != 0)
+ {
+ for (; leeway != 0 && p != str.end() && isAlNum(*p); -- leeway, ++ p);
+ if (p == str.end())
+ return TargetString(str);
+ }
+
+ if (isAlNum(*p))
+ {
+ for (; p != str.begin() && isAlNum(*p); -- p);
+ }
+ str.erase(p, str.end());
+ ba::trim_right(str);
+ str += end.value_or(emptyStr);
+
+ return TargetString(std::move(str));
+ });
+ break;
+ case UrlEncodeMode:
+ result = Apply<UrlStringEncoder>(baseVal);
+ break;
+ case CapitalMode:
+ result = ApplyStringConverter<GenericStringEncoder>(baseVal, [isFirstChar = true, &isAlpha](auto ch, auto&& fn) mutable {
+ if (isAlpha(ch))
+ {
+ if (isFirstChar)
+ fn(std::toupper(ch, std::locale()));
+ else
+ fn(std::tolower(ch, std::locale()));
+ }
+ else
+ fn(ch);
+
+ isFirstChar = false;
+ });
+ break;
+ case EscapeHtmlMode:
+ result = ApplyStringConverter<GenericStringEncoder>(baseVal, [](auto ch, auto&& fn) mutable {
+ switch(ch)
+ {
+ case '<':
+ fn('&', 'l', 't', ';');
+ break;
+ case '>':
+ fn('&', 'g', 't', ';');
+ break;
+ case '&':
+ fn('&', 'a', 'm', 'p', ';');
+ break;
+ case '\'':
+ fn('&', '#', '3', '9', ';');
+ break;
+ case '\"':
+ fn('&', '#', '3', '4', ';');
+ break;
+ default:
+ fn(ch);
+ break;
+ }
+ });
+ break;
+ case StriptagsMode:
+ result = ApplyStringConverter(baseVal, [](auto srcStr) -> TargetString {
+ auto str = sv_to_string(srcStr);
+ using StringT = decltype(str);
+ using CharT = typename StringT::value_type;
+ static const std::basic_regex<CharT> STRIPTAGS_RE(UNIVERSAL_STR("(<!--.*?-->|<[^>]*>)").GetValueStr<CharT>());
+ str = std::regex_replace(str, STRIPTAGS_RE, UNIVERSAL_STR("").GetValueStr<CharT>());
+ ba::trim_all(str);
+ static const StringT html_entities [] {
+ UNIVERSAL_STR("&amp;").GetValueStr<CharT>(), UNIVERSAL_STR("&").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&apos;").GetValueStr<CharT>(), UNIVERSAL_STR("\'").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&gt;").GetValueStr<CharT>(), UNIVERSAL_STR(">").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&lt;").GetValueStr<CharT>(), UNIVERSAL_STR("<").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&quot;").GetValueStr<CharT>(), UNIVERSAL_STR("\"").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&#39;").GetValueStr<CharT>(), UNIVERSAL_STR("\'").GetValueStr<CharT>(),
+ UNIVERSAL_STR("&#34;").GetValueStr<CharT>(), UNIVERSAL_STR("\"").GetValueStr<CharT>(),
+ };
+ for (auto it = std::begin(html_entities), end = std::end(html_entities); it < end; it += 2)
+ {
+ ba::replace_all(str, *it, *(it + 1));
+ }
+ return str;
+ });
+ break;
+ case CenterMode:
+ result = ApplyStringConverter(baseVal, [this, &context](auto srcStr) -> TargetString {
+ auto width = ConvertToInt(this->GetArgumentValue("width", context));
+ auto str = sv_to_string(srcStr);
+ auto string_length = static_cast<long long int>(str.size());
+ if (string_length >= width)
+ return str;
+ auto whitespaces = width - string_length;
+ str = decltype(str)(static_cast<std::string::size_type>(whitespaces + 1) / 2, ' ') + str;
+ str.append(static_cast<std::string::size_type>(whitespaces / 2), ' ');
+ return TargetString(std::move(str));
+ });
+ break;
+ default:
+ break;
+ }
+
+ return std::move(result);
+}
+
+} // namespace filters
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/template.cpp b/contrib/libs/jinja2cpp/src/template.cpp
new file mode 100644
index 0000000000..2734387508
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/template.cpp
@@ -0,0 +1,190 @@
+#include "jinja2cpp/template.h"
+#include "template_impl.h"
+
+#include <fmt/format.h>
+
+#include <fstream>
+#include <sstream>
+
+namespace jinja2
+{
+bool operator==(const Template& lhs, const Template& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+
+bool operator==(const TemplateW& lhs, const TemplateW& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+
+template<typename CharT>
+auto GetImpl(std::shared_ptr<ITemplateImpl> impl)
+{
+ return static_cast<TemplateImpl<CharT>*>(impl.get());
+}
+
+Template::Template(TemplateEnv* env)
+ : m_impl(new TemplateImpl<char>(env))
+{
+
+}
+
+Template::~Template() = default;
+
+Result<void> Template::Load(const char* tpl, std::string tplName)
+{
+ std::string t(tpl);
+ auto result = GetImpl<char>(m_impl)->Load(std::move(t), std::move(tplName));
+ return !result ? Result<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+Result<void> Template::Load(const std::string& str, std::string tplName)
+{
+ auto result = GetImpl<char>(m_impl)->Load(str, std::move(tplName));
+ return !result ? Result<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+Result<void> Template::Load(std::istream& stream, std::string tplName)
+{
+ std::string t;
+
+ while (stream.good() && !stream.eof())
+ {
+ char buff[0x10000];
+ stream.read(buff, sizeof(buff));
+ auto read = stream.gcount();
+ if (read)
+ t.append(buff, buff + read);
+ }
+
+ auto result = GetImpl<char>(m_impl)->Load(std::move(t), std::move(tplName));
+ return !result ? Result<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+Result<void> Template::LoadFromFile(const std::string& fileName)
+{
+ std::ifstream file(fileName);
+
+ if (!file.good())
+ return Result<void>();
+
+ return Load(file, fileName);
+}
+
+Result<void> Template::Render(std::ostream& os, const jinja2::ValuesMap& params)
+{
+ std::string buffer;
+ auto result = GetImpl<char>(m_impl)->Render(buffer, params);
+
+ if (!result)
+ os.write(buffer.data(), buffer.size());
+
+ return !result ? Result<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+Result<std::string> Template::RenderAsString(const jinja2::ValuesMap& params)
+{
+ std::string buffer;
+ auto result = GetImpl<char>(m_impl)->Render(buffer, params);
+ return !result ? Result<std::string>(std::move(buffer)) : Result<std::string>(nonstd::make_unexpected(std::move(result.get())));;
+}
+
+Result<GenericMap> Template::GetMetadata()
+{
+ return GetImpl<char>(m_impl)->GetMetadata();
+}
+
+Result<MetadataInfo<char>> Template::GetMetadataRaw()
+{
+ return GetImpl<char>(m_impl)->GetMetadataRaw();
+}
+
+bool Template::IsEqual(const Template& other) const
+{
+ return m_impl == other.m_impl;
+}
+
+TemplateW::TemplateW(TemplateEnv* env)
+ : m_impl(new TemplateImpl<wchar_t>(env))
+{
+
+}
+
+TemplateW::~TemplateW() = default;
+
+ResultW<void> TemplateW::Load(const wchar_t* tpl, std::string tplName)
+{
+ std::wstring t(tpl);
+ auto result = GetImpl<wchar_t>(m_impl)->Load(t, std::move(tplName));
+ return !result ? ResultW<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+ResultW<void> TemplateW::Load(const std::wstring& str, std::string tplName)
+{
+ auto result = GetImpl<wchar_t>(m_impl)->Load(str, std::move(tplName));
+ return !result ? ResultW<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+ResultW<void> TemplateW::Load(std::wistream& stream, std::string tplName)
+{
+ std::wstring t;
+
+ while (stream.good() && !stream.eof())
+ {
+ wchar_t buff[0x10000];
+ stream.read(buff, sizeof(buff));
+ auto read = stream.gcount();
+ if (read)
+ t.append(buff, buff + read);
+ }
+
+ auto result = GetImpl<wchar_t>(m_impl)->Load(t, std::move(tplName));
+ return !result ? ResultW<void>() : nonstd::make_unexpected(std::move(result.get()));
+}
+
+ResultW<void> TemplateW::LoadFromFile(const std::string& fileName)
+{
+ std::wifstream file(fileName);
+
+ if (!file.good())
+ return ResultW<void>();
+
+ return Load(file, fileName);
+}
+
+ResultW<void> TemplateW::Render(std::wostream& os, const jinja2::ValuesMap& params)
+{
+ std::wstring buffer;
+ auto result = GetImpl<wchar_t>(m_impl)->Render(buffer, params);
+ if (!result)
+ os.write(buffer.data(), buffer.size());
+ return !result ? ResultW<void>() : ResultW<void>(nonstd::make_unexpected(std::move(result.get())));
+}
+
+ResultW<std::wstring> TemplateW::RenderAsString(const jinja2::ValuesMap& params)
+{
+ std::wstring buffer;
+ auto result = GetImpl<wchar_t>(m_impl)->Render(buffer, params);
+
+ return !result ? buffer : ResultW<std::wstring>(nonstd::make_unexpected(std::move(result.get())));
+}
+
+ResultW<GenericMap> TemplateW::GetMetadata()
+{
+ return GenericMap();
+ // GetImpl<wchar_t>(m_impl)->GetMetadata();
+}
+
+ResultW<MetadataInfo<wchar_t>> TemplateW::GetMetadataRaw()
+{
+ return MetadataInfo<wchar_t>();
+ // GetImpl<wchar_t>(m_impl)->GetMetadataRaw();
+ ;
+}
+bool TemplateW::IsEqual(const TemplateW& other) const
+{
+ return m_impl == other.m_impl;
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/template_env.cpp b/contrib/libs/jinja2cpp/src/template_env.cpp
new file mode 100644
index 0000000000..239a4f02e1
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/template_env.cpp
@@ -0,0 +1,95 @@
+#include <jinja2cpp/template.h>
+#include <jinja2cpp/template_env.h>
+
+namespace jinja2
+{
+template<typename CharT>
+struct TemplateFunctions;
+
+template<>
+struct TemplateFunctions<char>
+{
+ using ResultType = nonstd::expected<Template, ErrorInfo>;
+ static Template CreateTemplate(TemplateEnv* env) { return Template(env); }
+ static auto LoadFile(const std::string& fileName, const IFilesystemHandler* fs) { return fs->OpenStream(fileName); }
+};
+
+template<>
+struct TemplateFunctions<wchar_t>
+{
+ using ResultType = nonstd::expected<TemplateW, ErrorInfoW>;
+ static TemplateW CreateTemplate(TemplateEnv* env) { return TemplateW(env); }
+ static auto LoadFile(const std::string& fileName, const IFilesystemHandler* fs) { return fs->OpenWStream(fileName); }
+};
+
+template<typename CharT, typename T, typename Cache>
+auto TemplateEnv::LoadTemplateImpl(TemplateEnv* env, std::string fileName, const T& filesystemHandlers, Cache& cache)
+{
+ using Functions = TemplateFunctions<CharT>;
+ using ResultType = typename Functions::ResultType;
+ using ErrorType = typename ResultType::error_type;
+ auto tpl = Functions::CreateTemplate(env);
+
+ {
+ std::shared_lock<std::shared_timed_mutex> l(m_guard);
+ auto p = cache.find(fileName);
+ if (p != cache.end())
+ {
+ if (m_settings.autoReload)
+ {
+ auto lastModified = p->second.handler->GetLastModificationDate(fileName);
+ if (!lastModified || (p->second.lastModification && lastModified.value() <= p->second.lastModification.value()))
+ return ResultType(p->second.tpl);
+ }
+ else
+ return ResultType(p->second.tpl);
+ }
+ }
+
+ for (auto& fh : filesystemHandlers)
+ {
+ if (!fh.prefix.empty() && fileName.find(fh.prefix) != 0)
+ continue;
+
+ auto stream = Functions::LoadFile(fileName, fh.handler.get());
+ if (stream)
+ {
+ auto res = tpl.Load(*stream, fileName);
+ if (!res)
+ return ResultType(res.get_unexpected());
+
+ if (m_settings.cacheSize != 0)
+ {
+ auto lastModified = fh.handler->GetLastModificationDate(fileName);
+ std::unique_lock<std::shared_timed_mutex> l(m_guard);
+ auto& cacheEntry = cache[fileName];
+ cacheEntry.tpl = tpl;
+ cacheEntry.handler = fh.handler;
+ cacheEntry.lastModification = lastModified;
+ }
+
+ return ResultType(tpl);
+ }
+ }
+
+ typename ErrorType::Data errorData;
+ errorData.code = ErrorCode::FileNotFound;
+ errorData.srcLoc.col = 1;
+ errorData.srcLoc.line = 1;
+ errorData.srcLoc.fileName = "";
+ errorData.extraParams.push_back(Value(fileName));
+
+ return ResultType(nonstd::make_unexpected(ErrorType(errorData)));
+}
+
+nonstd::expected<Template, ErrorInfo> TemplateEnv::LoadTemplate(std::string fileName)
+{
+ return LoadTemplateImpl<char>(this, std::move(fileName), m_filesystemHandlers, m_templateCache);
+}
+
+nonstd::expected<TemplateW, ErrorInfoW> TemplateEnv::LoadTemplateW(std::string fileName)
+{
+ return LoadTemplateImpl<wchar_t>(this, std::move(fileName), m_filesystemHandlers, m_templateWCache);
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/template_impl.h b/contrib/libs/jinja2cpp/src/template_impl.h
new file mode 100644
index 0000000000..cc9c9a2a42
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/template_impl.h
@@ -0,0 +1,487 @@
+#ifndef JINJA2CPP_SRC_TEMPLATE_IMPL_H
+#define JINJA2CPP_SRC_TEMPLATE_IMPL_H
+
+#include "internal_value.h"
+#include "jinja2cpp/binding/rapid_json.h"
+#include "jinja2cpp/template_env.h"
+#include "jinja2cpp/value.h"
+#include "renderer.h"
+#include "template_parser.h"
+#include "value_visitors.h"
+
+#include <boost/optional.hpp>
+#include <boost/predef/other/endian.h>
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+#include <rapidjson/error/en.h>
+
+#include <string>
+
+namespace jinja2
+{
+namespace detail
+{
+template<size_t Sz>
+struct RapidJsonEncodingType;
+
+template<>
+struct RapidJsonEncodingType<1>
+{
+ using type = rapidjson::UTF8<char>;
+};
+
+#ifdef BOOST_ENDIAN_BIG_BYTE
+template<>
+struct RapidJsonEncodingType<2>
+{
+ using type = rapidjson::UTF16BE<wchar_t>;
+};
+
+template<>
+struct RapidJsonEncodingType<4>
+{
+ using type = rapidjson::UTF32BE<wchar_t>;
+};
+#else
+template<>
+struct RapidJsonEncodingType<2>
+{
+ using type = rapidjson::UTF16LE<wchar_t>;
+};
+
+template<>
+struct RapidJsonEncodingType<4>
+{
+ using type = rapidjson::UTF32LE<wchar_t>;
+};
+#endif
+} // namespace detail
+
+extern void SetupGlobals(InternalValueMap& globalParams);
+
+class ITemplateImpl
+{
+public:
+ virtual ~ITemplateImpl() = default;
+};
+
+
+template<typename U>
+struct TemplateLoader;
+
+template<>
+struct TemplateLoader<char>
+{
+ static auto Load(const std::string& fileName, TemplateEnv* env)
+ {
+ return env->LoadTemplate(fileName);
+ }
+};
+
+template<>
+struct TemplateLoader<wchar_t>
+{
+ static auto Load(const std::string& fileName, TemplateEnv* env)
+ {
+ return env->LoadTemplateW(fileName);
+ }
+};
+
+template<typename CharT>
+class GenericStreamWriter : public OutStream::StreamWriter
+{
+public:
+ explicit GenericStreamWriter(std::basic_string<CharT>& os)
+ : m_os(os)
+ {}
+
+ // StreamWriter interface
+ void WriteBuffer(const void* ptr, size_t length) override
+ {
+ m_os.append(reinterpret_cast<const CharT*>(ptr), length);
+ }
+ void WriteValue(const InternalValue& val) override
+ {
+ Apply<visitors::ValueRenderer<CharT>>(val, m_os);
+ }
+
+private:
+ std::basic_string<CharT>& m_os;
+};
+
+template<typename CharT>
+class StringStreamWriter : public OutStream::StreamWriter
+{
+public:
+ explicit StringStreamWriter(std::basic_string<CharT>* targetStr)
+ : m_targetStr(targetStr)
+ {}
+
+ // StreamWriter interface
+ void WriteBuffer(const void* ptr, size_t length) override
+ {
+ m_targetStr->append(reinterpret_cast<const CharT*>(ptr), length);
+ // m_os.write(reinterpret_cast<const CharT*>(ptr), length);
+ }
+ void WriteValue(const InternalValue& val) override
+ {
+ Apply<visitors::ValueRenderer<CharT>>(val, *m_targetStr);
+ }
+
+private:
+ std::basic_string<CharT>* m_targetStr;
+};
+
+template<typename ErrorTpl1, typename ErrorTpl2>
+struct ErrorConverter;
+
+template<typename CharT1, typename CharT2>
+struct ErrorConverter<ErrorInfoTpl<CharT1>, ErrorInfoTpl<CharT2>>
+{
+ static ErrorInfoTpl<CharT1> Convert(const ErrorInfoTpl<CharT2>& srcError)
+ {
+ typename ErrorInfoTpl<CharT1>::Data errorData;
+ errorData.code = srcError.GetCode();
+ errorData.srcLoc = srcError.GetErrorLocation();
+ errorData.locationDescr = ConvertString<std::basic_string<CharT1>>(srcError.GetLocationDescr());
+ errorData.extraParams = srcError.GetExtraParams();
+
+ return ErrorInfoTpl<CharT1>(errorData);
+ }
+};
+
+template<typename CharT>
+struct ErrorConverter<ErrorInfoTpl<CharT>, ErrorInfoTpl<CharT>>
+{
+ static const ErrorInfoTpl<CharT>& Convert(const ErrorInfoTpl<CharT>& srcError)
+ {
+ return srcError;
+ }
+};
+
+template<typename CharT>
+inline bool operator==(const MetadataInfo<CharT>& lhs, const MetadataInfo<CharT>& rhs)
+{
+ if (lhs.metadata != rhs.metadata)
+ return false;
+ if (lhs.metadataType != rhs.metadataType)
+ return false;
+ if (lhs.location != rhs.location)
+ return false;
+ return true;
+}
+
+template<typename CharT>
+inline bool operator!=(const MetadataInfo<CharT>& lhs, const MetadataInfo<CharT>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const TemplateEnv& lhs, const TemplateEnv& rhs)
+{
+ return lhs.IsEqual(rhs);
+}
+inline bool operator!=(const TemplateEnv& lhs, const TemplateEnv& rhs)
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const SourceLocation& lhs, const SourceLocation& rhs)
+{
+ if (lhs.fileName != rhs.fileName)
+ return false;
+ if (lhs.line != rhs.line)
+ return false;
+ if (lhs.col != rhs.col)
+ return false;
+ return true;
+}
+inline bool operator!=(const SourceLocation& lhs, const SourceLocation& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename CharT>
+class TemplateImpl : public ITemplateImpl
+{
+public:
+ using ThisType = TemplateImpl<CharT>;
+
+ explicit TemplateImpl(TemplateEnv* env)
+ : m_env(env)
+ {
+ if (env)
+ m_settings = env->GetSettings();
+ }
+
+ auto GetRenderer() const {return m_renderer;}
+ auto GetTemplateName() const {};
+
+ boost::optional<ErrorInfoTpl<CharT>> Load(std::basic_string<CharT> tpl, std::string tplName)
+ {
+ m_template = std::move(tpl);
+ m_templateName = tplName.empty() ? std::string("noname.j2tpl") : std::move(tplName);
+ TemplateParser<CharT> parser(&m_template, m_settings, m_env, m_templateName);
+
+ auto parseResult = parser.Parse();
+ if (!parseResult)
+ return parseResult.error()[0];
+
+ m_renderer = *parseResult;
+ m_metadataInfo = parser.GetMetadataInfo();
+ return boost::optional<ErrorInfoTpl<CharT>>();
+ }
+
+ boost::optional<ErrorInfoTpl<CharT>> Render(std::basic_string<CharT>& os, const ValuesMap& params)
+ {
+ boost::optional<ErrorInfoTpl<CharT>> normalResult;
+
+ if (!m_renderer)
+ {
+ typename ErrorInfoTpl<CharT>::Data errorData;
+ errorData.code = ErrorCode::TemplateNotParsed;
+ errorData.srcLoc.col = 1;
+ errorData.srcLoc.line = 1;
+ errorData.srcLoc.fileName = "<unknown file>";
+
+ return ErrorInfoTpl<CharT>(errorData);
+ }
+
+ try
+ {
+ InternalValueMap extParams;
+ InternalValueMap intParams;
+
+ auto convertFn = [&intParams](const auto& params) {
+ for (auto& ip : params)
+ {
+ auto valRef = &ip.second.data();
+ auto newParam = visit(visitors::InputValueConvertor(false, true), *valRef);
+ if (!newParam)
+ intParams[ip.first] = ValueRef(static_cast<const Value&>(*valRef));
+ else
+ intParams[ip.first] = newParam.get();
+ }
+ };
+
+ if (m_env)
+ {
+ m_env->ApplyGlobals(convertFn);
+ std::swap(extParams, intParams);
+ }
+
+ convertFn(params);
+ SetupGlobals(extParams);
+
+ RendererCallback callback(this);
+ RenderContext context(intParams, extParams, &callback);
+ InitRenderContext(context);
+ OutStream outStream([writer = GenericStreamWriter<CharT>(os)]() mutable -> OutStream::StreamWriter* {return &writer;});
+ m_renderer->Render(outStream, context);
+ }
+ catch (const ErrorInfoTpl<char>& error)
+ {
+ return ErrorConverter<ErrorInfoTpl<CharT>, ErrorInfoTpl<char>>::Convert(error);
+ }
+ catch (const ErrorInfoTpl<wchar_t>& error)
+ {
+ return ErrorConverter<ErrorInfoTpl<CharT>, ErrorInfoTpl<wchar_t>>::Convert(error);
+ }
+ catch (const std::exception& ex)
+ {
+ typename ErrorInfoTpl<CharT>::Data errorData;
+ errorData.code = ErrorCode::UnexpectedException;
+ errorData.srcLoc.col = 1;
+ errorData.srcLoc.line = 1;
+ errorData.srcLoc.fileName = m_templateName;
+ errorData.extraParams.push_back(Value(std::string(ex.what())));
+
+ return ErrorInfoTpl<CharT>(errorData);
+ }
+
+ return normalResult;
+ }
+
+ InternalValueMap& InitRenderContext(RenderContext& context)
+ {
+ auto& curScope = context.GetCurrentScope();
+ return curScope;
+ }
+
+ using TplLoadResultType = std::variant<EmptyValue,
+ nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
+ nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>>;
+
+ using TplOrError = nonstd::expected<std::shared_ptr<TemplateImpl<CharT>>, ErrorInfoTpl<CharT>>;
+
+ TplLoadResultType LoadTemplate(const std::string& fileName)
+ {
+ if (!m_env)
+ return TplLoadResultType(EmptyValue());
+
+ auto tplWrapper = TemplateLoader<CharT>::Load(fileName, m_env);
+ if (!tplWrapper)
+ return TplLoadResultType(TplOrError(tplWrapper.get_unexpected()));
+
+ return TplLoadResultType(TplOrError(std::static_pointer_cast<ThisType>(tplWrapper.value().m_impl)));
+ }
+
+ TplLoadResultType LoadTemplate(const InternalValue& fileName)
+ {
+ auto name = GetAsSameString(std::string(), fileName);
+ if (!name)
+ {
+ typename ErrorInfoTpl<CharT>::Data errorData;
+ errorData.code = ErrorCode::InvalidTemplateName;
+ errorData.srcLoc.col = 1;
+ errorData.srcLoc.line = 1;
+ errorData.srcLoc.fileName = m_templateName;
+ errorData.extraParams.push_back(IntValue2Value(fileName));
+ return TplOrError(nonstd::make_unexpected(ErrorInfoTpl<CharT>(errorData)));
+ }
+
+ return LoadTemplate(name.value());
+ }
+
+ nonstd::expected<GenericMap, ErrorInfoTpl<CharT>> GetMetadata() const
+ {
+ auto& metadataString = m_metadataInfo.metadata;
+ if (metadataString.empty())
+ return GenericMap();
+
+ if (m_metadataInfo.metadataType == "json")
+ {
+ m_metadataJson = JsonDocumentType();
+ rapidjson::ParseResult res = m_metadataJson.value().Parse(metadataString.data(), metadataString.size());
+ if (!res)
+ {
+ typename ErrorInfoTpl<CharT>::Data errorData;
+ errorData.code = ErrorCode::MetadataParseError;
+ errorData.srcLoc = m_metadataInfo.location;
+ std::string jsonError = rapidjson::GetParseError_En(res.Code());
+ errorData.extraParams.push_back(Value(std::move(jsonError)));
+ return nonstd::make_unexpected(ErrorInfoTpl<CharT>(errorData));
+ }
+ m_metadata = std::move(std::get<GenericMap>(Reflect(m_metadataJson.value()).data()));
+ return m_metadata.value();
+ }
+ return GenericMap();
+ }
+
+ nonstd::expected<MetadataInfo<CharT>, ErrorInfoTpl<CharT>> GetMetadataRaw() const { return m_metadataInfo; }
+
+ bool operator==(const TemplateImpl<CharT>& other) const
+ {
+ if (m_env && other.m_env)
+ {
+ if (*m_env != *other.m_env)
+ return false;
+ }
+ if (m_settings != other.m_settings)
+ return false;
+ if (m_template != other.m_template)
+ return false;
+ if (m_renderer && other.m_renderer && !m_renderer->IsEqual(*other.m_renderer))
+ return false;
+ if (m_metadata != other.m_metadata)
+ return false;
+ if (m_metadataJson != other.m_metadataJson)
+ return false;
+ if (m_metadataInfo != other.m_metadataInfo)
+ return false;
+ return true;
+ }
+private:
+ void ThrowRuntimeError(ErrorCode code, ValuesList extraParams)
+ {
+ typename ErrorInfoTpl<CharT>::Data errorData;
+ errorData.code = code;
+ errorData.srcLoc.col = 1;
+ errorData.srcLoc.line = 1;
+ errorData.srcLoc.fileName = m_templateName;
+ errorData.extraParams = std::move(extraParams);
+
+ throw ErrorInfoTpl<CharT>(std::move(errorData));
+ }
+
+ class RendererCallback : public IRendererCallback
+ {
+ public:
+ explicit RendererCallback(ThisType* host)
+ : m_host(host)
+ {}
+
+ TargetString GetAsTargetString(const InternalValue& val) override
+ {
+ std::basic_string<CharT> os;
+ Apply<visitors::ValueRenderer<CharT>>(val, os);
+ return TargetString(std::move(os));
+ }
+
+ OutStream GetStreamOnString(TargetString& str) override
+ {
+ using string_t = std::basic_string<CharT>;
+ str = string_t();
+ return OutStream([writer = StringStreamWriter<CharT>(&std::get<string_t>(str))]() mutable -> OutStream::StreamWriter* { return &writer; });
+ }
+
+ std::variant<EmptyValue,
+ nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
+ nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const override
+ {
+ return m_host->LoadTemplate(fileName);
+ }
+
+ std::variant<EmptyValue,
+ nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
+ nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const InternalValue& fileName) const override
+ {
+ return m_host->LoadTemplate(fileName);
+ }
+
+ void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) override
+ {
+ m_host->ThrowRuntimeError(code, std::move(extraParams));
+ }
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* callback = dynamic_cast<const RendererCallback*>(&other);
+ if (!callback)
+ return false;
+ if (m_host && callback->m_host)
+ return *m_host == *(callback->m_host);
+ if ((!m_host && (callback->m_host)) || (m_host && !(callback->m_host)))
+ return false;
+ return true;
+ }
+ bool operator==(const IComparable& other) const
+ {
+ auto* callback = dynamic_cast<const RendererCallback*>(&other);
+ if (!callback)
+ return false;
+ if (m_host && callback->m_host)
+ return *m_host == *(callback->m_host);
+ if ((!m_host && (callback->m_host)) || (m_host && !(callback->m_host)))
+ return false;
+ return true;
+ }
+
+ private:
+ ThisType* m_host;
+ };
+private:
+ using JsonDocumentType = rapidjson::GenericDocument<typename detail::RapidJsonEncodingType<sizeof(CharT)>::type>;
+
+ TemplateEnv* m_env{};
+ Settings m_settings;
+ std::basic_string<CharT> m_template;
+ std::string m_templateName;
+ RendererPtr m_renderer;
+ mutable std::optional<GenericMap> m_metadata;
+ mutable std::optional<JsonDocumentType> m_metadataJson;
+ MetadataInfo<CharT> m_metadataInfo;
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_TEMPLATE_IMPL_H
diff --git a/contrib/libs/jinja2cpp/src/template_parser.cpp b/contrib/libs/jinja2cpp/src/template_parser.cpp
new file mode 100644
index 0000000000..bd09039341
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/template_parser.cpp
@@ -0,0 +1,972 @@
+#include "template_parser.h"
+#include "renderer.h"
+#include <boost/cast.hpp>
+
+namespace jinja2
+{
+
+StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, StatementInfoList& statementsInfo)
+{
+ Token tok = lexer.NextToken();
+ ParseResult result;
+
+ switch (lexer.GetAsKeyword(tok))
+ {
+ case Keyword::For:
+ result = ParseFor(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Endfor:
+ result = ParseEndFor(lexer, statementsInfo, tok);
+ break;
+ case Keyword::If:
+ result = ParseIf(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Else:
+ result = ParseElse(lexer, statementsInfo, tok);
+ break;
+ case Keyword::ElIf:
+ result = ParseElIf(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndIf:
+ result = ParseEndIf(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Set:
+ result = ParseSet(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndSet:
+ result = ParseEndSet(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Block:
+ result = ParseBlock(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndBlock:
+ result = ParseEndBlock(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Extends:
+ result = ParseExtends(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Macro:
+ result = ParseMacro(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndMacro:
+ result = ParseEndMacro(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Call:
+ result = ParseCall(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndCall:
+ result = ParseEndCall(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Include:
+ result = ParseInclude(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Import:
+ result = ParseImport(lexer, statementsInfo, tok);
+ break;
+ case Keyword::From:
+ result = ParseFrom(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Do:
+ if (!m_settings.extensions.Do)
+ return MakeParseError(ErrorCode::ExtensionDisabled, tok);
+ result = ParseDo(lexer, statementsInfo, tok);
+ break;
+ case Keyword::With:
+ result = ParseWith(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndWith:
+ result = ParseEndWith(lexer, statementsInfo, tok);
+ break;
+ case Keyword::Filter:
+ result = ParseFilter(lexer, statementsInfo, tok);
+ break;
+ case Keyword::EndFilter:
+ result = ParseEndFilter(lexer, statementsInfo, tok);
+ break;
+ default:
+ return MakeParseError(ErrorCode::UnexpectedToken, tok);
+ }
+
+ if (result)
+ {
+ tok = lexer.PeekNextToken();
+ if (tok != Token::Eof)
+ return MakeParseError(ErrorCode::ExpectedEndOfStatement, tok);
+ }
+
+ return result;
+}
+
+struct ErrorTokenConverter
+{
+ const Token& baseTok;
+
+ explicit ErrorTokenConverter(const Token& t)
+ : baseTok(t)
+ {}
+
+ Token operator()(const Token& tok) const
+ {
+ return tok;
+ }
+
+ template<typename T>
+ Token operator()(T tokType) const
+ {
+ auto newTok = baseTok;
+ newTok.type = static_cast<Token::Type>(tokType);
+ if (newTok.type == Token::Identifier || newTok.type == Token::String)
+ newTok.range.endOffset = newTok.range.startOffset;
+ return newTok;
+ }
+};
+
+template<typename ... Args>
+auto MakeParseErrorTL(ErrorCode code, const Token& baseTok, Args ... expectedTokens)
+{
+ ErrorTokenConverter tokCvt(baseTok);
+
+ return MakeParseError(code, baseTok, {tokCvt(expectedTokens)...});
+}
+
+StatementsParser::ParseResult StatementsParser::ParseFor(LexScanner &lexer, StatementInfoList &statementsInfo,
+ const Token &stmtTok)
+{
+ std::vector<std::string> vars;
+
+ while (lexer.PeekNextToken() == Token::Identifier)
+ {
+ auto tok = lexer.NextToken();
+ vars.push_back(AsString(tok.value));
+ if (lexer.NextToken() != ',')
+ {
+ lexer.ReturnToken();
+ break;
+ }
+ }
+
+ if (vars.empty())
+ return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());
+
+ if (!lexer.EatIfEqual(Keyword::In))
+ {
+ Token tok1 = lexer.PeekNextToken();
+ Token tok2 = tok1;
+ tok2.type = Token::Identifier;
+ tok2.range.endOffset = tok2.range.startOffset;
+ tok2.value = InternalValue();
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, tok1, tok2, Token::In, ',');
+ }
+
+ auto pivotToken = lexer.PeekNextToken();
+ ExpressionParser exprPraser(m_settings);
+ auto valueExpr = exprPraser.ParseFullExpression(lexer, false);
+ if (!valueExpr)
+ return valueExpr.get_unexpected();
+ // return MakeParseError(ErrorCode::ExpectedExpression, pivotToken);
+
+ Token flagsTok;
+ bool isRecursive = false;
+ if (lexer.EatIfEqual(Keyword::Recursive, &flagsTok))
+ {
+ isRecursive = true;
+ }
+
+ ExpressionEvaluatorPtr<> ifExpr;
+ if (lexer.EatIfEqual(Keyword::If))
+ {
+ auto parsedExpr = exprPraser.ParseFullExpression(lexer, false);
+ if (!parsedExpr)
+ return parsedExpr.get_unexpected();
+ ifExpr = *parsedExpr;
+ }
+ else if (lexer.PeekNextToken() != Token::Eof)
+ {
+ auto tok1 = lexer.PeekNextToken();
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, tok1, Token::If, Token::Recursive, Token::Eof);
+ }
+
+ auto renderer = std::make_shared<ForStatement>(vars, *valueExpr, ifExpr, isRecursive);
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ForStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndFor(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ StatementInfo info = statementsInfo.back();
+ RendererPtr elseRenderer;
+ if (info.type == StatementInfo::ElseIfStatement)
+ {
+ auto r = std::static_pointer_cast<ElseBranchStatement>(info.renderer);
+ r->SetMainBody(info.compositions[0]);
+ elseRenderer = std::static_pointer_cast<IRendererBase>(r);
+
+ statementsInfo.pop_back();
+ info = statementsInfo.back();
+ }
+
+ if (info.type != StatementInfo::ForStatement)
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ statementsInfo.pop_back();
+ auto renderer = static_cast<ForStatement*>(info.renderer.get());
+ renderer->SetMainBody(info.compositions[0]);
+ if (elseRenderer)
+ renderer->SetElseBody(elseRenderer);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseIf(LexScanner &lexer, StatementInfoList &statementsInfo,
+ const Token &stmtTok)
+{
+ auto pivotTok = lexer.PeekNextToken();
+ ExpressionParser exprParser(m_settings);
+ auto valueExpr = exprParser.ParseFullExpression(lexer);
+ if (!valueExpr)
+ return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
+
+ auto renderer = std::make_shared<IfStatement>(*valueExpr);
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::IfStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseElse(LexScanner& /*lexer*/, StatementInfoList& statementsInfo
+ , const Token& stmtTok)
+{
+ auto renderer = std::make_shared<ElseBranchStatement>(ExpressionEvaluatorPtr<>());
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ElseIfStatement, stmtTok);
+ statementInfo.renderer = std::static_pointer_cast<IRendererBase>(renderer);
+ statementsInfo.push_back(statementInfo);
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseElIf(LexScanner& lexer, StatementInfoList& statementsInfo
+ , const Token& stmtTok)
+{
+ auto pivotTok = lexer.PeekNextToken();
+ ExpressionParser exprParser(m_settings);
+ auto valueExpr = exprParser.ParseFullExpression(lexer);
+ if (!valueExpr)
+ return MakeParseError(ErrorCode::ExpectedExpression, pivotTok);
+
+ auto renderer = std::make_shared<ElseBranchStatement>(*valueExpr);
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ElseIfStatement, stmtTok);
+ statementInfo.renderer = std::static_pointer_cast<IRendererBase>(renderer);
+ statementsInfo.push_back(statementInfo);
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndIf(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ auto info = statementsInfo.back();
+ statementsInfo.pop_back();
+
+ std::list<StatementPtr<ElseBranchStatement>> elseBranches;
+
+ auto errorTok = stmtTok;
+ while (info.type != StatementInfo::IfStatement)
+ {
+ if (info.type != StatementInfo::ElseIfStatement)
+ return MakeParseError(ErrorCode::UnexpectedStatement, errorTok);
+
+ auto elseRenderer = std::static_pointer_cast<ElseBranchStatement>(info.renderer);
+ elseRenderer->SetMainBody(info.compositions[0]);
+
+ elseBranches.push_front(elseRenderer);
+ errorTok = info.token;
+ info = statementsInfo.back();
+ statementsInfo.pop_back();
+ }
+
+ auto renderer = static_cast<IfStatement*>(info.renderer.get());
+ renderer->SetMainBody(info.compositions[0]);
+
+ for (auto& b : elseBranches)
+ renderer->AddElseBranch(b);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseSet(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ std::vector<std::string> vars;
+
+ while (lexer.PeekNextToken() == Token::Identifier)
+ {
+ auto tok = lexer.NextToken();
+ vars.push_back(AsString(tok.value));
+ if (lexer.NextToken() != ',')
+ {
+ lexer.ReturnToken();
+ break;
+ }
+ }
+
+ if (vars.empty())
+ return MakeParseError(ErrorCode::ExpectedIdentifier, lexer.PeekNextToken());
+
+ ExpressionParser exprParser(m_settings);
+ if (lexer.EatIfEqual('='))
+ {
+ const auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ statementsInfo.back().currentComposition->AddRenderer(
+ std::make_shared<SetLineStatement>(std::move(vars), *expr));
+ }
+ else if (lexer.EatIfEqual('|'))
+ {
+ const auto expr = exprParser.ParseFilterExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ auto statementInfo = StatementInfo::Create(
+ StatementInfo::SetStatement, stmtTok);
+ statementInfo.renderer = std::make_shared<SetFilteredBlockStatement>(
+ std::move(vars), *expr);
+ statementsInfo.push_back(std::move(statementInfo));
+ }
+ else
+ {
+ auto operTok = lexer.NextToken();
+ if (lexer.NextToken() != Token::Eof)
+ return MakeParseError(ErrorCode::YetUnsupported, operTok, {std::move(stmtTok)});
+ auto statementInfo = StatementInfo::Create(
+ StatementInfo::SetStatement, stmtTok);
+ statementInfo.renderer = std::make_shared<SetRawBlockStatement>(
+ std::move(vars));
+ statementsInfo.push_back(std::move(statementInfo));
+ }
+
+ return {};
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndSet(LexScanner&
+ , StatementInfoList& statementsInfo
+ , const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ const auto info = statementsInfo.back();
+ if (info.type != StatementInfo::SetStatement)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ auto &renderer = *boost::polymorphic_downcast<SetBlockStatement*>(
+ info.renderer.get());
+ renderer.SetBody(info.compositions[0]);
+
+ statementsInfo.pop_back();
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return {};
+}
+
+StatementsParser::ParseResult StatementsParser::ParseBlock(LexScanner& lexer, StatementInfoList& statementsInfo
+ , const Token& stmtTok)
+{
+ if (statementsInfo.empty())
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ Token nextTok = lexer.NextToken();
+ if (nextTok != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
+
+ std::string blockName = AsString(nextTok.value);
+
+ auto& info = statementsInfo.back();
+ RendererPtr blockRenderer;
+ StatementInfo::Type blockType = StatementInfo::ParentBlockStatement;
+ if (info.type == StatementInfo::ExtendsStatement)
+ {
+ blockRenderer = std::make_shared<BlockStatement>(blockName);
+ blockType = StatementInfo::BlockStatement;
+ }
+ else
+ {
+ bool isScoped = false;
+ if (lexer.EatIfEqual(Keyword::Scoped, &nextTok))
+ isScoped = true;
+ else
+ {
+ nextTok = lexer.PeekNextToken();
+ if (nextTok != Token::Eof)
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Scoped);
+ }
+
+ blockRenderer = std::make_shared<ParentBlockStatement>(blockName, isScoped);
+ }
+
+ StatementInfo statementInfo = StatementInfo::Create(blockType, stmtTok);
+ statementInfo.renderer = std::move(blockRenderer);
+ statementsInfo.push_back(statementInfo);
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndBlock(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ Token nextTok = lexer.PeekNextToken();
+ if (nextTok != Token::Identifier && nextTok != Token::Eof)
+ {
+ Token tok2;
+ tok2.type = Token::Identifier;
+ Token tok3;
+ tok3.type = Token::Eof;
+ return MakeParseError(ErrorCode::ExpectedToken, nextTok, {tok2, tok3});
+ }
+
+ if (nextTok == Token::Identifier)
+ lexer.EatToken();
+
+ auto info = statementsInfo.back();
+ statementsInfo.pop_back();
+
+ if (info.type == StatementInfo::BlockStatement)
+ {
+ auto blockStmt = std::static_pointer_cast<BlockStatement>(info.renderer);
+ blockStmt->SetMainBody(info.compositions[0]);
+ auto& extendsInfo = statementsInfo.back();
+ auto extendsStmt = std::static_pointer_cast<ExtendsStatement>(extendsInfo.renderer);
+ extendsStmt->AddBlock(std::static_pointer_cast<BlockStatement>(info.renderer));
+ }
+ else if (info.type == StatementInfo::ParentBlockStatement)
+ {
+ auto blockStmt = std::static_pointer_cast<ParentBlockStatement>(info.renderer);
+ blockStmt->SetMainBody(info.compositions[0]);
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+ }
+ else
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseExtends(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.empty())
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ if (!m_env)
+ return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
+
+ Token tok = lexer.NextToken();
+ if (tok != Token::String && tok != Token::Identifier)
+ {
+ auto tok2 = tok;
+ tok2.type = Token::Identifier;
+ tok2.range.endOffset = tok2.range.startOffset;
+ tok2.value = EmptyValue{};
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, tok, tok2, Token::String);
+ }
+
+ auto renderer = std::make_shared<ExtendsStatement>(AsString(tok.value), tok == Token::String);
+ statementsInfo.back().currentComposition->AddRenderer(renderer);
+
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::ExtendsStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseMacro(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.empty())
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ Token nextTok = lexer.NextToken();
+ if (nextTok != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
+
+ std::string macroName = AsString(nextTok.value);
+ MacroParams macroParams;
+
+ if (lexer.EatIfEqual('('))
+ {
+ auto result = ParseMacroParams(lexer);
+ if (!result)
+ return result.get_unexpected();
+
+ macroParams = std::move(result.value());
+ }
+ else if (lexer.PeekNextToken() != Token::Eof)
+ {
+ Token tok = lexer.PeekNextToken();
+
+ return MakeParseErrorTL(ErrorCode::UnexpectedToken, tok, Token::RBracket, Token::Eof);
+ }
+
+ auto renderer = std::make_shared<MacroStatement>(std::move(macroName), std::move(macroParams));
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::MacroStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+
+ return ParseResult();
+}
+
+nonstd::expected<MacroParams, ParseError> StatementsParser::ParseMacroParams(LexScanner& lexer)
+{
+ MacroParams items;
+
+ if (lexer.EatIfEqual(')'))
+ return std::move(items);
+
+ ExpressionParser exprParser(m_settings);
+
+ do
+ {
+ Token name = lexer.NextToken();
+ if (name != Token::Identifier)
+ return MakeParseError(ErrorCode::ExpectedIdentifier, name);
+
+ ExpressionEvaluatorPtr<> defVal;
+ if (lexer.EatIfEqual('='))
+ {
+ auto result = exprParser.ParseFullExpression(lexer, false);
+ if (!result)
+ return result.get_unexpected();
+
+ defVal = *result;
+ }
+
+ MacroParam p;
+ p.paramName = AsString(name.value);
+ p.defaultValue = std::move(defVal);
+ items.push_back(std::move(p));
+
+ } while (lexer.EatIfEqual(','));
+
+ auto tok = lexer.NextToken();
+ if (tok != ')')
+ return MakeParseError(ErrorCode::ExpectedRoundBracket, tok);
+
+ return std::move(items);
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndMacro(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ StatementInfo info = statementsInfo.back();
+
+ if (info.type != StatementInfo::MacroStatement)
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ statementsInfo.pop_back();
+ auto renderer = static_cast<MacroStatement*>(info.renderer.get());
+ renderer->SetMainBody(info.compositions[0]);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseCall(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.empty())
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ MacroParams callbackParams;
+
+ if (lexer.EatIfEqual('('))
+ {
+ auto result = ParseMacroParams(lexer);
+ if (!result)
+ return result.get_unexpected();
+
+ callbackParams = std::move(result.value());
+ }
+
+ Token nextTok = lexer.NextToken();
+ if (nextTok != Token::Identifier)
+ {
+ Token tok = nextTok;
+ Token tok1;
+ tok1.type = Token::Identifier;
+
+ return MakeParseError(ErrorCode::UnexpectedToken, tok, {tok1});
+ }
+
+ std::string macroName = AsString(nextTok.value);
+
+ CallParamsInfo callParams;
+ if (lexer.EatIfEqual('('))
+ {
+ ExpressionParser exprParser(m_settings);
+ auto result = exprParser.ParseCallParams(lexer);
+ if (!result)
+ return result.get_unexpected();
+
+ callParams = std::move(result.value());
+ }
+
+ auto renderer = std::make_shared<MacroCallStatement>(std::move(macroName), std::move(callParams), std::move(callbackParams));
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::MacroCallStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndCall(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ StatementInfo info = statementsInfo.back();
+
+ if (info.type != StatementInfo::MacroCallStatement)
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ statementsInfo.pop_back();
+ auto renderer = static_cast<MacroCallStatement*>(info.renderer.get());
+ renderer->SetMainBody(info.compositions[0]);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.empty())
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ // auto operTok = lexer.NextToken();
+ ExpressionEvaluatorPtr<> valueExpr;
+ ExpressionParser exprParser(m_settings);
+ auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ valueExpr = *expr;
+
+ Token nextTok = lexer.PeekNextToken();
+ bool isIgnoreMissing = false;
+ bool isWithContext = true;
+ bool hasIgnoreMissing = false;
+ if (lexer.EatIfEqual(Keyword::Ignore))
+ {
+ if (lexer.EatIfEqual(Keyword::Missing))
+ isIgnoreMissing = true;
+ else
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Missing);
+
+ hasIgnoreMissing = true;
+ nextTok = lexer.PeekNextToken();
+ }
+
+ auto kw = lexer.GetAsKeyword(nextTok);
+ bool hasContextControl = false;
+ if (kw == Keyword::With || kw == Keyword::Without)
+ {
+ lexer.EatToken();
+ isWithContext = kw == Keyword::With;
+ if (!lexer.EatIfEqual(Keyword::Context))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Context);
+
+ nextTok = lexer.PeekNextToken();
+ hasContextControl = true;
+ }
+
+ if (nextTok != Token::Eof)
+ {
+ if (hasContextControl)
+ return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
+
+ if (hasIgnoreMissing)
+ return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::With, Token::Without);
+
+ return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Ignore, Token::With, Token::Without);
+ }
+
+ if (!m_env && !isIgnoreMissing)
+ return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
+
+ auto renderer = std::make_shared<IncludeStatement>(isIgnoreMissing, isWithContext);
+ renderer->SetIncludeNamesExpr(valueExpr);
+ statementsInfo.back().currentComposition->AddRenderer(renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (!m_env)
+ return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
+
+ ExpressionEvaluatorPtr<> valueExpr;
+ ExpressionParser exprParser(m_settings);
+ auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ valueExpr = *expr;
+
+ if (!lexer.EatIfEqual(Keyword::As))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::As);
+
+ Token name;
+ if (!lexer.EatIfEqual(Token::Identifier, &name))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Identifier);
+
+ Token nextTok = lexer.PeekNextToken();
+ auto kw = lexer.GetAsKeyword(nextTok);
+ bool hasContextControl = false;
+ bool isWithContext = false;
+ if (kw == Keyword::With || kw == Keyword::Without)
+ {
+ lexer.EatToken();
+ isWithContext = kw == Keyword::With;
+ if (!lexer.EatIfEqual(Keyword::Context))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Context);
+
+ nextTok = lexer.PeekNextToken();
+ hasContextControl = true;
+ }
+
+ if (nextTok != Token::Eof)
+ {
+ if (hasContextControl)
+ return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
+
+ return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::With, Token::Without);
+ }
+
+ auto renderer = std::make_shared<ImportStatement>(isWithContext);
+ renderer->SetImportNameExpr(valueExpr);
+ renderer->SetNamespace(AsString(name.value));
+ statementsInfo.back().currentComposition->AddRenderer(renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (!m_env)
+ return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok);
+
+ ExpressionEvaluatorPtr<> valueExpr;
+ ExpressionParser exprParser(m_settings);
+ auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ valueExpr = *expr;
+
+ if (!lexer.EatIfEqual(Keyword::Import))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Identifier);
+
+ std::vector<std::pair<std::string, std::string>> mappedNames;
+
+ Token nextTok;
+ bool hasContextControl = false;
+ bool isWithContext = false;
+
+ for (;;)
+ {
+ bool hasComma = false;
+ if (!mappedNames.empty())
+ {
+ if (!lexer.EatIfEqual(Token::Comma))
+ hasComma = true;;
+ }
+
+ nextTok = lexer.PeekNextToken();
+ auto kw = lexer.GetAsKeyword(nextTok);
+ if (kw == Keyword::With || kw == Keyword::Without)
+ {
+ lexer.NextToken();
+ if (lexer.EatIfEqual(Keyword::Context))
+ {
+ hasContextControl = true;
+ isWithContext = kw == Keyword::With;
+ nextTok = lexer.PeekNextToken();
+ break;
+ }
+ else
+ {
+ lexer.ReturnToken();
+ }
+ }
+
+ if (hasComma)
+ break;
+
+ std::pair<std::string, std::string> macroMap;
+ if (!lexer.EatIfEqual(Token::Identifier, &nextTok))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Identifier);
+
+ macroMap.first = AsString(nextTok.value);
+
+ if (lexer.EatIfEqual(Keyword::As))
+ {
+ if (!lexer.EatIfEqual(Token::Identifier, &nextTok))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Identifier);
+ macroMap.second = AsString(nextTok.value);
+ }
+ else
+ {
+ macroMap.second = macroMap.first;
+ }
+ mappedNames.push_back(std::move(macroMap));
+ }
+
+ if (nextTok != Token::Eof)
+ {
+ if (hasContextControl)
+ return MakeParseErrorTL(ErrorCode::ExpectedEndOfStatement, nextTok, Token::Eof);
+
+ if (mappedNames.empty())
+ MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Identifier);
+ else
+ MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Comma, Token::With, Token::Without);
+ }
+
+ auto renderer = std::make_shared<ImportStatement>(isWithContext);
+ renderer->SetImportNameExpr(valueExpr);
+
+ for (auto& nameInfo : mappedNames)
+ renderer->AddNameToImport(std::move(nameInfo.first), std::move(nameInfo.second));
+
+ statementsInfo.back().currentComposition->AddRenderer(renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& /*stmtTok*/)
+{
+ ExpressionEvaluatorPtr<> valueExpr;
+ ExpressionParser exprParser(m_settings);
+ auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ valueExpr = *expr;
+
+ auto renderer = std::make_shared<DoStatement>(valueExpr);
+ statementsInfo.back().currentComposition->AddRenderer(renderer);
+
+ return jinja2::StatementsParser::ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ std::vector<std::pair<std::string, ExpressionEvaluatorPtr<>>> vars;
+
+ ExpressionParser exprParser(m_settings);
+ while (lexer.PeekNextToken() == Token::Identifier)
+ {
+ auto nameTok = lexer.NextToken();
+ if (!lexer.EatIfEqual('='))
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), '=');
+
+ auto expr = exprParser.ParseFullExpression(lexer);
+ if (!expr)
+ return expr.get_unexpected();
+ auto valueExpr = *expr;
+
+ vars.emplace_back(AsString(nameTok.value), valueExpr);
+
+ if (!lexer.EatIfEqual(','))
+ break;
+ }
+
+ auto nextTok = lexer.PeekNextToken();
+ if (vars.empty())
+ return MakeParseError(ErrorCode::ExpectedIdentifier, nextTok);
+
+ if (nextTok != Token::Eof)
+ return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Eof, ',');
+
+ auto renderer = std::make_shared<WithStatement>();
+ renderer->SetScopeVars(std::move(vars));
+ StatementInfo statementInfo = StatementInfo::Create(StatementInfo::WithStatement, stmtTok);
+ statementInfo.renderer = renderer;
+ statementsInfo.push_back(statementInfo);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndWith(LexScanner& /*lexer*/, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ StatementInfo info = statementsInfo.back();
+
+ if (info.type != StatementInfo::WithStatement)
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ statementsInfo.pop_back();
+ auto renderer = static_cast<WithStatement*>(info.renderer.get());
+ renderer->SetMainBody(info.compositions[0]);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return ParseResult();
+}
+
+StatementsParser::ParseResult StatementsParser::ParseFilter(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ ExpressionParser exprParser(m_settings);
+ auto filterExpr = exprParser.ParseFilterExpression(lexer);
+ if (!filterExpr)
+ {
+ return filterExpr.get_unexpected();
+ }
+
+ auto renderer = std::make_shared<FilterStatement>(*filterExpr);
+ auto statementInfo = StatementInfo::Create(
+ StatementInfo::FilterStatement, stmtTok);
+ statementInfo.renderer = std::move(renderer);
+ statementsInfo.push_back(std::move(statementInfo));
+
+ return {};
+}
+
+StatementsParser::ParseResult StatementsParser::ParseEndFilter(LexScanner&, StatementInfoList& statementsInfo, const Token& stmtTok)
+{
+ if (statementsInfo.size() <= 1)
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+
+ const auto info = statementsInfo.back();
+ if (info.type != StatementInfo::FilterStatement)
+ {
+ return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok);
+ }
+
+ statementsInfo.pop_back();
+ auto &renderer = *boost::polymorphic_downcast<FilterStatement*>(info.renderer.get());
+ renderer.SetBody(info.compositions[0]);
+
+ statementsInfo.back().currentComposition->AddRenderer(info.renderer);
+
+ return {};
+}
+
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/template_parser.h b/contrib/libs/jinja2cpp/src/template_parser.h
new file mode 100644
index 0000000000..89c4270758
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/template_parser.h
@@ -0,0 +1,1081 @@
+#ifndef JINJA2CPP_SRC_TEMPLATE_PARSER_H
+#define JINJA2CPP_SRC_TEMPLATE_PARSER_H
+
+#include "error_handling.h"
+#include "expression_parser.h"
+#include "helpers.h"
+#include "lexer.h"
+#include "lexertk.h"
+#include "renderer.h"
+#include "statements.h"
+#include "template_parser.h"
+#include "value_visitors.h"
+
+#include <boost/algorithm/string/classification.hpp>
+#include <jinja2cpp/error_info.h>
+#include <jinja2cpp/template_env.h>
+#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
+
+#include <list>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifdef JINJA2CPP_USE_REGEX_BOOST
+#include <boost/regex.hpp>
+template <typename CharType>
+using BasicRegex = boost::basic_regex<CharType>;
+using Regex = boost::regex;
+using WideRegex = boost::wregex;
+template <typename CharIterator>
+using RegexIterator = boost::regex_iterator<CharIterator>;
+#else
+#include <regex>
+template <typename CharType>
+using BasicRegex = std::basic_regex<CharType>;
+using Regex = std::regex;
+using WideRegex = std::wregex;
+template <typename CharIterator>
+using RegexIterator = std::regex_iterator<CharIterator>;
+#endif
+
+namespace jinja2
+{
+template<typename CharT>
+struct ParserTraits;
+
+struct KeywordsInfo
+{
+ MultiStringLiteral name;
+ Keyword type;
+};
+
+struct TokenStrInfo : MultiStringLiteral
+{
+ template<typename CharT>
+ auto GetName() const
+ {
+ return MultiStringLiteral::template GetValue<CharT>();
+ }
+};
+
+template<typename T = void>
+struct ParserTraitsBase
+{
+ static Token::Type s_keywords[];
+ static KeywordsInfo s_keywordsInfo[41];
+ static std::unordered_map<int, MultiStringLiteral> s_tokens;
+ static MultiStringLiteral s_regexp;
+};
+
+template<typename T>
+MultiStringLiteral ParserTraitsBase<T>::s_regexp = UNIVERSAL_STR(
+ R"((\{\{)|(\}\})|(\{%[\+\-]?\s+raw\s+[\+\-]?%\})|(\{%[\+\-]?\s+endraw\s+[\+\-]?%\})|(\{%\s+meta\s+%\})|(\{%\s+endmeta\s+%\})|(\{%)|(%\})|(\{#)|(#\})|(\n))");
+
+template<>
+struct ParserTraits<char> : public ParserTraitsBase<>
+{
+ static Regex GetRoughTokenizer()
+ { return Regex(s_regexp.GetValueStr<char>()); }
+ static Regex GetKeywords()
+ {
+ std::string pattern;
+ std::string prefix("(^");
+ std::string postfix("$)");
+
+ bool isFirst = true;
+ for (auto& info : s_keywordsInfo)
+ {
+ if (!isFirst)
+ pattern += "|";
+ else
+ isFirst = false;
+
+ pattern += prefix + info.name.charValue + postfix;
+ }
+ return Regex(pattern);
+ }
+ static std::string GetAsString(const std::string& str, CharRange range) { return str.substr(range.startOffset, range.size()); }
+ static InternalValue RangeToNum(const std::string& str, CharRange range, Token::Type hint)
+ {
+ char buff[std::max(std::numeric_limits<int64_t>::max_digits10, std::numeric_limits<double>::max_digits10) * 2 + 1];
+ std::copy(str.data() + range.startOffset, str.data() + range.endOffset, buff);
+ buff[range.size()] = 0;
+ InternalValue result;
+ if (hint == Token::IntegerNum)
+ {
+ result = InternalValue(static_cast<int64_t>(strtoll(buff, nullptr, 0)));
+ }
+ else
+ {
+ char* endBuff = nullptr;
+ int64_t val = strtoll(buff, &endBuff, 10);
+ if ((errno == ERANGE) || *endBuff)
+ {
+ endBuff = nullptr;
+ double dblVal = strtod(buff, nullptr);
+ result = static_cast<double>(dblVal);
+ }
+ else
+ result = static_cast<int64_t>(val);
+ }
+ return result;
+ }
+};
+
+template<>
+struct ParserTraits<wchar_t> : public ParserTraitsBase<>
+{
+ static WideRegex GetRoughTokenizer()
+ { return WideRegex(s_regexp.GetValueStr<wchar_t>()); }
+ static WideRegex GetKeywords()
+ {
+ std::wstring pattern;
+ std::wstring prefix(L"(^");
+ std::wstring postfix(L"$)");
+
+ bool isFirst = true;
+ for (auto& info : s_keywordsInfo)
+ {
+ if (!isFirst)
+ pattern += L"|";
+ else
+ isFirst = false;
+
+ pattern += prefix + info.name.wcharValue + postfix;
+ }
+ return WideRegex(pattern);
+ }
+ static std::string GetAsString(const std::wstring& str, CharRange range)
+ {
+ auto srcStr = str.substr(range.startOffset, range.size());
+ return detail::StringConverter<std::wstring, std::string>::DoConvert(srcStr);
+ }
+ static InternalValue RangeToNum(const std::wstring& str, CharRange range, Token::Type hint)
+ {
+ wchar_t buff[std::max(std::numeric_limits<int64_t>::max_digits10, std::numeric_limits<double>::max_digits10) * 2 + 1];
+ std::copy(str.data() + range.startOffset, str.data() + range.endOffset, buff);
+ buff[range.size()] = 0;
+ InternalValue result;
+ if (hint == Token::IntegerNum)
+ {
+ result = static_cast<int64_t>(wcstoll(buff, nullptr, 0));
+ }
+ else
+ {
+ wchar_t* endBuff = nullptr;
+ int64_t val = wcstoll(buff, &endBuff, 10);
+ if ((errno == ERANGE) || *endBuff)
+ {
+ endBuff = nullptr;
+ double dblVal = wcstod(buff, nullptr);
+ result = static_cast<double>(dblVal);
+ }
+ else
+ result = static_cast<int64_t>(val);
+ }
+ return result;
+ }
+};
+
+struct StatementInfo
+{
+ enum Type {
+ TemplateRoot,
+ IfStatement,
+ ElseIfStatement,
+ ForStatement,
+ SetStatement,
+ ExtendsStatement,
+ BlockStatement,
+ ParentBlockStatement,
+ MacroStatement,
+ MacroCallStatement,
+ WithStatement,
+ FilterStatement
+ };
+
+ using ComposedPtr = std::shared_ptr<ComposedRenderer>;
+ Type type;
+ ComposedPtr currentComposition;
+ std::vector<ComposedPtr> compositions;
+ Token token;
+ RendererPtr renderer;
+
+ static StatementInfo Create(Type type, const Token& tok, ComposedPtr renderers = std::make_shared<ComposedRenderer>())
+ {
+ StatementInfo result;
+ result.type = type;
+ result.currentComposition = renderers;
+ result.compositions.push_back(renderers);
+ result.token = tok;
+ return result;
+ }
+};
+
+using StatementInfoList = std::list<StatementInfo>;
+
+class StatementsParser
+{
+public:
+ using ParseResult = nonstd::expected<void, ParseError>;
+
+ StatementsParser(const Settings& settings, TemplateEnv* env)
+ : m_settings(settings)
+ , m_env(env)
+ {
+ }
+
+ ParseResult Parse(LexScanner& lexer, StatementInfoList& statementsInfo);
+
+private:
+ ParseResult ParseFor(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseEndFor(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseIf(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseElse(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseElIf(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseEndIf(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& pos);
+ ParseResult ParseSet(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& pos);
+ ParseResult ParseEndSet(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseBlock(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseEndBlock(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseExtends(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseMacro(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ nonstd::expected<MacroParams, ParseError> ParseMacroParams(LexScanner& lexer);
+ ParseResult ParseEndMacro(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseCall(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseEndCall(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseInclude(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& token);
+ ParseResult ParseEndWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseFilter(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+ ParseResult ParseEndFilter(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok);
+
+private:
+ Settings m_settings;
+ TemplateEnv* m_env;
+};
+
+template<typename CharT>
+class TemplateParser : public LexerHelper
+{
+public:
+ using string_t = std::basic_string<CharT>;
+ using traits_t = ParserTraits<CharT>;
+ using sregex_iterator = RegexIterator<typename string_t::const_iterator>;
+ using ErrorInfo = ErrorInfoTpl<CharT>;
+ using ParseResult = nonstd::expected<RendererPtr, std::vector<ErrorInfo>>;
+
+ TemplateParser(const string_t* tpl, const Settings& setts, TemplateEnv* env, std::string tplName)
+ : m_template(tpl)
+ , m_templateName(std::move(tplName))
+ , m_settings(setts)
+ , m_env(env)
+ , m_roughTokenizer(traits_t::GetRoughTokenizer())
+ , m_keywords(traits_t::GetKeywords())
+ , m_metadataType(setts.m_defaultMetadataType)
+ {
+ }
+
+ ParseResult Parse()
+ {
+ auto roughResult = DoRoughParsing();
+
+ if (!roughResult)
+ {
+ return ParseErrorsToErrorInfo(roughResult.error());
+ }
+
+ auto composeRenderer = std::make_shared<ComposedRenderer>();
+
+ auto fineResult = DoFineParsing(composeRenderer);
+ if (!fineResult)
+ return ParseErrorsToErrorInfo(fineResult.error());
+
+ return composeRenderer;
+ }
+
+ MetadataInfo<CharT> GetMetadataInfo() const
+ {
+ MetadataInfo<CharT> result;
+ result.metadataType = m_metadataType;
+ result.metadata = m_metadata;
+ result.location = m_metadataLocation;
+ return result;
+ }
+
+private:
+ enum {
+ RM_Unknown = 0,
+ RM_ExprBegin = 1,
+ RM_ExprEnd,
+ RM_RawBegin,
+ RM_RawEnd,
+ RM_MetaBegin,
+ RM_MetaEnd,
+ RM_StmtBegin,
+ RM_StmtEnd,
+ RM_CommentBegin,
+ RM_CommentEnd,
+ RM_NewLine
+ };
+
+ struct LineInfo
+ {
+ CharRange range;
+ unsigned lineNumber;
+ };
+
+ enum class TextBlockType { RawText, Expression, Statement, Comment, LineStatement, RawBlock, MetaBlock };
+
+ struct TextBlockInfo
+ {
+ CharRange range;
+ TextBlockType type;
+ };
+
+ nonstd::expected<void, std::vector<ParseError>> DoRoughParsing()
+ {
+ std::vector<ParseError> foundErrors;
+
+ auto matchBegin = sregex_iterator(m_template->begin(), m_template->end(), m_roughTokenizer);
+ auto matchEnd = sregex_iterator();
+
+ auto matches = std::distance(matchBegin, matchEnd);
+ // One line, no customization
+ if (matches == 0)
+ {
+ CharRange range{ 0ULL, m_template->size() };
+ m_lines.push_back(LineInfo{ range, 0 });
+ m_textBlocks.push_back(
+ TextBlockInfo{ range, (!m_template->empty() && m_template->front() == '#') ? TextBlockType::LineStatement : TextBlockType::RawText });
+ return nonstd::expected<void, std::vector<ParseError>>();
+ }
+
+ m_currentBlockInfo.range.startOffset = 0;
+ m_currentBlockInfo.range.endOffset = 0;
+ m_currentLineInfo.range = m_currentBlockInfo.range;
+ m_currentLineInfo.lineNumber = 0;
+ if (m_settings.useLineStatements)
+ m_currentBlockInfo.type = m_template->front() == '#' ? TextBlockType::LineStatement : TextBlockType::RawText;
+ else
+ m_currentBlockInfo.type = TextBlockType::RawText;
+ do
+ {
+ auto result = ParseRoughMatch(matchBegin, matchEnd);
+ if (!result)
+ {
+ foundErrors.push_back(result.error());
+ return nonstd::make_unexpected(std::move(foundErrors));
+ }
+ } while (matchBegin != matchEnd);
+ FinishCurrentLine(m_template->size());
+
+ if (m_currentBlockInfo.type == TextBlockType::RawBlock)
+ {
+ nonstd::expected<void, ParseError> result =
+ MakeParseError(ErrorCode::ExpectedRawEnd, MakeToken(Token::RawEnd, { m_template->size(), m_template->size() }));
+ foundErrors.push_back(result.error());
+ return nonstd::make_unexpected(std::move(foundErrors));
+ }
+ else if (m_currentBlockInfo.type == TextBlockType::MetaBlock)
+ {
+ nonstd::expected<void, ParseError> result =
+ MakeParseError(ErrorCode::ExpectedMetaEnd, MakeToken(Token::RawEnd, { m_template->size(), m_template->size() }));
+ foundErrors.push_back(result.error());
+ return nonstd::make_unexpected(std::move(foundErrors));
+ }
+
+ FinishCurrentBlock(m_template->size(), TextBlockType::RawText);
+
+ if (!foundErrors.empty())
+ return nonstd::make_unexpected(std::move(foundErrors));
+ return nonstd::expected<void, std::vector<ParseError>>();
+ }
+ nonstd::expected<void, ParseError> ParseRoughMatch(sregex_iterator& curMatch, const sregex_iterator& /*endMatch*/)
+ {
+ auto match = *curMatch;
+ ++curMatch;
+ unsigned matchType = RM_Unknown;
+ for (unsigned idx = 1; idx != match.size(); ++idx)
+ {
+ if (match.length(idx) != 0)
+ {
+ matchType = idx;
+ break;
+ }
+ }
+
+ size_t matchStart = static_cast<size_t>(match.position());
+
+ switch (matchType)
+ {
+ case RM_NewLine:
+ FinishCurrentLine(match.position());
+ m_currentLineInfo.range.startOffset = m_currentLineInfo.range.endOffset + 1;
+ if (m_currentLineInfo.range.startOffset < m_template->size() &&
+ (m_currentBlockInfo.type == TextBlockType::RawText || m_currentBlockInfo.type == TextBlockType::LineStatement))
+ {
+ if (m_currentBlockInfo.type == TextBlockType::LineStatement)
+ {
+ FinishCurrentBlock(matchStart, TextBlockType::RawText);
+ m_currentBlockInfo.range.startOffset = m_currentLineInfo.range.startOffset;
+ }
+
+ if (m_settings.useLineStatements)
+ m_currentBlockInfo.type =
+ (*m_template)[m_currentLineInfo.range.startOffset] == '#' ? TextBlockType::LineStatement : TextBlockType::RawText;
+ else
+ m_currentBlockInfo.type = TextBlockType::RawText;
+ }
+ break;
+ case RM_CommentBegin:
+ if (m_currentBlockInfo.type == TextBlockType::RawBlock)
+ break;
+ if (m_currentBlockInfo.type != TextBlockType::RawText)
+ {
+ FinishCurrentLine(match.position() + 2);
+ return MakeParseError(ErrorCode::UnexpectedCommentBegin, MakeToken(Token::CommentBegin, { matchStart, matchStart + 2 }));
+ }
+
+ FinishCurrentBlock(matchStart, TextBlockType::Comment);
+ m_currentBlockInfo.range.startOffset = matchStart + 2;
+ m_currentBlockInfo.type = TextBlockType::Comment;
+ break;
+
+ case RM_CommentEnd:
+ if (m_currentBlockInfo.type == TextBlockType::RawBlock)
+ break;
+ if (m_currentBlockInfo.type != TextBlockType::Comment)
+ {
+ FinishCurrentLine(match.position() + 2);
+ return MakeParseError(ErrorCode::UnexpectedCommentEnd, MakeToken(Token::CommentEnd, { matchStart, matchStart + 2 }));
+ }
+
+ m_currentBlockInfo.range.startOffset = FinishCurrentBlock(matchStart, TextBlockType::RawText);
+ break;
+ case RM_ExprBegin:
+ StartControlBlock(TextBlockType::Expression, matchStart);
+ break;
+ case RM_ExprEnd:
+ if (m_currentBlockInfo.type == TextBlockType::RawText)
+ {
+ FinishCurrentLine(match.position() + 2);
+ return MakeParseError(ErrorCode::UnexpectedExprEnd, MakeToken(Token::ExprEnd, { matchStart, matchStart + 2 }));
+ }
+ else if (m_currentBlockInfo.type != TextBlockType::Expression || (*m_template)[match.position() - 1] == '\'')
+ break;
+
+ m_currentBlockInfo.range.startOffset = FinishCurrentBlock(matchStart, TextBlockType::RawText);
+ break;
+ case RM_StmtBegin:
+ StartControlBlock(TextBlockType::Statement, matchStart);
+ break;
+ case RM_StmtEnd:
+ if (m_currentBlockInfo.type == TextBlockType::RawText)
+ {
+ FinishCurrentLine(match.position() + 2);
+ return MakeParseError(ErrorCode::UnexpectedStmtEnd, MakeToken(Token::StmtEnd, { matchStart, matchStart + 2 }));
+ }
+ else if (m_currentBlockInfo.type != TextBlockType::Statement || (*m_template)[match.position() - 1] == '\'')
+ break;
+
+ m_currentBlockInfo.range.startOffset = FinishCurrentBlock(matchStart, TextBlockType::RawText);
+ break;
+ case RM_RawBegin:
+ if (m_currentBlockInfo.type == TextBlockType::RawBlock)
+ break;
+ else if (m_currentBlockInfo.type != TextBlockType::RawText && m_currentBlockInfo.type != TextBlockType::Comment)
+ {
+ FinishCurrentLine(match.position() + match.length());
+ return MakeParseError(ErrorCode::UnexpectedRawBegin, MakeToken(Token::RawBegin, { matchStart, matchStart + match.length() }));
+ }
+ StartControlBlock(TextBlockType::RawBlock, matchStart, matchStart + match.length());
+ break;
+ case RM_RawEnd:
+ if (m_currentBlockInfo.type == TextBlockType::Comment)
+ break;
+ else if (m_currentBlockInfo.type != TextBlockType::RawBlock)
+ {
+ FinishCurrentLine(match.position() + match.length());
+ return MakeParseError(ErrorCode::UnexpectedRawEnd, MakeToken(Token::RawEnd, { matchStart, matchStart + match.length() }));
+ }
+ m_currentBlockInfo.range.startOffset = FinishCurrentBlock(matchStart + match.length() - 2, TextBlockType::RawText, matchStart);
+ break;
+ case RM_MetaBegin:
+ if (m_currentBlockInfo.type == TextBlockType::Comment)
+ break;
+ if ((m_currentBlockInfo.type != TextBlockType::RawText && m_currentBlockInfo.type != TextBlockType::Comment) || m_hasMetaBlock)
+ {
+ FinishCurrentLine(match.position() + match.length());
+ return MakeParseError(ErrorCode::UnexpectedMetaBegin, MakeToken(Token::MetaBegin, { matchStart, matchStart + match.length() }));
+ }
+ StartControlBlock(TextBlockType::MetaBlock, matchStart, matchStart + match.length());
+ m_metadataLocation.line = m_currentLineInfo.lineNumber + 1;
+ m_metadataLocation.col = static_cast<unsigned>(match.position() - m_currentLineInfo.range.startOffset + 1);
+ m_metadataLocation.fileName = m_templateName;
+ break;
+ case RM_MetaEnd:
+ if (m_currentBlockInfo.type == TextBlockType::Comment)
+ break;
+ if (m_currentBlockInfo.type != TextBlockType::MetaBlock)
+ {
+ FinishCurrentLine(match.position() + match.length());
+ return MakeParseError(ErrorCode::UnexpectedMetaEnd, MakeToken(Token::MetaEnd, { matchStart, matchStart + match.length() }));
+ }
+ m_currentBlockInfo.range.startOffset = FinishCurrentBlock(matchStart + match.length() - 2, TextBlockType::MetaBlock, matchStart);
+ m_hasMetaBlock = true;
+ break;
+ }
+
+ return nonstd::expected<void, ParseError>();
+ }
+
+ void StartControlBlock(TextBlockType blockType, size_t matchStart, size_t startOffset = 0)
+ {
+ if (!startOffset)
+ startOffset = matchStart + 2;
+
+ size_t endOffset = matchStart;
+ if (m_currentBlockInfo.type != TextBlockType::RawText || m_currentBlockInfo.type == TextBlockType::RawBlock)
+ return;
+ else
+ endOffset = StripBlockLeft(m_currentBlockInfo, startOffset, endOffset, blockType == TextBlockType::Expression ? false : m_settings.lstripBlocks);
+
+ FinishCurrentBlock(endOffset, blockType);
+ if (startOffset < m_template->size() && blockType != TextBlockType::MetaBlock)
+ {
+ if ((*m_template)[startOffset] == '+' || (*m_template)[startOffset] == '-')
+ ++startOffset;
+ }
+
+ m_currentBlockInfo.type = blockType;
+
+ if (blockType == TextBlockType::RawBlock)
+ startOffset = StripBlockRight(m_currentBlockInfo, startOffset - 2, m_settings.trimBlocks);
+
+ m_currentBlockInfo.range.startOffset = startOffset;
+ }
+
+ size_t StripBlockRight(TextBlockInfo& /* currentBlockInfo */, size_t position, bool trimBlocks)
+ {
+ bool doTrim = trimBlocks;
+
+ size_t newPos = position + 2;
+
+ if ((m_currentBlockInfo.type != TextBlockType::RawText) && position != 0)
+ {
+ auto ctrlChar = (*m_template)[position - 1];
+ doTrim = ctrlChar == '-' ? true : (ctrlChar == '+' ? false : doTrim);
+ }
+
+ if (doTrim)
+ {
+ auto locale = std::locale();
+ for (; newPos < m_template->size(); ++newPos)
+ {
+ auto ch = (*m_template)[newPos];
+ if (ch == '\n')
+ {
+ ++newPos;
+ break;
+ }
+ if (!std::isspace(ch, locale))
+ break;
+ }
+ }
+ return newPos;
+ }
+
+ size_t StripBlockLeft(TextBlockInfo& currentBlockInfo, size_t ctrlCharPos, size_t endOffset, bool doStrip)
+ {
+ bool doTotalStrip = false;
+ if (ctrlCharPos < m_template->size())
+ {
+ auto ctrlChar = (*m_template)[ctrlCharPos];
+ if (ctrlChar == '+')
+ doStrip = false;
+ else
+ doTotalStrip = ctrlChar == '-';
+
+ doStrip |= doTotalStrip;
+ }
+ if (!doStrip || (currentBlockInfo.type != TextBlockType::RawText && currentBlockInfo.type != TextBlockType::RawBlock))
+ return endOffset;
+
+ auto locale = std::locale();
+ auto& tpl = *m_template;
+ auto originalOffset = endOffset;
+ bool sameLine = true;
+ for (; endOffset != currentBlockInfo.range.startOffset && endOffset > 0; --endOffset)
+ {
+ auto ch = tpl[endOffset - 1];
+ if (!std::isspace(ch, locale))
+ {
+ if (!sameLine)
+ break;
+
+ return doTotalStrip ? endOffset : originalOffset;
+ }
+ if (ch == '\n')
+ {
+ if (!doTotalStrip)
+ break;
+ sameLine = false;
+ }
+ }
+ return endOffset;
+ }
+
+ nonstd::expected<void, std::vector<ParseError>> DoFineParsing(std::shared_ptr<ComposedRenderer> renderers)
+ {
+ std::vector<ParseError> errors;
+ StatementInfoList statementsStack;
+ StatementInfo root = StatementInfo::Create(StatementInfo::TemplateRoot, Token(), renderers);
+ statementsStack.push_back(root);
+ for (auto& origBlock : m_textBlocks)
+ {
+ auto block = origBlock;
+ if (block.type == TextBlockType::LineStatement)
+ ++block.range.startOffset;
+
+ switch (block.type)
+ {
+ case TextBlockType::RawBlock:
+ case TextBlockType::RawText:
+ {
+ auto range = block.range;
+ if (range.size() == 0)
+ break;
+ auto renderer = std::make_shared<RawTextRenderer>(m_template->data() + range.startOffset, range.size());
+ statementsStack.back().currentComposition->AddRenderer(renderer);
+ break;
+ }
+ case TextBlockType::MetaBlock:
+ {
+ auto range = block.range;
+ if (range.size() == 0)
+ break;
+ auto metadata = std::basic_string_view<CharT>(m_template->data() + range.startOffset, range.size());
+ if (!boost::algorithm::all(metadata, boost::algorithm::is_space()))
+ m_metadata = metadata;
+ break;
+ }
+ case TextBlockType::Expression:
+ {
+ auto parseResult = InvokeParser<RendererPtr, ExpressionParser>(block);
+ if (parseResult)
+ statementsStack.back().currentComposition->AddRenderer(*parseResult);
+ else
+ errors.push_back(parseResult.error());
+ break;
+ }
+ case TextBlockType::Statement:
+ case TextBlockType::LineStatement:
+ {
+ auto parseResult = InvokeParser<void, StatementsParser>(block, statementsStack);
+ if (!parseResult)
+ errors.push_back(parseResult.error());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (!errors.empty())
+ return nonstd::make_unexpected(std::move(errors));
+
+ return nonstd::expected<void, std::vector<ParseError>>();
+ }
+ template<typename R, typename P, typename... Args>
+ nonstd::expected<R, ParseError> InvokeParser(const TextBlockInfo& block, Args&&... args)
+ {
+ lexertk::generator<CharT> tokenizer;
+ auto range = block.range;
+ auto start = m_template->data();
+ if (!tokenizer.process(start + range.startOffset, start + range.endOffset))
+ return MakeParseError(ErrorCode::Unspecified, MakeToken(Token::Unknown, { range.startOffset, range.startOffset + 1 }));
+
+ tokenizer.begin();
+ Lexer lexer(
+ [&tokenizer, adjust = range.startOffset]() mutable {
+ lexertk::token tok = tokenizer.next_token();
+ tok.position += adjust;
+ return tok;
+ },
+ this);
+
+ if (!lexer.Preprocess())
+ return MakeParseError(ErrorCode::Unspecified, MakeToken(Token::Unknown, { range.startOffset, range.startOffset + 1 }));
+
+ P praser(m_settings, m_env);
+ LexScanner scanner(lexer);
+ auto result = praser.Parse(scanner, std::forward<Args>(args)...);
+ if (!result)
+ return result.get_unexpected();
+
+ return result;
+ }
+
+ nonstd::unexpected_type<std::vector<ErrorInfo>> ParseErrorsToErrorInfo(const std::vector<ParseError>& errors)
+ {
+ std::vector<ErrorInfo> resultErrors;
+
+ for (auto& e : errors)
+ {
+ typename ErrorInfo::Data errInfoData;
+ errInfoData.code = e.errorCode;
+ errInfoData.srcLoc.fileName = m_templateName;
+ OffsetToLinePos(e.errorToken.range.startOffset, errInfoData.srcLoc.line, errInfoData.srcLoc.col);
+ errInfoData.locationDescr = GetLocationDescr(errInfoData.srcLoc.line, errInfoData.srcLoc.col);
+ errInfoData.extraParams.emplace_back(TokenToString(e.errorToken));
+ for (auto& tok : e.relatedTokens)
+ {
+ errInfoData.extraParams.emplace_back(TokenToString(tok));
+ if (tok.range.startOffset != e.errorToken.range.startOffset)
+ {
+ SourceLocation relLoc;
+ relLoc.fileName = m_templateName;
+ OffsetToLinePos(tok.range.startOffset, relLoc.line, relLoc.col);
+ errInfoData.relatedLocs.push_back(std::move(relLoc));
+ }
+ }
+
+ resultErrors.emplace_back(errInfoData);
+ }
+
+ return nonstd::make_unexpected(std::move(resultErrors));
+ }
+
+ Token MakeToken(Token::Type type, const CharRange& range, string_t value = string_t())
+ {
+ Token tok;
+ tok.type = type;
+ tok.range = range;
+ tok.value = TargetString(static_cast<string_t>(value));
+
+ return tok;
+ }
+
+ auto TokenToString(const Token& tok)
+ {
+ auto p = traits_t::s_tokens.find(tok.type);
+ if (p != traits_t::s_tokens.end())
+ return p->second.template GetValueStr<CharT>();
+
+ if (tok.range.size() != 0)
+ return string_t(m_template->substr(tok.range.startOffset, tok.range.size()));
+ else if (tok.type == Token::Identifier)
+ {
+ if (!tok.value.IsEmpty())
+ {
+ std::basic_string<CharT> tpl;
+ return GetAsSameString(tpl, tok.value).value_or(std::basic_string<CharT>());
+ }
+
+ return UNIVERSAL_STR("<<Identifier>>").template GetValueStr<CharT>();
+ }
+ else if (tok.type == Token::String)
+ return UNIVERSAL_STR("<<String>>").template GetValueStr<CharT>();
+
+ return string_t();
+ }
+
+ size_t FinishCurrentBlock(size_t position, TextBlockType nextBlockType, size_t matchStart = 0)
+ {
+ size_t newPos = position;
+
+ if (m_currentBlockInfo.type == TextBlockType::RawBlock || m_currentBlockInfo.type == TextBlockType::MetaBlock)
+ {
+ size_t currentPosition = matchStart ? matchStart : position;
+ auto origPos = position;
+ position = StripBlockLeft(m_currentBlockInfo, currentPosition + 2, currentPosition, m_settings.lstripBlocks);
+ newPos = StripBlockRight(m_currentBlockInfo, origPos, m_settings.trimBlocks);
+ }
+ else
+ {
+ if (m_currentBlockInfo.type == TextBlockType::RawText)
+ position =
+ StripBlockLeft(m_currentBlockInfo, position + 2, position, nextBlockType == TextBlockType::Expression ? false : m_settings.lstripBlocks);
+ else if (nextBlockType == TextBlockType::RawText)
+ newPos = StripBlockRight(m_currentBlockInfo, position, m_currentBlockInfo.type == TextBlockType::Expression ? false : m_settings.trimBlocks);
+
+ if ((m_currentBlockInfo.type != TextBlockType::RawText) && position != 0)
+ {
+ auto ctrlChar = (*m_template)[position - 1];
+ if (ctrlChar == '+' || ctrlChar == '-')
+ --position;
+ }
+ }
+
+ m_currentBlockInfo.range.endOffset = position;
+ m_textBlocks.push_back(m_currentBlockInfo);
+ m_currentBlockInfo.type = TextBlockType::RawText;
+ return newPos;
+ }
+
+ void FinishCurrentLine(int64_t position)
+ {
+ m_currentLineInfo.range.endOffset = static_cast<size_t>(position);
+ m_lines.push_back(m_currentLineInfo);
+ m_currentLineInfo.lineNumber++;
+ }
+
+ void OffsetToLinePos(size_t offset, unsigned& line, unsigned& col)
+ {
+ auto p = std::find_if(
+ m_lines.begin(), m_lines.end(), [offset](const LineInfo& info) { return offset >= info.range.startOffset && offset < info.range.endOffset; });
+
+ if (p == m_lines.end())
+ {
+ if (m_lines.empty() || offset != m_lines.back().range.endOffset)
+ {
+ line = 1;
+ col = 1;
+ return;
+ }
+ p = m_lines.end() - 1;
+ }
+
+ line = p->lineNumber + 1;
+ col = static_cast<unsigned>(offset - p->range.startOffset + 1);
+ }
+
+ string_t GetLocationDescr(unsigned line, unsigned col)
+ {
+ if (line == 0 && col == 0)
+ return string_t();
+
+ --line;
+ --col;
+
+ auto toCharT = [](char ch) { return static_cast<CharT>(ch); };
+
+ auto& lineInfo = m_lines[line];
+ std::basic_ostringstream<CharT> os;
+ auto origLine = m_template->substr(lineInfo.range.startOffset, lineInfo.range.size());
+ os << origLine << std::endl;
+
+ string_t spacePrefix;
+ auto locale = std::locale();
+ for (auto ch : origLine)
+ {
+ if (!std::isspace(ch, locale))
+ break;
+ spacePrefix.append(1, ch);
+ }
+
+ const int headLen = 3;
+ const int tailLen = 7;
+ auto spacePrefixLen = spacePrefix.size();
+
+ if (col < spacePrefixLen)
+ {
+ for (unsigned i = 0; i < col; ++i)
+ os << toCharT(' ');
+
+ os << toCharT('^');
+ for (int i = 0; i < tailLen; ++i)
+ os << toCharT('-');
+ return os.str();
+ }
+
+ os << spacePrefix;
+ int actualHeadLen = std::min(static_cast<int>(col - spacePrefixLen), headLen);
+
+ if (actualHeadLen == headLen)
+ {
+ for (std::size_t i = 0; i < col - actualHeadLen - spacePrefixLen; ++i)
+ os << toCharT(' ');
+ }
+ for (int i = 0; i < actualHeadLen; ++i)
+ os << toCharT('-');
+ os << toCharT('^');
+ for (int i = 0; i < tailLen; ++i)
+ os << toCharT('-');
+
+ return os.str();
+ }
+
+ // LexerHelper interface
+ std::string GetAsString(const CharRange& range) override { return traits_t::GetAsString(*m_template, range); }
+ InternalValue GetAsValue(const CharRange& range, Token::Type type) override
+ {
+ if (type == Token::String)
+ {
+ auto rawValue = CompileEscapes(m_template->substr(range.startOffset, range.size()));
+ return InternalValue(TargetString(std::move(rawValue)));
+ }
+ if (type == Token::IntegerNum || type == Token::FloatNum)
+ return traits_t::RangeToNum(*m_template, range, type);
+ return InternalValue();
+ }
+ Keyword GetKeyword(const CharRange& range) override
+ {
+ auto matchBegin = sregex_iterator(m_template->begin() + range.startOffset, m_template->begin() + range.endOffset, m_keywords);
+ auto matchEnd = sregex_iterator();
+
+ auto matches = std::distance(matchBegin, matchEnd);
+ // One line, no customization
+ if (matches == 0)
+ return Keyword::Unknown;
+
+ auto& match = *matchBegin;
+ for (size_t idx = 1; idx != match.size(); ++idx)
+ {
+ if (match.length(idx) != 0)
+ {
+ return traits_t::s_keywordsInfo[idx - 1].type;
+ }
+ }
+
+ return Keyword::Unknown;
+ }
+ char GetCharAt(size_t /*pos*/) override { return '\0'; }
+
+private:
+ const string_t* m_template;
+ std::string m_templateName;
+ const Settings& m_settings;
+ TemplateEnv* m_env = nullptr;
+ BasicRegex<CharT> m_roughTokenizer;
+ BasicRegex<CharT> m_keywords;
+ std::vector<LineInfo> m_lines;
+ std::vector<TextBlockInfo> m_textBlocks;
+ LineInfo m_currentLineInfo = {};
+ TextBlockInfo m_currentBlockInfo = {};
+ bool m_hasMetaBlock = false;
+ std::basic_string_view<CharT> m_metadata;
+ std::string m_metadataType;
+ SourceLocation m_metadataLocation;
+};
+
+template<typename T>
+KeywordsInfo ParserTraitsBase<T>::s_keywordsInfo[41] = {
+ { UNIVERSAL_STR("for"), Keyword::For },
+ { UNIVERSAL_STR("endfor"), Keyword::Endfor },
+ { UNIVERSAL_STR("in"), Keyword::In },
+ { UNIVERSAL_STR("if"), Keyword::If },
+ { UNIVERSAL_STR("else"), Keyword::Else },
+ { UNIVERSAL_STR("elif"), Keyword::ElIf },
+ { UNIVERSAL_STR("endif"), Keyword::EndIf },
+ { UNIVERSAL_STR("or"), Keyword::LogicalOr },
+ { UNIVERSAL_STR("and"), Keyword::LogicalAnd },
+ { UNIVERSAL_STR("not"), Keyword::LogicalNot },
+ { UNIVERSAL_STR("is"), Keyword::Is },
+ { UNIVERSAL_STR("block"), Keyword::Block },
+ { UNIVERSAL_STR("endblock"), Keyword::EndBlock },
+ { UNIVERSAL_STR("extends"), Keyword::Extends },
+ { UNIVERSAL_STR("macro"), Keyword::Macro },
+ { UNIVERSAL_STR("endmacro"), Keyword::EndMacro },
+ { UNIVERSAL_STR("call"), Keyword::Call },
+ { UNIVERSAL_STR("endcall"), Keyword::EndCall },
+ { UNIVERSAL_STR("filter"), Keyword::Filter },
+ { UNIVERSAL_STR("endfilter"), Keyword::EndFilter },
+ { UNIVERSAL_STR("set"), Keyword::Set },
+ { UNIVERSAL_STR("endset"), Keyword::EndSet },
+ { UNIVERSAL_STR("include"), Keyword::Include },
+ { UNIVERSAL_STR("import"), Keyword::Import },
+ { UNIVERSAL_STR("true"), Keyword::True },
+ { UNIVERSAL_STR("false"), Keyword::False },
+ { UNIVERSAL_STR("True"), Keyword::True },
+ { UNIVERSAL_STR("False"), Keyword::False },
+ { UNIVERSAL_STR("none"), Keyword::None },
+ { UNIVERSAL_STR("None"), Keyword::None },
+ { UNIVERSAL_STR("recursive"), Keyword::Recursive },
+ { UNIVERSAL_STR("scoped"), Keyword::Scoped },
+ { UNIVERSAL_STR("with"), Keyword::With },
+ { UNIVERSAL_STR("endwith"), Keyword::EndWith },
+ { UNIVERSAL_STR("without"), Keyword::Without },
+ { UNIVERSAL_STR("ignore"), Keyword::Ignore },
+ { UNIVERSAL_STR("missing"), Keyword::Missing },
+ { UNIVERSAL_STR("context"), Keyword::Context },
+ { UNIVERSAL_STR("from"), Keyword::From },
+ { UNIVERSAL_STR("as"), Keyword::As },
+ { UNIVERSAL_STR("do"), Keyword::Do },
+};
+
+template<typename T>
+std::unordered_map<int, MultiStringLiteral> ParserTraitsBase<T>::s_tokens = {
+ { Token::Unknown, UNIVERSAL_STR("<<Unknown>>") },
+ { Token::Lt, UNIVERSAL_STR("<") },
+ { Token::Gt, UNIVERSAL_STR(">") },
+ { Token::Plus, UNIVERSAL_STR("+") },
+ { Token::Minus, UNIVERSAL_STR("-") },
+ { Token::Percent, UNIVERSAL_STR("%") },
+ { Token::Mul, UNIVERSAL_STR("*") },
+ { Token::Div, UNIVERSAL_STR("/") },
+ { Token::LBracket, UNIVERSAL_STR("(") },
+ { Token::RBracket, UNIVERSAL_STR(")") },
+ { Token::LSqBracket, UNIVERSAL_STR("[") },
+ { Token::RSqBracket, UNIVERSAL_STR("]") },
+ { Token::LCrlBracket, UNIVERSAL_STR("{") },
+ { Token::RCrlBracket, UNIVERSAL_STR("}") },
+ { Token::Assign, UNIVERSAL_STR("=") },
+ { Token::Comma, UNIVERSAL_STR(",") },
+ { Token::Eof, UNIVERSAL_STR("<<End of block>>") },
+ { Token::Equal, UNIVERSAL_STR("==") },
+ { Token::NotEqual, UNIVERSAL_STR("!=") },
+ { Token::LessEqual, UNIVERSAL_STR("<=") },
+ { Token::GreaterEqual, UNIVERSAL_STR(">=") },
+ { Token::StarStar, UNIVERSAL_STR("**") },
+ { Token::DashDash, UNIVERSAL_STR("//") },
+ { Token::LogicalOr, UNIVERSAL_STR("or") },
+ { Token::LogicalAnd, UNIVERSAL_STR("and") },
+ { Token::LogicalNot, UNIVERSAL_STR("not") },
+ { Token::MulMul, UNIVERSAL_STR("**") },
+ { Token::DivDiv, UNIVERSAL_STR("//") },
+ { Token::True, UNIVERSAL_STR("true") },
+ { Token::False, UNIVERSAL_STR("false") },
+ { Token::None, UNIVERSAL_STR("none") },
+ { Token::In, UNIVERSAL_STR("in") },
+ { Token::Is, UNIVERSAL_STR("is") },
+ { Token::For, UNIVERSAL_STR("for") },
+ { Token::Endfor, UNIVERSAL_STR("endfor") },
+ { Token::If, UNIVERSAL_STR("if") },
+ { Token::Else, UNIVERSAL_STR("else") },
+ { Token::ElIf, UNIVERSAL_STR("elif") },
+ { Token::EndIf, UNIVERSAL_STR("endif") },
+ { Token::Block, UNIVERSAL_STR("block") },
+ { Token::EndBlock, UNIVERSAL_STR("endblock") },
+ { Token::Extends, UNIVERSAL_STR("extends") },
+ { Token::Macro, UNIVERSAL_STR("macro") },
+ { Token::EndMacro, UNIVERSAL_STR("endmacro") },
+ { Token::Call, UNIVERSAL_STR("call") },
+ { Token::EndCall, UNIVERSAL_STR("endcall") },
+ { Token::Filter, UNIVERSAL_STR("filter") },
+ { Token::EndFilter, UNIVERSAL_STR("endfilter") },
+ { Token::Set, UNIVERSAL_STR("set") },
+ { Token::EndSet, UNIVERSAL_STR("endset") },
+ { Token::Include, UNIVERSAL_STR("include") },
+ { Token::Import, UNIVERSAL_STR("import") },
+ { Token::Recursive, UNIVERSAL_STR("recursive") },
+ { Token::Scoped, UNIVERSAL_STR("scoped") },
+ { Token::With, UNIVERSAL_STR("with") },
+ { Token::EndWith, UNIVERSAL_STR("endwith") },
+ { Token::Without, UNIVERSAL_STR("without") },
+ { Token::Ignore, UNIVERSAL_STR("ignore") },
+ { Token::Missing, UNIVERSAL_STR("missing") },
+ { Token::Context, UNIVERSAL_STR("context") },
+ { Token::From, UNIVERSAL_STR("form") },
+ { Token::As, UNIVERSAL_STR("as") },
+ { Token::Do, UNIVERSAL_STR("do") },
+ { Token::RawBegin, UNIVERSAL_STR("{% raw %}") },
+ { Token::RawEnd, UNIVERSAL_STR("{% endraw %}") },
+ { Token::MetaBegin, UNIVERSAL_STR("{% meta %}") },
+ { Token::MetaEnd, UNIVERSAL_STR("{% endmeta %}") },
+ { Token::CommentBegin, UNIVERSAL_STR("{#") },
+ { Token::CommentEnd, UNIVERSAL_STR("#}") },
+ { Token::StmtBegin, UNIVERSAL_STR("{%") },
+ { Token::StmtEnd, UNIVERSAL_STR("%}") },
+ { Token::ExprBegin, UNIVERSAL_STR("{{") },
+ { Token::ExprEnd, UNIVERSAL_STR("}}") },
+};
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_TEMPLATE_PARSER_H
diff --git a/contrib/libs/jinja2cpp/src/testers.cpp b/contrib/libs/jinja2cpp/src/testers.cpp
new file mode 100644
index 0000000000..ce9d255d82
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/testers.cpp
@@ -0,0 +1,379 @@
+#include "testers.h"
+#include "value_visitors.h"
+
+namespace jinja2
+{
+
+template<typename F>
+struct TesterFactory
+{
+ static TesterPtr Create(TesterParams params)
+ {
+ return std::make_shared<F>(std::move(params));
+ }
+
+ template<typename ... Args>
+ static IsExpression::TesterFactoryFn MakeCreator(Args&& ... args)
+ {
+ return [args...](TesterParams params) {return std::make_shared<F>(std::move(params), args...);};
+ }
+};
+
+std::unordered_map<std::string, IsExpression::TesterFactoryFn> s_testers = {
+ {"defined", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsDefinedMode)},
+ {"startsWith", &TesterFactory<testers::StartsWith>::Create},
+ {"eq", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalEq)},
+ {"==", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalEq)},
+ {"equalto", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalEq)},
+ {"even", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsEvenMode)},
+ {"ge", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalGe)},
+ {">=", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalGe)},
+ {"gt", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalGt)},
+ {">", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalGt)},
+ {"greaterthan", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalGt)},
+ {"in", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsInMode)},
+ {"iterable", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsIterableMode)},
+ {"le", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalLe)},
+ {"<=", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalLe)},
+ {"lower", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsLowerMode)},
+ {"lt", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalLt)},
+ {"<", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalLt)},
+ {"lessthan", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalLt)},
+ {"mapping", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsMappingMode)},
+ {"ne", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalNe)},
+ {"!=", TesterFactory<testers::Comparator>::MakeCreator(BinaryExpression::LogicalNe)},
+ {"number", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsNumberMode)},
+ {"odd", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsOddMode)},
+ {"sequence", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsSequenceMode)},
+ {"string", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsStringMode)},
+ {"undefined", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsUndefinedMode)},
+ {"upper", TesterFactory<testers::ValueTester>::MakeCreator(testers::ValueTester::IsUpperMode)},
+};
+
+TesterPtr CreateTester(std::string testerName, CallParamsInfo params)
+{
+ auto p = s_testers.find(testerName);
+ if (p == s_testers.end())
+ return std::make_shared<testers::UserDefinedTester>(std::move(testerName), std::move(params));
+
+ return p->second(std::move(params));
+}
+
+namespace testers
+{
+
+Comparator::Comparator(TesterParams params, BinaryExpression::Operation op)
+ : m_op(op)
+{
+ ParseParams({{"b", true}}, params);
+}
+
+bool Comparator::Test(const InternalValue& baseVal, RenderContext& context)
+{
+ auto b = GetArgumentValue("b", context);
+
+ auto cmpRes = Apply2<visitors::BinaryMathOperation>(baseVal, b, m_op);
+ return ConvertToBool(cmpRes);
+}
+
+#if 0
+bool Defined::Test(const InternalValue& baseVal, RenderContext& /*context*/)
+{
+ return boost::get<EmptyValue>(&baseVal) == nullptr;
+}
+#endif
+
+StartsWith::StartsWith(TesterParams params)
+{
+ bool parsed = true;
+ auto args = helpers::ParseCallParamsInfo({ { "str", true } }, params, parsed);
+ m_stringEval = args["str"];
+}
+
+bool StartsWith::Test(const InternalValue& baseVal, RenderContext& context)
+{
+ InternalValue val = m_stringEval->Evaluate(context);
+ std::string baseStr = AsString(baseVal);
+ std::string str = AsString(val);
+ return baseStr.find(str) == 0;
+}
+
+ValueTester::ValueTester(TesterParams params, ValueTester::Mode mode)
+ : m_mode(mode)
+{
+ switch (m_mode)
+ {
+ case IsDefinedMode:
+ break;
+ case IsEvenMode:
+ break;
+ case IsInMode:
+ ParseParams({{"seq", true}}, params);
+ break;
+ case IsIterableMode:
+ break;
+ case IsLowerMode:
+ break;
+ case IsMappingMode:
+ break;
+ case IsNumberMode:
+ break;
+ case IsOddMode:
+ break;
+ case IsSequenceMode:
+ break;
+ case IsStringMode:
+ break;
+ case IsUndefinedMode:
+ break;
+ case IsUpperMode:
+ break;
+
+ }
+}
+
+enum class ValueKind
+{
+ Empty,
+ Boolean,
+ String,
+ Integer,
+ Double,
+ List,
+ Map,
+ KVPair,
+ Callable,
+ Renderer
+};
+
+struct ValueKindGetter : visitors::BaseVisitor<ValueKind>
+{
+ using visitors::BaseVisitor<ValueKind>::operator ();
+
+ ValueKind operator()(const EmptyValue&) const
+ {
+ return ValueKind::Empty;
+ }
+ ValueKind operator()(bool) const
+ {
+ return ValueKind::Boolean;
+ }
+ template<typename CharT>
+ ValueKind operator()(const std::basic_string<CharT>&) const
+ {
+ return ValueKind::String;
+ }
+ template<typename CharT>
+ ValueKind operator()(const std::basic_string_view<CharT>&) const
+ {
+ return ValueKind::String;
+ }
+ ValueKind operator()(int64_t) const
+ {
+ return ValueKind::Integer;
+ }
+ ValueKind operator()(double) const
+ {
+ return ValueKind::Double;
+ }
+ ValueKind operator()(const ListAdapter&) const
+ {
+ return ValueKind::List;
+ }
+ ValueKind operator()(const MapAdapter&) const
+ {
+ return ValueKind::Map;
+ }
+ ValueKind operator()(const KeyValuePair&) const
+ {
+ return ValueKind::KVPair;
+ }
+ ValueKind operator()(const Callable&) const
+ {
+ return ValueKind::Callable;
+ }
+ ValueKind operator()(IRendererBase*) const
+ {
+ return ValueKind::Renderer;
+ }
+};
+
+bool ValueTester::Test(const InternalValue& baseVal, RenderContext& context)
+{
+ bool result = false;
+ auto valKind = Apply<ValueKindGetter>(baseVal);
+ enum
+ {
+ EvenTest,
+ OddTest
+ };
+
+ int testMode = EvenTest;
+ auto evenOddTest = [&testMode, valKind](const InternalValue& val) -> bool
+ {
+ bool result = false;
+ if (valKind == ValueKind::Integer)
+ {
+ auto intVal = ConvertToInt(val);
+ result = (intVal & 1) == (testMode == EvenTest ? 0 : 1);
+ }
+ else if (valKind == ValueKind::Double)
+ {
+ auto dblVal = ConvertToDouble(val);
+ int64_t intVal = static_cast<int64_t>(dblVal);
+ if (dblVal == intVal)
+ result = (intVal & 1) == (testMode == EvenTest ? 0 : 1);
+ }
+ return result;
+ };
+
+ switch (m_mode)
+ {
+ case IsIterableMode:
+ result = valKind == ValueKind::List || valKind == ValueKind::Map;
+ break;
+ case IsMappingMode:
+ result = valKind == ValueKind::KVPair || valKind == ValueKind::Map;
+ break;
+ case IsNumberMode:
+ result = valKind == ValueKind::Integer || valKind == ValueKind::Double;
+ break;
+ case IsSequenceMode:
+ result = valKind == ValueKind::List;
+ break;
+ case IsStringMode:
+ result = valKind == ValueKind::String;
+ break;
+ case IsDefinedMode:
+ result = valKind != ValueKind::Empty;
+ break;
+ case IsUndefinedMode:
+ result = valKind == ValueKind::Empty;
+ break;
+ case IsInMode:
+ {
+ bool isConverted = false;
+ auto seq = GetArgumentValue("seq", context);
+ auto seqKind = Apply<ValueKindGetter>(seq);
+ if (seqKind == ValueKind::List) {
+ ListAdapter values = ConvertToList(seq, InternalValue(), isConverted);
+
+ if (!isConverted)
+ return false;
+
+ auto equalComparator = [&baseVal](auto& val) {
+ InternalValue cmpRes;
+ cmpRes = Apply2<visitors::BinaryMathOperation>(val, baseVal, BinaryExpression::LogicalEq);
+ return ConvertToBool(cmpRes);
+ };
+
+ auto p = std::find_if(values.begin(), values.end(), equalComparator);
+ result = p != values.end();
+ } else if (seqKind == ValueKind::String) {
+ result = ApplyStringConverter(baseVal, [&](const auto& srcStr) {
+ std::decay_t<decltype(srcStr)> emptyStrView;
+ using CharT = typename decltype(emptyStrView)::value_type;
+ std::basic_string<CharT> emptyStr;
+
+ auto substring = sv_to_string(srcStr);
+ auto seq = GetAsSameString(srcStr, this->GetArgumentValue("seq", context)).value_or(emptyStr);
+
+ return seq.find(substring) != std::string::npos;
+ });
+ }
+ break;
+ }
+ case IsEvenMode:
+ {
+ testMode = EvenTest;
+ result = evenOddTest(baseVal);
+ break;
+ }
+ case IsOddMode:
+ {
+ testMode = OddTest;
+ result = evenOddTest(baseVal);
+ break;
+ }
+ case IsLowerMode:
+ if (valKind != ValueKind::String)
+ {
+ result = false;
+ }
+ else
+ {
+ result = ApplyStringConverter(baseVal, [](const auto& str) {
+ bool result = true;
+ for (auto& ch : str)
+ {
+ if (std::isalpha(ch, std::locale()) && std::isupper(ch, std::locale()))
+ {
+ result = false;
+ break;
+ }
+ }
+ return result;
+ });
+ }
+ break;
+ case IsUpperMode:
+ if (valKind != ValueKind::String)
+ {
+ result = false;
+ }
+ else
+ {
+ result = ApplyStringConverter(baseVal, [](const auto& str) {
+ bool result = true;
+ for (auto& ch : str)
+ {
+ if (std::isalpha(ch, std::locale()) && std::islower(ch, std::locale()))
+ {
+ result = false;
+ break;
+ }
+ }
+ return result;
+ });
+ }
+ break;
+
+ }
+ return result;
+}
+
+UserDefinedTester::UserDefinedTester(std::string testerName, TesterParams params)
+ : m_testerName(std::move(testerName))
+{
+ ParseParams({{"*args"}, {"**kwargs"}}, params);
+ m_callParams.kwParams = m_args.extraKwArgs;
+ m_callParams.posParams = m_args.extraPosArgs;
+}
+
+bool UserDefinedTester::Test(const InternalValue& baseVal, RenderContext& context)
+{
+ bool testerFound = false;
+ auto testerValPtr = context.FindValue(m_testerName, testerFound);
+ if (!testerFound)
+ return false;
+
+ const Callable* callable = GetIf<Callable>(&testerValPtr->second);
+ if (callable == nullptr || callable->GetKind() != Callable::UserCallable)
+ return false;
+
+ CallParams tmpCallParams = helpers::EvaluateCallParams(m_callParams, context);
+ CallParams callParams;
+ callParams.kwParams = std::move(tmpCallParams.kwParams);
+ callParams.posParams.reserve(tmpCallParams.posParams.size() + 1);
+ callParams.posParams.push_back(baseVal);
+ for (auto& p : tmpCallParams.posParams)
+ callParams.posParams.push_back(std::move(p));
+
+ InternalValue result;
+ if (callable->GetType() != Callable::Type::Expression)
+ return false;
+
+ return ConvertToBool(callable->GetExpressionCallable()(callParams, context));
+}
+} // namespace testers
+} // namespace jinja2
diff --git a/contrib/libs/jinja2cpp/src/testers.h b/contrib/libs/jinja2cpp/src/testers.h
new file mode 100644
index 0000000000..9d29e6cbc1
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/testers.h
@@ -0,0 +1,116 @@
+#ifndef JINJA2CPP_SRC_TESTERS_H
+#define JINJA2CPP_SRC_TESTERS_H
+
+#include "expression_evaluator.h"
+#include "function_base.h"
+#include "jinja2cpp/value.h"
+#include "render_context.h"
+
+#include <memory>
+#include <functional>
+
+namespace jinja2
+{
+using TesterPtr = std::shared_ptr<IsExpression::ITester>;
+using TesterParams = CallParamsInfo;
+
+extern TesterPtr CreateTester(std::string testerName, CallParamsInfo params);
+
+namespace testers
+{
+
+class TesterBase : public FunctionBase, public IsExpression::ITester
+{
+};
+
+class Comparator : public TesterBase
+{
+public:
+ Comparator(TesterParams params, BinaryExpression::Operation op);
+
+ bool Test(const InternalValue& baseVal, RenderContext& context) override;
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const Comparator*>(&other);
+ if (!val)
+ return false;
+ return m_op == val->m_op;
+ }
+private:
+ BinaryExpression::Operation m_op;
+};
+
+class StartsWith : public IsExpression::ITester
+{
+public:
+ StartsWith(TesterParams);
+
+ bool Test(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const StartsWith*>(&other);
+ if (!val)
+ return false;
+ return m_stringEval == val->m_stringEval;
+ }
+private:
+ ExpressionEvaluatorPtr<> m_stringEval;
+};
+
+class ValueTester : public TesterBase
+{
+public:
+ enum Mode
+ {
+ IsDefinedMode,
+ IsEvenMode,
+ IsInMode,
+ IsIterableMode,
+ IsLowerMode,
+ IsMappingMode,
+ IsNumberMode,
+ IsOddMode,
+ IsSequenceMode,
+ IsStringMode,
+ IsUndefinedMode,
+ IsUpperMode
+ };
+
+ ValueTester(TesterParams params, Mode mode);
+
+ bool Test(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const ValueTester*>(&other);
+ if (!val)
+ return false;
+ return m_mode == val->m_mode;
+ }
+private:
+ Mode m_mode;
+};
+
+class UserDefinedTester : public TesterBase
+{
+public:
+ UserDefinedTester(std::string filterName, TesterParams params);
+
+ bool Test(const InternalValue& baseVal, RenderContext& context) override;
+
+ bool IsEqual(const IComparable& other) const override
+ {
+ auto* val = dynamic_cast<const UserDefinedTester*>(&other);
+ if (!val)
+ return false;
+ return m_testerName == val->m_testerName && m_callParams == val->m_callParams;
+ }
+private:
+ std::string m_testerName;
+ TesterParams m_callParams;
+};
+} // namespace testers
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_TESTERS_H
diff --git a/contrib/libs/jinja2cpp/src/value.cpp b/contrib/libs/jinja2cpp/src/value.cpp
new file mode 100644
index 0000000000..3139b29ac2
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/value.cpp
@@ -0,0 +1,143 @@
+#if 0
+#include "jinja2cpp/value.h"
+#include <sstream>
+
+namespace jinja2
+{
+template<typename T>
+std::string toString(T val)
+{
+ std::ostringstream os;
+ os << val;
+ return os.str();
+}
+
+namespace
+{
+struct ValueRenderer : boost::static_visitor<std::string>
+{
+ std::string operator() (bool val) const
+ {
+ return val ? "True" : "False";
+ }
+ std::string operator() (const EmptyValue&) const
+ {
+ return std::string();
+ }
+ std::string operator() (const std::wstring&) const
+ {
+ return std::string();
+ }
+
+ std::string operator() (const ValuesList& vals) const
+ {
+ std::string result = "{";
+ bool isFirst = true;
+ for (auto& val : vals)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ result += ", ";
+
+ result += boost::apply_visitor(ValueRenderer(), val.data());
+ }
+ result += "}";
+ return result;
+ }
+
+ std::string operator() (const ValuesMap& vals) const
+ {
+ std::string result = "{";
+ bool isFirst = true;
+ for (auto& val : vals)
+ {
+ if (isFirst)
+ isFirst = false;
+ else
+ result += ", ";
+
+ result += "{\"" + val.first + "\",";
+ result += boost::apply_visitor(ValueRenderer(), val.second.data());
+ result += "}";
+ }
+ result += "}";
+ return result;
+ }
+
+ std::string operator() (const GenericMap& /*val*/) const
+ {
+ return "";
+ }
+
+ std::string operator() (const GenericList& /*val*/) const
+ {
+ return "";
+ }
+
+ template<typename T>
+ std::string operator() (const T& val) const
+ {
+ return toString(val);
+ }
+};
+
+struct SubscriptionVisitor : public boost::static_visitor<InternalValue>
+{
+ InternalValue operator() (const ValuesMap& values, const std::string& field) const
+ {
+ auto p = values.find(field);
+ if (p == values.end())
+ return InternalValue();
+
+ return p->second;
+ }
+
+ InternalValue operator() (const GenericMap& values, const std::string& field) const
+ {
+ if (!values.HasValue(field))
+ return InternalValue();
+
+ return values.GetValueByName(field);
+ }
+
+ InternalValue operator() (const GenericMap& values, const int64_t index) const
+ {
+ if (index < 0 || static_cast<size_t>(index) >= values.GetSize())
+ return InternalValue();
+
+ return values.GetValueByIndex(index);
+ }
+
+ InternalValue operator() (const ValuesList& values, int64_t index) const
+ {
+ if (index < 0 || static_cast<size_t>(index) >= values.size())
+ return InternalValue();
+
+ return values[static_cast<size_t>(index)];
+ }
+
+ InternalValue operator() (const GenericList& values, const int64_t index) const
+ {
+ if (index < 0 || static_cast<size_t>(index) >= values.GetSize())
+ return InternalValue();
+
+ return values.GetValueByIndex(index);
+ }
+
+ template<typename T, typename U>
+ InternalValue operator() (T&& /*first*/, U&& /*second*/) const
+ {
+ return InternalValue();
+ }
+};
+
+} //
+
+InternalValue InternalValue::subscript(const InternalValue& index) const
+{
+ return boost::apply_visitor(SubscriptionVisitor(), m_data, index.m_data);
+}
+
+} // jinja2
+#endif
diff --git a/contrib/libs/jinja2cpp/src/value_helpers.h b/contrib/libs/jinja2cpp/src/value_helpers.h
new file mode 100644
index 0000000000..d5ac829fc4
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/value_helpers.h
@@ -0,0 +1,167 @@
+#ifndef JINJA2CPP_SRC_VALUE_HELPERS_H
+#define JINJA2CPP_SRC_VALUE_HELPERS_H
+
+#include <jinja2cpp/value.h>
+
+#include <boost/iterator/iterator_facade.hpp>
+
+namespace jinja2
+{
+#if 0
+class GenericListIterator
+ : public boost::iterator_facade<
+ GenericListIterator,
+ const InternalValue,
+ boost::random_access_traversal_tag>
+{
+public:
+ GenericListIterator()
+ : m_current(0)
+ , m_list(nullptr)
+ {}
+
+ explicit GenericListIterator(GenericList& list)
+ : m_current(0)
+ , m_list(&list)
+ {}
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ ++ m_current;
+ m_valueIdx = m_current;
+ m_currentVal = m_current == m_list->GetSize() ? InternalValue() : m_list->GetValueByIndex(static_cast<int64_t>(m_current));
+ }
+
+ int distance_to(const GenericListIterator& other) const
+ {
+ if (m_list == nullptr)
+ return other.m_list == nullptr ? 0 : -other.distance_to(*this);
+
+ if (other.m_list == nullptr)
+ return m_list->GetSize() - m_current;
+
+ return other.m_current - m_current;
+ }
+
+ void advance(int distance)
+ {
+ m_current += distance;
+ if (distance != 0)
+ {
+ m_valueIdx = m_current;
+ m_currentVal = m_current == m_list->GetSize() ? InternalValue() : m_list->GetValueByIndex(static_cast<int64_t>(m_current));
+
+ }
+ }
+
+ bool equal(const GenericListIterator& other) const
+ {
+ if (m_list == nullptr)
+ return other.m_list == nullptr ? true : other.equal(*this);
+
+ if (other.m_list == nullptr)
+ return m_current == m_list->GetSize();
+
+ return this->m_list == other.m_list && this->m_current == other.m_current;
+ }
+
+ const InternalValue& dereference() const
+ {
+ if (m_current != m_valueIdx)
+ m_currentVal = m_current == m_list->GetSize() ? InternalValue() : m_list->GetValueByIndex(static_cast<int64_t>(m_current));
+ return m_currentVal;
+ }
+
+ int64_t m_current = 0;
+ mutable int64_t m_valueIdx = -1;
+ mutable InternalValue m_currentVal;
+ GenericList* m_list;
+};
+
+class ConstGenericListIterator
+ : public boost::iterator_facade<
+ GenericListIterator,
+ const InternalValue,
+ boost::random_access_traversal_tag>
+{
+public:
+ ConstGenericListIterator()
+ : m_current(0)
+ , m_list(nullptr)
+ {}
+
+ explicit ConstGenericListIterator(const GenericList& list)
+ : m_current(0)
+ , m_list(&list)
+ {}
+
+private:
+ friend class boost::iterator_core_access;
+
+ void increment()
+ {
+ ++ m_current;
+ }
+
+ int distance_to(const ConstGenericListIterator& other) const
+ {
+ if (m_list == nullptr)
+ return other.m_list == nullptr ? 0 : -other.distance_to(*this);
+
+ if (other.m_list == nullptr)
+ return m_list->GetSize() - m_current;
+
+ return other.m_current - m_current;
+ }
+
+ void advance(int distance)
+ {
+ m_current += distance;
+ }
+
+ bool equal(const ConstGenericListIterator& other) const
+ {
+ if (m_list == nullptr)
+ return other.m_list == nullptr ? true : other.equal(*this);
+
+ if (other.m_list == nullptr)
+ return m_current == m_list->GetSize();
+
+ return this->m_list == other.m_list && this->m_current == other.m_current;
+ }
+
+ const InternalValue& dereference() const
+ {
+ return m_list->GetValueByIndex(static_cast<int64_t>(m_current));
+ }
+
+ size_t m_current;
+ const GenericList* m_list;
+};
+
+inline auto begin(GenericList& list)
+{
+ return GenericListIterator(list);
+}
+
+inline auto end(GenericList& list)
+{
+ return GenericListIterator();
+}
+
+inline auto begin(const GenericList& list)
+{
+ return ConstGenericListIterator(list);
+}
+
+inline auto end(const GenericList& list)
+{
+ return ConstGenericListIterator();
+}
+#endif
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_VALUE_HELPERS_H
diff --git a/contrib/libs/jinja2cpp/src/value_visitors.h b/contrib/libs/jinja2cpp/src/value_visitors.h
new file mode 100644
index 0000000000..8814f645cf
--- /dev/null
+++ b/contrib/libs/jinja2cpp/src/value_visitors.h
@@ -0,0 +1,1151 @@
+#ifndef JINJA2CPP_SRC_VALUE_VISITORS_H
+#define JINJA2CPP_SRC_VALUE_VISITORS_H
+
+#include "expression_evaluator.h"
+#include "helpers.h"
+#include "jinja2cpp/value.h"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/optional.hpp>
+#include <fmt/format.h>
+#include <fmt/xchar.h>
+
+#include <iostream>
+#include <cmath>
+#include <limits>
+#include <utility>
+#include <typeinfo>
+
+namespace jinja2
+{
+
+namespace detail
+{
+
+template<typename V>
+struct RecursiveUnwrapper
+{
+ V* m_visitor{};
+
+ RecursiveUnwrapper(V* v)
+ : m_visitor(v)
+ {}
+
+
+ template<typename T>
+ static const auto& UnwrapRecursive(const T& arg)
+ {
+ return arg; // std::forward<T>(arg);
+ }
+
+ template<typename T>
+ static auto& UnwrapRecursive(const RecursiveWrapper<T>& arg)
+ {
+ return arg.GetValue();
+ }
+
+// template<typename T>
+// static auto& UnwrapRecursive(RecursiveWrapper<T>& arg)
+// {
+// return arg.GetValue();
+// }
+
+ template<typename ... Args>
+ auto operator()(const Args& ... args) const
+ {
+ assert(m_visitor != nullptr);
+ return (*m_visitor)(UnwrapRecursive(args)...);
+ }
+};
+
+template<typename Fn>
+auto ApplyUnwrapped(const InternalValueData& val, Fn&& fn)
+{
+ auto valueRef = GetIf<ValueRef>(&val);
+ auto targetString = GetIf<TargetString>(&val);
+ auto targetSV = GetIf<TargetStringView>(&val);
+ // auto internalValueRef = GetIf<InternalValueRef>(&val);
+
+ if (valueRef != nullptr)
+ return fn(valueRef->get().data());
+ else if (targetString != nullptr)
+ return fn(*targetString);
+ else if (targetSV != nullptr)
+ return fn(*targetSV);
+// else if (internalValueRef != nullptr)
+// return fn(internalValueRef->get());
+
+ return fn(val);
+}
+} // namespace detail
+
+template<typename V, typename ... Args>
+auto Apply(const InternalValue& val, Args&& ... args)
+{
+ return detail::ApplyUnwrapped(val.GetData(), [&args...](auto& val) {
+ auto v = V(args...);
+ return std::visit(detail::RecursiveUnwrapper<V>(&v), val);
+ });
+}
+
+template<typename V, typename ... Args>
+auto Apply2(const InternalValue& val1, const InternalValue& val2, Args&& ... args)
+{
+ return detail::ApplyUnwrapped(val1.GetData(), [&val2, &args...](auto& uwVal1) {
+ return detail::ApplyUnwrapped(val2.GetData(), [&uwVal1, &args...](auto& uwVal2) {
+ auto v = V(args...);
+ return std::visit(detail::RecursiveUnwrapper<V>(&v), uwVal1, uwVal2);
+ });
+ });
+}
+
+bool ConvertToBool(const InternalValue& val);
+
+namespace visitors
+{
+template<typename R = InternalValue>
+struct BaseVisitor
+{
+ R operator() (const GenericMap&) const
+ {
+ assert(false);
+ return R();
+ }
+
+ R operator() (const GenericList&) const
+ {
+ assert(false);
+ return R();
+ }
+
+ R operator() (const ValueRef&) const
+ {
+ assert(false);
+ return R();
+ }
+
+ R operator() (const TargetString&) const
+ {
+ assert(false);
+ return R();
+ }
+
+ template<typename T>
+ R operator() (T&&) const
+ {
+ return R();
+ }
+
+ template<typename T, typename U>
+ R operator() (T&&, U&&) const
+ {
+ return R();
+ }
+};
+
+
+template<typename CharT>
+struct ValueRendererBase
+{
+ ValueRendererBase(std::basic_string<CharT>& os)
+ : m_os(&os)
+ {
+ }
+
+ template<typename T>
+ void operator()(const T& val) const;
+ void operator()(double val) const;
+ void operator()(const std::basic_string_view<CharT>& val) const
+ {
+ m_os->append(val.begin(), val.end());
+ }
+ void operator()(const std::basic_string<CharT>& val) const
+ {
+ m_os->append(val.begin(), val.end());
+ }
+
+ void operator()(const EmptyValue&) const {}
+ void operator()(const ValuesList&) const {}
+ void operator()(const ValuesMap&) const {}
+ void operator()(const GenericMap&) const {}
+ void operator()(const GenericList&) const {}
+ void operator()(const MapAdapter&) const {}
+ void operator()(const ListAdapter&) const {}
+ void operator()(const ValueRef&) const {}
+ void operator()(const TargetString&) const {}
+ void operator()(const TargetStringView&) const {}
+ void operator()(const KeyValuePair&) const {}
+ void operator()(const Callable&) const {}
+ void operator()(const UserCallable&) const {}
+ void operator()(const std::shared_ptr<IRendererBase>) const {}
+ template<typename T>
+ void operator()(const boost::recursive_wrapper<T>&) const {}
+ template<typename T>
+ void operator()(const RecWrapper<T>&) const {}
+
+ auto GetOs() const { return std::back_inserter(*m_os); }
+
+ std::basic_string<CharT>* m_os;
+};
+
+template<>
+template<typename T>
+void ValueRendererBase<char>::operator()(const T& val) const
+{
+ fmt::format_to(GetOs(), "{}", val);
+}
+
+template<>
+template<typename T>
+void ValueRendererBase<wchar_t>::operator()(const T& val) const
+{
+ fmt::format_to(GetOs(), L"{}", val);
+}
+
+template<>
+inline void ValueRendererBase<char>::operator()(double val) const
+{
+ fmt::format_to(GetOs(), "{:.8g}", val);
+}
+
+template<>
+inline void ValueRendererBase<wchar_t>::operator()(double val) const
+{
+ fmt::format_to(GetOs(), L"{:.8g}", val);
+}
+
+struct InputValueConvertor
+{
+ using result_t = boost::optional<InternalValue>;
+
+ InputValueConvertor(bool byValue, bool allowStringRef)
+ : m_byValue(byValue)
+ , m_allowStringRef(allowStringRef)
+ {
+ }
+
+ template<typename ChT>
+ result_t operator()(const std::basic_string<ChT>& val) const
+ {
+ if (m_allowStringRef)
+ return result_t(TargetStringView(std::basic_string_view<ChT>(val)));
+
+ return result_t(TargetString(val));
+ }
+
+ template<typename ChT>
+ result_t operator()(std::basic_string<ChT>& val) const
+ {
+ return result_t(TargetString(std::move(val)));
+ }
+
+ result_t operator()(const ValuesList& vals) const
+ {
+ if (m_byValue)
+ {
+ ValuesList newVals(vals);
+ return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals))));
+ }
+
+ return result_t(InternalValue(ListAdapter::CreateAdapter(vals)));
+ }
+
+ result_t operator() (ValuesList& vals) const
+ {
+ return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals))));
+ }
+
+ result_t operator() (const GenericList& vals) const
+ {
+ if (m_byValue)
+ {
+ GenericList newVals(vals);
+ return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(newVals))));
+ }
+
+ return result_t(InternalValue(ListAdapter::CreateAdapter(vals)));
+ }
+
+ result_t operator() (GenericList& vals) const
+ {
+ return result_t(InternalValue(ListAdapter::CreateAdapter(std::move(vals))));
+ }
+
+ result_t operator()(const ValuesMap& vals) const
+ {
+ if (m_byValue)
+ {
+ ValuesMap newVals(vals);
+ return result_t(CreateMapAdapter(std::move(newVals)));
+ }
+
+ return result_t(CreateMapAdapter(vals));
+ }
+
+ result_t operator()(ValuesMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); }
+
+ result_t operator()(const GenericMap& vals) const
+ {
+ if (m_byValue)
+ {
+ GenericMap newVals(vals);
+ return result_t(CreateMapAdapter(std::move(newVals)));
+ }
+
+ return result_t(CreateMapAdapter(vals));
+ }
+
+ result_t operator()(GenericMap& vals) const { return result_t(CreateMapAdapter(std::move(vals))); }
+
+ result_t operator()(const UserCallable& val) const { return ConvertUserCallable(val); }
+
+ result_t operator()(UserCallable& val) const { return ConvertUserCallable(std::move(val)); }
+
+ template<typename T>
+ result_t operator()(const RecWrapper<T>& val) const
+ {
+ return this->operator()(const_cast<const T&>(*val));
+ }
+
+ template<typename T>
+ result_t operator()(RecWrapper<T>& val) const
+ {
+ return this->operator()(*val);
+ }
+
+ template<typename T>
+ result_t operator()(const T& val) const
+ {
+ return result_t(InternalValue(val));
+ }
+
+ static result_t ConvertUserCallable(const UserCallable& val);
+
+ bool m_byValue{};
+ bool m_allowStringRef{};
+};
+
+template<typename CharT>
+struct ValueRenderer;
+
+template<>
+struct ValueRenderer<char> : ValueRendererBase<char>
+{
+ ValueRenderer(std::string& os)
+ : ValueRendererBase<char>::ValueRendererBase<char>(os)
+ {
+ }
+
+ using ValueRendererBase<char>::operator ();
+ void operator()(const std::wstring& str) const
+ {
+ (*m_os) += ConvertString<std::string>(str);
+ }
+ void operator()(const std::wstring_view& str) const
+ {
+ (*m_os) += ConvertString<std::string>(str);
+ }
+ void operator() (bool val) const
+ {
+ m_os->append(val ? "true" : "false");
+ }
+};
+
+template<>
+struct ValueRenderer<wchar_t> : ValueRendererBase<wchar_t>
+{
+ ValueRenderer(std::wstring& os)
+ : ValueRendererBase<wchar_t>::ValueRendererBase<wchar_t>(os)
+ {
+ }
+
+ using ValueRendererBase<wchar_t>::operator ();
+ void operator()(const std::string& str) const
+ {
+ (*m_os) += ConvertString<std::wstring>(str);
+ }
+ void operator()(const std::string_view& str) const
+ {
+ (*m_os) += ConvertString<std::wstring>(str);
+ }
+ void operator() (bool val) const
+ {
+ // fmt::format_to(GetOs(), L"{}", (const wchar_t*)(val ? "true" : "false"));
+ m_os->append(val ? L"true" : L"false");
+ }
+};
+
+struct UnaryOperation : BaseVisitor<InternalValue>
+{
+ using BaseVisitor::operator ();
+
+ UnaryOperation(UnaryExpression::Operation oper)
+ : m_oper(oper)
+ {
+ }
+
+ InternalValue operator() (int64_t val) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = val ? false : true;
+ break;
+ case jinja2::UnaryExpression::UnaryPlus:
+ result = +val;
+ break;
+ case jinja2::UnaryExpression::UnaryMinus:
+ result = -val;
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator() (double val) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = fabs(val) > std::numeric_limits<double>::epsilon() ? false : true;
+ break;
+ case jinja2::UnaryExpression::UnaryPlus:
+ result = +val;
+ break;
+ case jinja2::UnaryExpression::UnaryMinus:
+ result = -val;
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator() (bool val) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = !val;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator() (const MapAdapter&) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator() (const ListAdapter&) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ template<typename CharT>
+ InternalValue operator() (const std::basic_string<CharT>& val) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = val.empty();
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ template<typename CharT>
+ InternalValue operator() (const std::basic_string_view<CharT>& val) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = val.empty();
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ InternalValue operator() (const EmptyValue&) const
+ {
+ InternalValue result;
+ switch (m_oper)
+ {
+ case jinja2::UnaryExpression::LogicalNot:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ UnaryExpression::Operation m_oper;
+};
+
+struct BinaryMathOperation : BaseVisitor<>
+{
+ using BaseVisitor::operator ();
+ using ResultType = InternalValue;
+ // InternalValue operator() (int, int) const {return InternalValue();}
+
+ bool AlmostEqual(double x, double y) const
+ {
+ return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * 6
+ || std::abs(x - y) < std::numeric_limits<double>::min();
+ }
+
+ BinaryMathOperation(BinaryExpression::Operation oper, BinaryExpression::CompareType compType = BinaryExpression::CaseSensitive)
+ : m_oper(oper)
+ , m_compType(compType)
+ {
+ }
+
+ ResultType operator() (double left, double right) const
+ {
+ ResultType result = 0.0;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::Plus:
+ result = left + right;
+ break;
+ case jinja2::BinaryExpression::Minus:
+ result = left - right;
+ break;
+ case jinja2::BinaryExpression::Mul:
+ result = left * right;
+ break;
+ case jinja2::BinaryExpression::Div:
+ result = left / right;
+ break;
+ case jinja2::BinaryExpression::DivReminder:
+ result = std::remainder(left, right);
+ break;
+ case jinja2::BinaryExpression::DivInteger:
+ {
+ double val = left / right;
+ result = val < 0 ? ceil(val) : floor(val);
+ break;
+ }
+ case jinja2::BinaryExpression::Pow:
+ result = pow(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalEq:
+ result = AlmostEqual(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = !AlmostEqual(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalGt:
+ result = left > right;
+ break;
+ case jinja2::BinaryExpression::LogicalLt:
+ result = left < right;
+ break;
+ case jinja2::BinaryExpression::LogicalGe:
+ result = left > right || AlmostEqual(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalLe:
+ result = left < right || AlmostEqual(left, right);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ ResultType operator() (int64_t left, int64_t right) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::Plus:
+ result = left + right;
+ break;
+ case jinja2::BinaryExpression::Minus:
+ result = left - right;
+ break;
+ case jinja2::BinaryExpression::Mul:
+ result = left * right;
+ break;
+ case jinja2::BinaryExpression::DivInteger:
+ result = left / right;
+ break;
+ case jinja2::BinaryExpression::Div:
+ case jinja2::BinaryExpression::DivReminder:
+ case jinja2::BinaryExpression::Pow:
+ result = this->operator ()(static_cast<double>(left), static_cast<double>(right));
+ break;
+ case jinja2::BinaryExpression::LogicalEq:
+ result = left == right;
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = left != right;
+ break;
+ case jinja2::BinaryExpression::LogicalGt:
+ result = left > right;
+ break;
+ case jinja2::BinaryExpression::LogicalLt:
+ result = left < right;
+ break;
+ case jinja2::BinaryExpression::LogicalGe:
+ result = left >= right;
+ break;
+ case jinja2::BinaryExpression::LogicalLe:
+ result = left <= right;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ ResultType operator() (int64_t left, double right) const
+ {
+ return this->operator ()(static_cast<double>(left), static_cast<double>(right));
+ }
+
+ ResultType operator() (double left, int64_t right) const
+ {
+ return this->operator ()(static_cast<double>(left), static_cast<double>(right));
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string<CharT> &right) const
+ {
+ return ProcessStrings(std::basic_string_view<CharT>(left), std::basic_string_view<CharT>(right));
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const
+ {
+ auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
+ return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr));
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string<CharT> &right) const
+ {
+ return ProcessStrings(left, std::basic_string_view<CharT>(right));
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string<CharT2>& right) const
+ {
+ auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
+ return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr));
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string<CharT> &left, const std::basic_string_view<CharT> &right) const
+ {
+ return ProcessStrings(std::basic_string_view<CharT>(left), right);
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string<CharT1>& left, const std::basic_string_view<CharT2>& right) const
+ {
+ auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
+ return ProcessStrings(std::basic_string_view<CharT1>(left), std::basic_string_view<CharT1>(rightStr));
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string_view<CharT> &left, const std::basic_string_view<CharT> &right) const
+ {
+ return ProcessStrings(left, right);
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, ResultType> operator() (const std::basic_string_view<CharT1>& left, const std::basic_string_view<CharT2>& right) const
+ {
+ auto rightStr = ConvertString<std::basic_string<CharT1>>(right);
+ return ProcessStrings(left, std::basic_string_view<CharT1>(rightStr));
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string<CharT> &left, int64_t right) const
+ {
+ return RepeatString(std::basic_string_view<CharT>(left), right);
+ }
+
+ template<typename CharT>
+ ResultType operator() (const std::basic_string_view<CharT> &left, int64_t right) const
+ {
+ return RepeatString(left, right);
+ }
+
+ template<typename CharT>
+ ResultType RepeatString(const std::basic_string_view<CharT>& left, const int64_t right) const
+ {
+ using string = std::basic_string<CharT>;
+ ResultType result;
+
+ if(m_oper == jinja2::BinaryExpression::Mul)
+ {
+ string str;
+ for (int i = 0; i < right; ++i)
+ str.append(left.begin(), left.end());
+ result = TargetString(std::move(str));
+ }
+ return result;
+ }
+
+ template<typename CharT>
+ ResultType ProcessStrings(const std::basic_string_view<CharT>& left, const std::basic_string_view<CharT>& right) const
+ {
+ using string = std::basic_string<CharT>;
+ ResultType result;
+
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::Plus:
+ {
+ auto str = string(left.begin(), left.end());
+ str.append(right.begin(), right.end());
+ result = TargetString(std::move(str));
+ break;
+ }
+ case jinja2::BinaryExpression::LogicalEq:
+ result = m_compType == BinaryExpression::CaseSensitive ? left == right : boost::iequals(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = m_compType == BinaryExpression::CaseSensitive ? left != right : !boost::iequals(left, right);
+ break;
+ case jinja2::BinaryExpression::LogicalGt:
+ result = m_compType == BinaryExpression::CaseSensitive ? left > right : boost::lexicographical_compare(right, left, boost::algorithm::is_iless());
+ break;
+ case jinja2::BinaryExpression::LogicalLt:
+ result = m_compType == BinaryExpression::CaseSensitive ? left < right : boost::lexicographical_compare(left, right, boost::algorithm::is_iless());
+ break;
+ case jinja2::BinaryExpression::LogicalGe:
+ if (m_compType == BinaryExpression::CaseSensitive)
+ {
+ result = left >= right;
+ }
+ else
+ {
+ result = boost::iequals(left, right) ? true : boost::lexicographical_compare(right, left, boost::algorithm::is_iless());
+ }
+ break;
+ case jinja2::BinaryExpression::LogicalLe:
+ if (m_compType == BinaryExpression::CaseSensitive)
+ {
+ result = left <= right;
+ }
+ else
+ {
+ result = boost::iequals(left, right) ? true : boost::lexicographical_compare(left, right, boost::algorithm::is_iless());
+ }
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ ResultType operator() (const KeyValuePair& left, const KeyValuePair& right) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalEq:
+ result = ConvertToBool(this->operator ()(left.key, right.key)) && ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalEq, m_compType));
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = ConvertToBool(this->operator ()(left.key, right.key)) || ConvertToBool(Apply2<BinaryMathOperation>(left.value, right.value, BinaryExpression::LogicalNe, m_compType));
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ ResultType operator() (const ListAdapter& left, const ListAdapter& right) const
+ {
+ ResultType result;
+ if (m_oper == jinja2::BinaryExpression::Plus)
+ {
+ InternalValueList values;
+ values.reserve(left.GetSize().value_or(0) + right.GetSize().value_or(0));
+ for (auto& v : left)
+ values.push_back(v);
+ for (auto& v : right)
+ values.push_back(v);
+ result = ListAdapter::CreateAdapter(std::move(values));
+ }
+
+ return result;
+ }
+
+ ResultType operator() (const ListAdapter& left, int64_t right) const
+ {
+ ResultType result;
+ if (right >= 0 && m_oper == jinja2::BinaryExpression::Mul)
+ {
+ InternalValueList values;
+ values.reserve(left.GetSize().value_or(0));
+ for (auto& v : left)
+ values.push_back(v);
+ auto listSize = values.size() * right;
+ result = ListAdapter::CreateAdapter(static_cast<size_t>(listSize),
+ [size = values.size(), values = std::move(values)](size_t idx) { return values[idx % size]; });
+ }
+
+ return result;
+ }
+
+ ResultType operator() (bool left, bool right) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalEq:
+ result = left == right;
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = left != right;
+ break;
+ case jinja2::BinaryExpression::LogicalLt:
+ result = (left ? 1 : 0) < (right ? 1 : 0);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ ResultType operator() (EmptyValue, EmptyValue) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalEq:
+ result = true;
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = false;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ template<typename T>
+ ResultType operator() (EmptyValue, T&&) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalEq:
+ result = false;
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ template<typename T>
+ ResultType operator() (T&&, EmptyValue) const
+ {
+ ResultType result;
+ switch (m_oper)
+ {
+ case jinja2::BinaryExpression::LogicalEq:
+ result = false;
+ break;
+ case jinja2::BinaryExpression::LogicalNe:
+ result = true;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ BinaryExpression::Operation m_oper;
+ BinaryExpression::CompareType m_compType;
+};
+
+struct BooleanEvaluator : BaseVisitor<bool>
+{
+ using BaseVisitor::operator ();
+
+ bool operator() (int64_t val) const
+ {
+ return val != 0;
+ }
+
+ bool operator() (double val) const
+ {
+ return fabs(val) < std::numeric_limits<double>::epsilon();
+ }
+
+ bool operator() (bool val) const
+ {
+ return val;
+ }
+
+ template<typename CharT>
+ bool operator()(const std::basic_string<CharT>& str) const
+ {
+ return !str.empty();
+ }
+
+ template<typename CharT>
+ bool operator()(const std::basic_string_view<CharT>& str) const
+ {
+ return !str.empty();
+ }
+
+ bool operator() (const MapAdapter& val) const
+ {
+ return val.GetSize() != 0ULL;
+ }
+
+ bool operator() (const ListAdapter& val) const
+ {
+ return val.GetSize() != 0ULL;
+ }
+
+ bool operator() (const EmptyValue&) const
+ {
+ return false;
+ }
+};
+
+template<typename TargetType>
+struct NumberEvaluator
+{
+ NumberEvaluator(TargetType def = 0) : m_def(def)
+ {}
+
+ TargetType operator ()(int64_t val) const
+ {
+ return static_cast<TargetType>(val);
+ }
+ TargetType operator ()(double val) const
+ {
+ return static_cast<TargetType>(val);
+ }
+ TargetType operator ()(bool val) const
+ {
+ return static_cast<TargetType>(val);
+ }
+ template<typename U>
+ TargetType operator()(U&&) const
+ {
+ return m_def;
+ }
+
+ TargetType m_def;
+};
+
+using IntegerEvaluator = NumberEvaluator<int64_t>;
+using DoubleEvaluator = NumberEvaluator<double>;
+
+
+struct StringJoiner : BaseVisitor<TargetString>
+{
+ using BaseVisitor::operator ();
+
+ template<typename CharT>
+ TargetString operator() (EmptyValue, const std::basic_string<CharT>& str) const
+ {
+ return str;
+ }
+
+ template<typename CharT>
+ TargetString operator() (EmptyValue, const std::basic_string_view<CharT>& str) const
+ {
+ return std::basic_string<CharT>(str.begin(), str.end());
+ }
+
+ template<typename CharT>
+ TargetString operator() (const std::basic_string<CharT>& left, const std::basic_string<CharT>& right) const
+ {
+ return left + right;
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (const std::basic_string<CharT1>& left, const std::basic_string<CharT2>& right) const
+ {
+ return left + ConvertString<std::basic_string<CharT1>>(right);
+ }
+
+ template<typename CharT>
+ TargetString operator() (std::basic_string<CharT> left, const std::basic_string_view<CharT>& right) const
+ {
+ left.append(right.begin(), right.end());
+ return std::move(left);
+ }
+
+ template<typename CharT1, typename CharT2>
+ std::enable_if_t<!std::is_same<CharT1, CharT2>::value, TargetString> operator() (std::basic_string<CharT1> left, const std::basic_string_view<CharT2>& right) const
+ {
+ auto r = ConvertString<std::basic_string<CharT1>>(right);
+ left.append(r.begin(), r.end());
+ return std::move(left);
+ }
+};
+
+template<typename Fn>
+struct StringConverterImpl : public BaseVisitor<decltype(std::declval<Fn>()(std::declval<std::string_view>()))>
+{
+ using R = decltype(std::declval<Fn>()(std::string_view()));
+ using BaseVisitor<R>::operator ();
+
+ StringConverterImpl(const Fn& fn) : m_fn(fn) {}
+
+ template<typename CharT>
+ R operator()(const std::basic_string<CharT>& str) const
+ {
+ return m_fn(std::basic_string_view<CharT>(str));
+ }
+
+ template<typename CharT>
+ R operator()(const std::basic_string_view<CharT>& str) const
+ {
+ return m_fn(str);
+ }
+
+ const Fn& m_fn;
+};
+
+template<typename CharT>
+struct SameStringGetter : public visitors::BaseVisitor<nonstd::expected<void, std::basic_string<CharT>>>
+{
+ using ResultString = std::basic_string<CharT>;
+ using OtherString = std::conditional_t<std::is_same<CharT, char>::value, std::wstring, std::string>;
+ using ResultStringView = std::basic_string_view<CharT>;
+ using OtherStringView = std::conditional_t<std::is_same<CharT, char>::value, std::wstring_view, std::string_view>;
+ using Result = nonstd::expected<void, ResultString>;
+ using BaseVisitor<Result>::operator ();
+
+ Result operator()(const ResultString& str) const
+ {
+ return nonstd::make_unexpected(str);
+ }
+
+ Result operator()(const ResultStringView& str) const
+ {
+ return nonstd::make_unexpected(ResultString(str.begin(), str.end()));
+ }
+
+ Result operator()(const OtherString& str) const
+ {
+ return nonstd::make_unexpected(ConvertString<ResultString>(str));
+ }
+
+ Result operator()(const OtherStringView& str) const
+ {
+ return nonstd::make_unexpected(ConvertString<ResultString>(str));
+ }
+};
+
+} // namespace visitors
+
+inline bool ConvertToBool(const InternalValue& val)
+{
+ return Apply<visitors::BooleanEvaluator>(val);
+}
+
+inline int64_t ConvertToInt(const InternalValue& val, int64_t def = 0)
+{
+ return Apply<visitors::IntegerEvaluator>(val, def);
+}
+
+inline double ConvertToDouble(const InternalValue& val, double def = 0)
+{
+ return Apply<visitors::DoubleEvaluator>(val, def);
+}
+
+template<template<typename> class Cvt = visitors::StringConverterImpl, typename Fn>
+auto ApplyStringConverter(const InternalValue& str, Fn&& fn)
+{
+ return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn));
+}
+
+template<typename CharT>
+auto GetAsSameString(const std::basic_string<CharT>&, const InternalValue& val)
+{
+ using Result = std::optional<std::basic_string<CharT>>;
+ auto result = Apply<visitors::SameStringGetter<CharT>>(val);
+ if (!result)
+ return Result(result.error());
+
+ return Result();
+}
+
+template<typename CharT>
+auto GetAsSameString(const std::basic_string_view<CharT>&, const InternalValue& val)
+{
+ using Result = std::optional<std::basic_string<CharT>>;
+ auto result = Apply<visitors::SameStringGetter<CharT>>(val);
+ if (!result)
+ return Result(result.error());
+
+ return Result();
+}
+
+inline bool operator==(const InternalValueData& lhs, const InternalValueData& rhs)
+{
+ InternalValue cmpRes;
+ cmpRes = Apply2<visitors::BinaryMathOperation>(lhs, rhs, BinaryExpression::LogicalEq);
+ return ConvertToBool(cmpRes);
+}
+
+inline bool operator!=(const InternalValueData& lhs, const InternalValueData& rhs)
+{
+ return !(lhs == rhs);
+}
+
+} // namespace jinja2
+
+#endif // JINJA2CPP_SRC_VALUE_VISITORS_H
diff --git a/contrib/libs/jinja2cpp/ya.make b/contrib/libs/jinja2cpp/ya.make
new file mode 100644
index 0000000000..fff6152672
--- /dev/null
+++ b/contrib/libs/jinja2cpp/ya.make
@@ -0,0 +1,68 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(
+ MIT AND
+ MPL-1.1 AND
+ MPL-2.0
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(1.3.1)
+
+ORIGINAL_SOURCE(https://github.com/jinja2cpp/Jinja2Cpp/archive/1.3.1.tar.gz)
+
+PEERDIR(
+ contrib/libs/fmt
+ contrib/libs/rapidjson
+ contrib/restricted/boost/algorithm
+ contrib/restricted/boost/container
+ contrib/restricted/boost/filesystem
+ contrib/restricted/boost/numeric_conversion
+ contrib/restricted/boost/unordered
+ contrib/restricted/boost/variant
+ contrib/restricted/expected-lite
+)
+
+ADDINCL(
+ GLOBAL contrib/libs/jinja2cpp/include
+ GLOBAL contrib/libs/rapidjson/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ -DBOOST_ERROR_CODE_HEADER_ONLY
+ -DBOOST_SYSTEM_NO_DEPRECATED
+ -DFMT_HEADER_ONLY=1
+ -DFMT_USE_INTERNAL=TRUE
+ -DJINJA2CPP_BUILD_AS_SHARED
+ -DJINJA2CPP_LINK_AS_SHARED
+ -DJINJA2CPP_USE_REGEX_BOOST
+)
+
+SRCS(
+ src/binding/rapid_json_serializer.cpp
+ src/error_info.cpp
+ src/expression_evaluator.cpp
+ src/expression_parser.cpp
+ src/filesystem_handler.cpp
+ src/filters.cpp
+ src/generic_list.cpp
+ src/internal_value.cpp
+ src/lexer.cpp
+ src/serialize_filters.cpp
+ src/statements.cpp
+ src/string_converter_filter.cpp
+ src/template.cpp
+ src/template_env.cpp
+ src/template_parser.cpp
+ src/testers.cpp
+ src/value.cpp
+)
+
+END()
diff --git a/contrib/restricted/boost/filesystem/.yandex_meta/devtools.copyrights.report b/contrib/restricted/boost/filesystem/.yandex_meta/devtools.copyrights.report
new file mode 100644
index 0000000000..9491a2d6aa
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/.yandex_meta/devtools.copyrights.report
@@ -0,0 +1,503 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits
+# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files
+# $ # user comments
+# $
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP COPYRIGHT_SERVICE_LABEL 0106e29d815b6be7c9d2d76fe96e71bd
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2019 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/error_handling.hpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 0577358848238a2ccf175d4150580c55
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2001 Dietmar Kuehl
+ // Copyright 2018-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/operations.cpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL 29b575183bf7715bbbd1ac18a2d411d8
+BELONGS ya.make
+ License text:
+ // Copyright Vladimir Prus 2002
+ // Copyright Beman Dawes 2002-2005, 2009
+ // Copyright Andrey Semashev 2021-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/path.hpp [3:5]
+ src/path.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 31dab43fa080cbb98017ca60b8536ab0
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2003
+ // Copyright Andrey Semashev 2021-2023
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/config.hpp [3:4]
+ include/boost/filesystem/exception.hpp [3:4]
+ src/exception.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 40943c7a65585558b5a8a647077af90b
+BELONGS ya.make
+ License text:
+ // Copyright 2009 Beman Dawes
+ // Copyright 2022 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/codecvt_error_category.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 4654eb52be7c4d34a5204f2702ae51df
+BELONGS ya.make
+ License text:
+ // Copyright 2021-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/posix_tools.hpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL 4b76d293e7e0b07c0269a1e8929c4f9a
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2005 Beman Dawes
+ // Use, modification, and distribution is subject to the Boost Software
+ // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/portability.cpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL 4d26b906dcfafbac2938fc9f553e8765
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2008, 2009
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/path_traits.cpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL 5ca418207a9a9e57884eb84192e8056e
+BELONGS ya.make
+ License text:
+ // Copyright Vladimir Prus 2004.
+ // Distributed under the Boost Software License, Version 1.0.
+ // (See accompanying file LICENSE_1_0.txt
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/utf8_codecvt_facet.cpp [1:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL 5e2789af15a3f0dceb1a6835b257fde9
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2001 Dietmar Kuehl
+ // Copyright 2019, 2022-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/directory.cpp [3:5]
+ src/error_handling.hpp [3:4]
+ src/operations.cpp [3:5]
+ src/windows_tools.hpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL 5ee46bb8f4cfeb03304f0c40f09d31f4
+BELONGS ya.make
+ License text:
+ * Copyright Andrey Semashev 2021.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/detail/footer.hpp [2:4]
+ include/boost/filesystem/detail/header.hpp [2:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 5f5f9caa49264cbb4cb51ab4b0070946
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2010
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem.hpp [3:3]
+ src/unique_path.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 6543e5a77421b1baaaf79a9a214494d8
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2001 Dietmar Kuehl
+ // Copyright 2019, 2022-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/directory.cpp [3:5]
+ src/operations.cpp [3:5]
+ src/windows_tools.hpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL 68dc02374f20324ede6b933ec6332a17
+BELONGS ya.make
+ License text:
+ // Copyright 2001 Dietmar Kuehl
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2021-2022 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/windows_tools.hpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL 6d59e81fdd72b00433694c8efadc38af
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2019, 2022
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/directory.hpp [3:7]
+
+KEEP COPYRIGHT_SERVICE_LABEL 7594bc6afcdef4c27932b42df772d631
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2010
+ // Copyright Andrey Semashev 2020, 2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/unique_path.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 75ef7529f27f9a710eea2869f97333f1
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2008
+ // Copyright Andrey Semashev 2021-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/path.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 791de63e8a85039486e8b095c3912ad1
+BELONGS ya.make
+ License text:
+ // Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
+ // Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/detail/utf8_codecvt_facet.hpp [1:2]
+
+KEEP COPYRIGHT_SERVICE_LABEL 79847d622a0625e4d77b3b1d999b7ed6
+BELONGS ya.make
+ License text:
+ // Copyright 2021 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/atomic_ref.hpp [3:3]
+ src/atomic_tools.hpp [3:3]
+ src/private_config.hpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL 7d2ce92b5651d63b72a42d5c7eaed67f
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2003
+ // Copyright Andrey Semashev 2019
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/exception.hpp [3:4]
+ include/boost/filesystem/file_status.hpp [3:7]
+ src/exception.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL 9a830292eb7d7692c5b622def685d36a
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2009
+ // Copyright Andrey Semashev 2022-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/detail/path_traits.hpp [3:4]
+ src/windows_file_codecvt.cpp [3:3]
+ src/windows_file_codecvt.hpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL 9cc1cbe1db928fd72af9865f2977880e
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2020-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/operations.hpp [3:7]
+
+KEEP COPYRIGHT_SERVICE_LABEL 9e7998261b95e69bd3775056e00500da
+BELONGS ya.make
+ License text:
+ // Copyright 2009 Beman Dawes
+ // Copyright 2022 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/codecvt_error_category.cpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL a00ef2913f053c40e19dbb04a67f672d
+BELONGS ya.make
+ License text:
+ // Copyright Vladimir Prus 2002
+ // Copyright Beman Dawes 2002-2005, 2009
+ // Copyright Andrey Semashev 2021-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/path.hpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL aab0f7927eb905b8b8d0be7082f1129f
+BELONGS ya.make
+ License text:
+ // Copyright Andrey Semashev 2023
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/cstdio.hpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL bbdecc0b9528cc3e811715c5267e8923
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2003
+ // Copyright Andrey Semashev 2021-2023
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/config.hpp [3:4]
+ include/boost/filesystem/fstream.hpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL bd5e197c40c289f178ae8b137d43f0a4
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2009
+ // Copyright Andrey Semashev 2022-2024
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/detail/path_traits.hpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL bdde4763d6a57dbe14d89f4fed53a008
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002
+ // Copyright Andrey Semashev 2021-2023
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/fstream.hpp [3:4]
+
+KEEP COPYRIGHT_SERVICE_LABEL bdf099522fdb545139e4e9b02eab27e1
+BELONGS ya.make
+ License text:
+ // Copyright 2002-2009, 2014 Beman Dawes
+ // Copyright 2001 Dietmar Kuehl
+ // Copyright 2019, 2022-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/directory.cpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL cbcc84d7a80f4d8aeabd2a87eca7072c
+BELONGS ya.make
+ License text:
+ // Copyright 2020 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ src/platform_config.hpp [3:3]
+
+KEEP COPYRIGHT_SERVICE_LABEL cf5e56ae189ec8cdbf6a61c940073563
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2019, 2022
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/directory.hpp [3:7]
+ include/boost/filesystem/file_status.hpp [3:7]
+ include/boost/filesystem/operations.hpp [3:7]
+ include/boost/filesystem/path.hpp [3:5]
+
+KEEP COPYRIGHT_SERVICE_LABEL de436355f6cfaff01d7ce0d889764ae9
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2019, 2022
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/directory.hpp [3:7]
+ include/boost/filesystem/file_status.hpp [3:7]
+ include/boost/filesystem/operations.hpp [3:7]
+
+KEEP COPYRIGHT_SERVICE_LABEL e38d04805dcf88e6564ae734a4cd8a49
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2019, 2022
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/directory.hpp [3:7]
+ include/boost/filesystem/file_status.hpp [3:7]
+ include/boost/filesystem/operations.hpp [3:7]
+
+KEEP COPYRIGHT_SERVICE_LABEL eb3a0ca480ae49d67ca93db4d779aa0f
+BELONGS ya.make
+ License text:
+ * Copyright (c) 2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/detail/type_traits/conjunction.hpp [6:6]
+ include/boost/filesystem/detail/type_traits/disjunction.hpp [6:6]
+ include/boost/filesystem/detail/type_traits/negation.hpp [6:6]
+
+KEEP COPYRIGHT_SERVICE_LABEL f47d692aff1724cf5d6cfdcc998f6b84
+BELONGS ya.make
+ License text:
+ // Copyright Beman Dawes 2002-2009
+ // Copyright Jan Langer 2002
+ // Copyright Dietmar Kuehl 2001
+ // Copyright Vladimir Prus 2002
+ // Copyright Andrey Semashev 2019, 2022
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/filesystem/directory.hpp [3:7]
+ include/boost/filesystem/file_status.hpp [3:7]
+ include/boost/filesystem/operations.hpp [3:7]
diff --git a/contrib/restricted/boost/filesystem/.yandex_meta/devtools.licenses.report b/contrib/restricted/boost/filesystem/.yandex_meta/devtools.licenses.report
new file mode 100644
index 0000000000..d0ef27a0ba
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/.yandex_meta/devtools.licenses.report
@@ -0,0 +1,179 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits
+# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files
+# $ # user comments
+# $
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP BSL-1.0 1e1b35c3ae13c65f63b2c7467cce8a87
+BELONGS ya.make
+ License text:
+ // Distributed under the Boost Software License, Version 1.0.
+ // (See accompanying file LICENSE_1_0.txt
+ // or copy at http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ src/utf8_codecvt_facet.cpp [2:4]
+
+KEEP BSL-1.0 2077ca9d01c7e6d6029ec1763233c5b0
+BELONGS ya.make
+ License text:
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/boost/filesystem/detail/type_traits/conjunction.hpp [2:4]
+ include/boost/filesystem/detail/type_traits/disjunction.hpp [2:4]
+ include/boost/filesystem/detail/type_traits/negation.hpp [2:4]
+
+KEEP BSL-1.0 49af97cadb10453f2b05003f793e4adc
+BELONGS ya.make
+ License text:
+ Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 88.89
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ README.md [28:28]
+
+KEEP BSL-1.0 67c315f84c9f7fe64ea5034b22a3514e
+BELONGS ya.make
+ License text:
+ // Distributed under the Boost Software License, Version 1.0.
+ // See http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ src/codecvt_error_category.cpp [6:7]
+
+KEEP BSL-1.0 a5006bb276a0e8fcc0c080cd5a14814e
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 55.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ README.md [17:17]
+
+KEEP BSL-1.0 a779859d4b2e8896d4ed9ba8f600ca99
+BELONGS ya.make
+ License text:
+ // Distributed under the Boost Software License, Version 1.0.
+ // (See http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/boost/filesystem/detail/utf8_codecvt_facet.hpp [4:5]
+
+KEEP BSL-1.0 bb6c4a594a57e74611676fda9449a7e3
+BELONGS ya.make
+ License text:
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/boost/filesystem/detail/footer.hpp [3:5]
+ include/boost/filesystem/detail/header.hpp [3:5]
+
+KEEP BSL-1.0 da2a87ccf5ae416e33c8d6bfe78baa14
+BELONGS ya.make
+ License text:
+ // Distributed under the Boost Software License, Version 1.0.
+ // See http://www.boost.org/LICENSE_1_0.txt
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/boost/filesystem.hpp [5:6]
+ include/boost/filesystem/config.hpp [6:7]
+ include/boost/filesystem/cstdio.hpp [5:6]
+ include/boost/filesystem/detail/path_traits.hpp [6:7]
+ include/boost/filesystem/directory.hpp [9:10]
+ include/boost/filesystem/exception.hpp [6:7]
+ include/boost/filesystem/file_status.hpp [9:10]
+ include/boost/filesystem/fstream.hpp [6:7]
+ include/boost/filesystem/operations.hpp [9:10]
+ include/boost/filesystem/path.hpp [7:8]
+ src/atomic_ref.hpp [5:6]
+ src/atomic_tools.hpp [5:6]
+ src/directory.cpp [7:8]
+ src/error_handling.hpp [6:7]
+ src/exception.cpp [6:7]
+ src/operations.cpp [7:8]
+ src/path.cpp [6:7]
+ src/path_traits.cpp [5:6]
+ src/platform_config.hpp [5:6]
+ src/posix_tools.hpp [5:6]
+ src/private_config.hpp [5:6]
+ src/unique_path.cpp [6:7]
+ src/windows_file_codecvt.cpp [5:6]
+ src/windows_file_codecvt.hpp [5:6]
+ src/windows_tools.hpp [7:8]
+
+KEEP BSL-1.0 f0421be5a87b237d5f722433355f7a9e
+BELONGS ya.make
+ License text:
+ // Use, modification, and distribution is subject to the Boost Software
+ // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+ // at http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 96.88
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ src/portability.cpp [4:6]
diff --git a/contrib/restricted/boost/filesystem/.yandex_meta/licenses.list.txt b/contrib/restricted/boost/filesystem/.yandex_meta/licenses.list.txt
new file mode 100644
index 0000000000..7437319473
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/.yandex_meta/licenses.list.txt
@@ -0,0 +1,175 @@
+====================BSL-1.0====================
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+* Submit your patches as [pull requests](https://github.com/boostorg/filesystem/compare) against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+
+====================BSL-1.0====================
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+
+====================BSL-1.0====================
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+// Use, modification, and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+// at http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt
+// or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+// Distributed under the Boost Software License, Version 1.0.
+// (See http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+====================COPYRIGHT====================
+ * Copyright Andrey Semashev 2021.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+
+
+====================COPYRIGHT====================
+ * Copyright (c) 2024 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2021-2022 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2002-2005 Beman Dawes
+// Use, modification, and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+
+
+====================COPYRIGHT====================
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2018-2024 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2019, 2022-2024 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2019 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2009 Beman Dawes
+// Copyright 2022 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2020 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2021 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright 2021-2024 Andrey Semashev
+
+
+====================COPYRIGHT====================
+// Copyright Andrey Semashev 2023
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2002
+// Copyright Andrey Semashev 2021-2023
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2002-2009
+// Copyright Jan Langer 2002
+// Copyright Dietmar Kuehl 2001
+// Copyright Vladimir Prus 2002
+// Copyright Andrey Semashev 2019, 2022
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2002-2009
+// Copyright Jan Langer 2002
+// Copyright Dietmar Kuehl 2001
+// Copyright Vladimir Prus 2002
+// Copyright Andrey Semashev 2020-2024
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2003
+// Copyright Andrey Semashev 2019
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2003
+// Copyright Andrey Semashev 2021-2023
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2008
+// Copyright Andrey Semashev 2021-2024
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2008, 2009
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2009
+// Copyright Andrey Semashev 2022-2024
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2010
+
+
+====================COPYRIGHT====================
+// Copyright Beman Dawes 2010
+// Copyright Andrey Semashev 2020, 2024
+
+
+====================COPYRIGHT====================
+// Copyright Vladimir Prus 2002
+// Copyright Beman Dawes 2002-2005, 2009
+// Copyright Andrey Semashev 2021-2024
+
+
+====================COPYRIGHT====================
+// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
+// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
+
+
+====================COPYRIGHT====================
+// Copyright Vladimir Prus 2004.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt
diff --git a/contrib/restricted/boost/filesystem/README.md b/contrib/restricted/boost/filesystem/README.md
new file mode 100644
index 0000000000..9af3f0140d
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/README.md
@@ -0,0 +1,28 @@
+# Boost.Filesystem
+
+Boost.Filesystem, part of collection of the [Boost C++ Libraries](https://github.com/boostorg), provides facilities to manipulate files and directories, and the paths that identify them.
+
+### Directories
+
+* **doc** - Documentation sources
+* **include** - Interface headers of Boost.Filesystem
+* **src** - Compilable source files of Boost.Filesystem
+* **test** - Boost.Filesystem unit tests
+* **example** - Boost.Filesystem usage examples
+
+### More information
+
+* [Documentation](https://boost.org/libs/filesystem)
+* [Report bugs](https://github.com/boostorg/filesystem/issues/new). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
+* Submit your patches as [pull requests](https://github.com/boostorg/filesystem/compare) against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+### Build status
+
+Branch | GitHub Actions | AppVeyor | Test Matrix | Dependencies |
+:-------------: | -------------- | -------- | ----------- | ------------ |
+[`master`](https://github.com/boostorg/filesystem/tree/master) | [![GitHub Actions](https://github.com/boostorg/filesystem/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/boostorg/filesystem/actions?query=branch%3Amaster) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/nx3e7bcavvn3q953/branch/master?svg=true)](https://ci.appveyor.com/project/Lastique/filesystem/branch/master) | [![Tests](https://img.shields.io/badge/matrix-master-brightgreen.svg)](http://www.boost.org/development/tests/master/developer/filesystem.html) | [![Dependencies](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/filesystem.html)
+[`develop`](https://github.com/boostorg/filesystem/tree/develop) | [![GitHub Actions](https://github.com/boostorg/filesystem/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/boostorg/filesystem/actions?query=branch%3Adevelop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/nx3e7bcavvn3q953/branch/develop?svg=true)](https://ci.appveyor.com/project/Lastique/filesystem/branch/develop) | [![Tests](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](http://www.boost.org/development/tests/develop/developer/filesystem.html) | [![Dependencies](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/filesystem.html)
+
+### License
+
+Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem.hpp
new file mode 100644
index 0000000000..ee22fbcac2
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem.hpp
@@ -0,0 +1,22 @@
+// boost/filesystem.hpp --------------------------------------------------------------//
+
+// Copyright Beman Dawes 2010
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_FILESYSTEM_HPP
+#define BOOST_FILESYSTEM_FILESYSTEM_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/filesystem/directory.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/file_status.hpp>
+
+#endif // BOOST_FILESYSTEM_FILESYSTEM_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/config.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/config.hpp
new file mode 100644
index 0000000000..795a0fe2c2
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/config.hpp
@@ -0,0 +1,153 @@
+// boost/filesystem/v3/config.hpp ----------------------------------------------------//
+
+// Copyright Beman Dawes 2003
+// Copyright Andrey Semashev 2021-2023
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_CONFIG_HPP
+#define BOOST_FILESYSTEM_CONFIG_HPP
+
+// This header implements separate compilation features as described in
+// http://www.boost.org/more/separate_compilation.html
+
+#include <boost/config.hpp>
+#include <boost/system/api_config.hpp> // for BOOST_POSIX_API or BOOST_WINDOWS_API
+#include <boost/detail/workaround.hpp>
+
+#if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION != 3 && BOOST_FILESYSTEM_VERSION != 4
+#error Compiling Boost.Filesystem file with BOOST_FILESYSTEM_VERSION defined != 3 or 4
+#endif
+
+#if defined(BOOST_FILESYSTEM_SOURCE)
+#undef BOOST_FILESYSTEM_VERSION
+#define BOOST_FILESYSTEM_VERSION 4
+#elif !defined(BOOST_FILESYSTEM_VERSION)
+#define BOOST_FILESYSTEM_VERSION 3
+#endif
+
+#define BOOST_FILESYSTEM_VERSIONED_SYM(sym) BOOST_JOIN(sym, BOOST_JOIN(_v, BOOST_FILESYSTEM_VERSION))
+#define BOOST_FILESYSTEM_VERSION_NAMESPACE BOOST_JOIN(v, BOOST_FILESYSTEM_VERSION)
+
+#if BOOST_FILESYSTEM_VERSION == 4
+#undef BOOST_FILESYSTEM_DEPRECATED
+#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED)
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+#endif
+#endif
+
+#define BOOST_FILESYSTEM_I18N // aid users wishing to compile several versions
+
+// BOOST_FILESYSTEM_DEPRECATED needed for source compiles -----------------------------//
+
+#ifdef BOOST_FILESYSTEM_SOURCE
+#define BOOST_FILESYSTEM_DEPRECATED
+#undef BOOST_FILESYSTEM_NO_DEPRECATED // fixes #9454, src bld fails if NO_DEP defined
+#endif
+
+#if defined(BOOST_FILESYSTEM_DEPRECATED) && defined(BOOST_FILESYSTEM_NO_DEPRECATED)
+#error Both BOOST_FILESYSTEM_DEPRECATED and BOOST_FILESYSTEM_NO_DEPRECATED are defined
+#endif
+
+// throw an exception ----------------------------------------------------------------//
+//
+// Exceptions were originally thrown via boost::throw_exception().
+// As throw_exception() became more complex, it caused user error reporting
+// to be harder to interpret, since the exception reported became much more complex.
+// The immediate fix was to throw directly, wrapped in a macro to make any later change
+// easier.
+
+#define BOOST_FILESYSTEM_THROW(EX) throw EX
+
+#if defined(BOOST_NO_STD_WSTRING)
+#error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support
+#endif
+
+// Deprecated symbols markup -----------------------------------------------------------//
+
+#if !defined(BOOST_FILESYSTEM_ALLOW_DEPRECATED)
+#define BOOST_FILESYSTEM_DETAIL_DEPRECATED(msg) BOOST_DEPRECATED(msg)
+#else
+#define BOOST_FILESYSTEM_DETAIL_DEPRECATED(msg)
+#endif
+
+
+// This header implements separate compilation features as described in
+// http://www.boost.org/more/separate_compilation.html
+
+// normalize macros ------------------------------------------------------------------//
+
+#if !defined(BOOST_FILESYSTEM_DYN_LINK) && !defined(BOOST_FILESYSTEM_STATIC_LINK) && !defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_ALL_STATIC_LINK)
+#define BOOST_FILESYSTEM_STATIC_LINK
+#endif
+
+#if defined(BOOST_ALL_DYN_LINK) && !defined(BOOST_FILESYSTEM_DYN_LINK)
+#define BOOST_FILESYSTEM_DYN_LINK
+#elif defined(BOOST_ALL_STATIC_LINK) && !defined(BOOST_FILESYSTEM_STATIC_LINK)
+#define BOOST_FILESYSTEM_STATIC_LINK
+#endif
+
+#if defined(BOOST_FILESYSTEM_DYN_LINK) && defined(BOOST_FILESYSTEM_STATIC_LINK)
+#error Must not define both BOOST_FILESYSTEM_DYN_LINK and BOOST_FILESYSTEM_STATIC_LINK
+#endif
+
+#if defined(BOOST_ALL_NO_LIB) && !defined(BOOST_FILESYSTEM_NO_LIB)
+#define BOOST_FILESYSTEM_NO_LIB
+#endif
+
+// enable dynamic linking ------------------------------------------------------------//
+
+#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FILESYSTEM_DYN_LINK)
+#if defined(BOOST_FILESYSTEM_SOURCE)
+#define BOOST_FILESYSTEM_DECL BOOST_SYMBOL_EXPORT
+#else
+#define BOOST_FILESYSTEM_DECL BOOST_SYMBOL_IMPORT
+#endif
+#else
+#define BOOST_FILESYSTEM_DECL
+#endif
+
+// enable automatic library variant selection ----------------------------------------//
+
+#if !defined(BOOST_FILESYSTEM_SOURCE) && !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_FILESYSTEM_NO_LIB)
+//
+// Set the name of our library, this will get undef'ed by auto_link.hpp
+// once it's done with it:
+//
+#define BOOST_LIB_NAME boost_filesystem
+//
+// If we're importing code from a dll, then tell auto_link.hpp about it:
+//
+#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FILESYSTEM_DYN_LINK)
+#define BOOST_DYN_LINK
+#endif
+//
+// And include the header that does the work:
+//
+#include <boost/config/auto_link.hpp>
+#endif // auto-linking disabled
+
+#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) ||\
+ (defined(BOOST_LIBSTDCXX_VERSION) && (BOOST_LIBSTDCXX_VERSION < 50000)) ||\
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION < 100))
+// Indicates that the standard library fstream types do not support move constructor/assignment.
+#define BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS
+#endif
+
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) && \
+ (\
+ (defined(BOOST_DINKUMWARE_STDLIB) && defined(_HAS_CXX23) && (_HAS_CXX23 != 0) && defined(_MSVC_STL_UPDATE) && (_MSVC_STL_UPDATE < 202208L)) || \
+ (defined(BOOST_LIBSTDCXX_VERSION) && (BOOST_LIBSTDCXX_VERSION < 110400 || (BOOST_LIBSTDCXX_VERSION >= 120000 && BOOST_LIBSTDCXX_VERSION < 120200)) && (BOOST_CXX_VERSION > 202002L))\
+ )
+// Indicates that std::string_view has implicit constructor from ranges that was present in an early C++23 draft (N4892).
+// This was later rectified by marking the constructor explicit (https://wg21.link/p2499). Unfortunately, some compilers
+// were released with the constructor being implicit.
+#define BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR
+#endif
+
+#endif // BOOST_FILESYSTEM_CONFIG_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/cstdio.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/cstdio.hpp
new file mode 100644
index 0000000000..b09e89f002
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/cstdio.hpp
@@ -0,0 +1,87 @@
+// boost/filesystem/cstdio.hpp ------------------------------------------------------//
+
+// Copyright Andrey Semashev 2023
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_CSTDIO_HPP
+#define BOOST_FILESYSTEM_CSTDIO_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <cstdio>
+#if defined(BOOST_WINDOWS_API)
+#include <wchar.h>
+#include <cstddef>
+#include <cstring>
+#include <cstdlib>
+#endif
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+#if defined(BOOST_WINDOWS_API)
+
+inline std::FILE* fopen(filesystem::path const& p, const char* mode)
+{
+#if defined(__CYGWIN__) || (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) && defined(__STRICT_ANSI__))
+ // Cygwin and MinGW32 in strict ANSI mode do not declare _wfopen
+ return std::fopen(p.string().c_str(), mode);
+#else
+ // mode should only contain ASCII characters and is likely short
+ struct small_array
+ {
+ wchar_t buf[128u];
+ wchar_t* p;
+
+ small_array() noexcept : p(buf) {}
+ ~small_array() noexcept
+ {
+ if (BOOST_UNLIKELY(p != buf))
+ std::free(p);
+ }
+ }
+ wmode;
+ std::size_t wmode_len = std::mbstowcs(wmode.p, mode, sizeof(wmode.buf) / sizeof(wchar_t));
+ if (BOOST_UNLIKELY(wmode_len >= (sizeof(wmode.buf) / sizeof(wchar_t))))
+ {
+ wmode_len = std::mbstowcs(nullptr, mode, 0u);
+ // Check for size overflow, including (size_t)-1, which indicates mbstowcs error
+ if (BOOST_UNLIKELY(wmode_len >= (static_cast< std::size_t >(-1) / sizeof(wchar_t))))
+ return nullptr;
+
+ wmode.p = static_cast< wchar_t* >(std::malloc((wmode_len + 1u) * sizeof(wchar_t)));
+ if (BOOST_UNLIKELY(!wmode.p))
+ return nullptr;
+
+ std::size_t wmode_len2 = std::mbstowcs(wmode.p, mode, wmode_len + 1u);
+ if (BOOST_UNLIKELY(wmode_len2 > wmode_len))
+ return nullptr;
+ }
+
+ return ::_wfopen(p.c_str(), wmode.p);
+#endif
+}
+
+#else // defined(BOOST_WINDOWS_API)
+
+inline std::FILE* fopen(filesystem::path const& p, const char* mode)
+{
+ return std::fopen(p.c_str(), mode);
+}
+
+#endif // defined(BOOST_WINDOWS_API)
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_CSTDIO_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/footer.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/footer.hpp
new file mode 100644
index 0000000000..ce4bc1feed
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/footer.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright Andrey Semashev 2021.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#if !defined(BOOST_FILESYSTEM_ENABLE_WARNINGS)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+#pragma warning(pop)
+
+#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \
+ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__)
+
+#pragma GCC diagnostic pop
+
+#endif
+
+#endif // !defined(BOOST_FILESYSTEM_ENABLE_WARNINGS)
+
+#include <boost/config/abi_suffix.hpp>
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/header.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/header.hpp
new file mode 100644
index 0000000000..f98b0aba95
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/header.hpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright Andrey Semashev 2021.
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#include <boost/config/abi_prefix.hpp>
+
+#if !defined(BOOST_FILESYSTEM_ENABLE_WARNINGS)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+#pragma warning(push, 3)
+// 'm_A' : class 'A' needs to have dll-interface to be used by clients of class 'B'
+#pragma warning(disable: 4251)
+// non dll-interface class 'A' used as base for dll-interface class 'B'
+#pragma warning(disable: 4275)
+// 'int' : forcing value to bool 'true' or 'false' (performance warning)
+#pragma warning(disable: 4800)
+// unreferenced formal parameter
+#pragma warning(disable: 4100)
+// conditional expression is constant
+#pragma warning(disable: 4127)
+// function marked as __forceinline not inlined
+#pragma warning(disable: 4714)
+// decorated name length exceeded, name was truncated
+#pragma warning(disable: 4503)
+// 'X': This function or variable may be unsafe. Consider using Y instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
+#pragma warning(disable: 4996)
+// qualifier applied to function type has no meaning; ignored
+#pragma warning(disable: 4180)
+// qualifier applied to reference type; ignored
+#pragma warning(disable: 4181)
+
+#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \
+ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__)
+
+// Note: clang-cl goes here as well, as it seems to support gcc-style warning control pragmas.
+
+#pragma GCC diagnostic push
+// unused parameter 'arg'
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+// unused function 'foo'
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#if defined(__clang__)
+// template argument uses unnamed type
+#pragma clang diagnostic ignored "-Wunnamed-type-template-args"
+#endif // defined(__clang__)
+
+#endif
+
+#endif // !defined(BOOST_FILESYSTEM_ENABLE_WARNINGS)
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/path_traits.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/path_traits.hpp
new file mode 100644
index 0000000000..416c073788
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/path_traits.hpp
@@ -0,0 +1,734 @@
+// filesystem path_traits.hpp --------------------------------------------------------//
+
+// Copyright Beman Dawes 2009
+// Copyright Andrey Semashev 2022-2024
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+#ifndef BOOST_FILESYSTEM_DETAIL_PATH_TRAITS_HPP
+#define BOOST_FILESYSTEM_DETAIL_PATH_TRAITS_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <cstddef>
+#include <cstring> // for strlen
+#include <cwchar> // for mbstate_t, wcslen
+#include <locale>
+#include <string>
+#include <iterator>
+#include <type_traits>
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+#include <string_view>
+#endif
+#include <boost/assert.hpp>
+#include <boost/system/error_category.hpp>
+#include <boost/iterator/is_iterator.hpp>
+#include <boost/filesystem/detail/type_traits/conjunction.hpp>
+#if defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+#include <boost/filesystem/detail/type_traits/disjunction.hpp>
+#endif
+#if defined(BOOST_FILESYSTEM_DEPRECATED) && BOOST_FILESYSTEM_VERSION < 4
+#include <vector>
+#include <list>
+#endif
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+
+template< typename, typename > class basic_string_view;
+
+namespace container {
+template< typename, typename, typename > class basic_string;
+} // namespace container
+
+namespace filesystem {
+
+BOOST_FILESYSTEM_DECL system::error_category const& codecvt_error_category() noexcept;
+
+class directory_entry;
+
+namespace detail {
+namespace path_traits {
+
+#if defined(BOOST_WINDOWS_API)
+typedef wchar_t path_native_char_type;
+#define BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE false
+#define BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE true
+#else
+typedef char path_native_char_type;
+#define BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE true
+#define BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE false
+#endif
+
+typedef std::codecvt< wchar_t, char, std::mbstate_t > codecvt_type;
+
+struct unknown_type_tag {};
+struct ntcts_type_tag {};
+struct char_ptr_tag : ntcts_type_tag {};
+struct char_array_tag : ntcts_type_tag {};
+struct string_class_tag {};
+struct std_string_tag : string_class_tag {};
+struct boost_container_string_tag : string_class_tag {};
+struct std_string_view_tag : string_class_tag {};
+struct boost_string_view_tag : string_class_tag {};
+struct range_type_tag {};
+struct directory_entry_tag {};
+
+//! The traits define a number of properties of a path source
+template< typename T >
+struct path_source_traits
+{
+ //! The kind of the path source. Useful for dispatching.
+ typedef unknown_type_tag tag_type;
+ //! Character type that the source contains
+ typedef void char_type;
+ //! Indicates whether the source is natively supported by \c path::string_type as arguments for constructors/assignment/appending
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct path_source_traits< char* >
+{
+ typedef char_ptr_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< const char* >
+{
+ typedef char_ptr_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< wchar_t* >
+{
+ typedef char_ptr_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< >
+struct path_source_traits< const wchar_t* >
+{
+ typedef char_ptr_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< >
+struct path_source_traits< char[] >
+{
+ typedef char_array_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< const char[] >
+{
+ typedef char_array_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< wchar_t[] >
+{
+ typedef char_array_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< >
+struct path_source_traits< const wchar_t[] >
+{
+ typedef char_array_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< std::size_t N >
+struct path_source_traits< char[N] >
+{
+ typedef char_array_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< std::size_t N >
+struct path_source_traits< const char[N] >
+{
+ typedef char_array_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< std::size_t N >
+struct path_source_traits< wchar_t[N] >
+{
+ typedef char_array_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< std::size_t N >
+struct path_source_traits< const wchar_t[N] >
+{
+ typedef char_array_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< >
+struct path_source_traits< std::string >
+{
+ typedef std_string_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< std::wstring >
+{
+ typedef std_string_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+template< >
+struct path_source_traits< boost::container::basic_string< char, std::char_traits< char >, void > >
+{
+ typedef boost_container_string_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct path_source_traits< boost::container::basic_string< wchar_t, std::char_traits< wchar_t >, void > >
+{
+ typedef boost_container_string_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+
+template< >
+struct path_source_traits< std::string_view >
+{
+ typedef std_string_view_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE;
+};
+
+template< >
+struct path_source_traits< std::wstring_view >
+{
+ typedef std_string_view_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE;
+};
+
+#endif // !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+
+template< >
+struct path_source_traits< boost::basic_string_view< char, std::char_traits< char > > >
+{
+ typedef boost_string_view_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct path_source_traits< boost::basic_string_view< wchar_t, std::char_traits< wchar_t > > >
+{
+ typedef boost_string_view_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+#if defined(BOOST_FILESYSTEM_DEPRECATED) && BOOST_FILESYSTEM_VERSION < 4
+template< >
+struct
+BOOST_FILESYSTEM_DETAIL_DEPRECATED("Boost.Filesystem path construction/assignment/appending from containers is deprecated, use strings or iterators instead.")
+path_source_traits< std::vector< char > >
+{
+ // Since C++11 this could be string_class_tag as std::vector gained data() member
+ typedef range_type_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct
+BOOST_FILESYSTEM_DETAIL_DEPRECATED("Boost.Filesystem path construction/assignment/appending from containers is deprecated, use strings or iterators instead.")
+path_source_traits< std::vector< wchar_t > >
+{
+ // Since C++11 this could be string_class_tag as std::vector gained data() member
+ typedef range_type_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct
+BOOST_FILESYSTEM_DETAIL_DEPRECATED("Boost.Filesystem path construction/assignment/appending from containers is deprecated, use strings or iterators instead.")
+path_source_traits< std::list< char > >
+{
+ typedef range_type_tag tag_type;
+ typedef char char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+template< >
+struct
+BOOST_FILESYSTEM_DETAIL_DEPRECATED("Boost.Filesystem path construction/assignment/appending from containers is deprecated, use strings or iterators instead.")
+path_source_traits< std::list< wchar_t > >
+{
+ typedef range_type_tag tag_type;
+ typedef wchar_t char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+#endif // defined(BOOST_FILESYSTEM_DEPRECATED) && BOOST_FILESYSTEM_VERSION < 4
+
+template< >
+struct path_source_traits< directory_entry >
+{
+ typedef directory_entry_tag tag_type;
+ typedef path_native_char_type char_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_native = false;
+};
+
+#undef BOOST_FILESYSTEM_DETAIL_IS_CHAR_NATIVE
+#undef BOOST_FILESYSTEM_DETAIL_IS_WCHAR_T_NATIVE
+
+
+//! The trait tests if the type is a known path Source tag
+template< typename Tag >
+struct is_known_path_source_tag :
+ public std::true_type
+{
+};
+
+template< >
+struct is_known_path_source_tag< unknown_type_tag > :
+ public std::false_type
+{
+};
+
+//! The trait tests if the type is compatible with path Source requirements
+template< typename T >
+struct is_path_source :
+ public is_known_path_source_tag< typename path_source_traits< T >::tag_type >::type
+{
+};
+
+
+//! The trait indicates whether the type is a path Source that is natively supported by path::string_type as the source for construction/assignment/appending
+template< typename T >
+struct is_native_path_source :
+ public std::integral_constant< bool, path_source_traits< T >::is_native >
+{
+};
+
+
+//! The trait indicates whether the type is one of the supported path character types
+template< typename T >
+struct is_path_char_type :
+ public std::false_type
+{
+};
+
+template< >
+struct is_path_char_type< char > :
+ public std::true_type
+{
+};
+
+template< >
+struct is_path_char_type< wchar_t > :
+ public std::true_type
+{
+};
+
+
+template< typename Iterator >
+struct is_iterator_to_path_chars :
+ public is_path_char_type< typename std::iterator_traits< Iterator >::value_type >::type
+{
+};
+
+//! The trait indicates whether the type is an iterator over a sequence of path characters
+template< typename Iterator >
+struct is_path_source_iterator :
+ public std::integral_constant<
+ bool,
+ detail::conjunction<
+ boost::iterators::is_iterator< Iterator >,
+ is_iterator_to_path_chars< Iterator >
+ >::value
+ >
+{
+};
+
+
+//! The trait indicates whether the type is a pointer to a sequence of native path characters
+template< typename T >
+struct is_native_char_ptr :
+ public std::false_type
+{
+};
+
+template< >
+struct is_native_char_ptr< path_native_char_type* > :
+ public std::true_type
+{
+};
+
+template< >
+struct is_native_char_ptr< const path_native_char_type* > :
+ public std::true_type
+{
+};
+
+
+//! Converts character encoding using the supplied codecvt facet. If \a cvt is \c nullptr then \c path::codecvt() will be used.
+BOOST_FILESYSTEM_DECL
+void convert(const char* from, const char* from_end, std::wstring& to, const codecvt_type* cvt = nullptr);
+
+//! \overload convert
+BOOST_FILESYSTEM_DECL
+void convert(const wchar_t* from, const wchar_t* from_end, std::string& to, const codecvt_type* cvt = nullptr);
+
+
+// Source dispatch -----------------------------------------------------------------//
+
+template< typename Source, typename Callback >
+typename Callback::result_type dispatch(Source const& source, Callback cb, const codecvt_type* cvt = nullptr);
+
+template< typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(const char* source, Callback cb, const codecvt_type* cvt, ntcts_type_tag)
+{
+ return cb(source, source + std::strlen(source), cvt);
+}
+
+template< typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(const wchar_t* source, Callback cb, const codecvt_type* cvt, ntcts_type_tag)
+{
+ return cb(source, source + std::wcslen(source), cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(Source const& source, Callback cb, const codecvt_type* cvt, string_class_tag)
+{
+ return cb(source.data(), source.data() + source.size(), cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(Source const& source, Callback cb, const codecvt_type* cvt, range_type_tag)
+{
+ std::basic_string< typename Source::value_type > src(source.begin(), source.end());
+ return cb(src.data(), src.data() + src.size(), cvt);
+}
+
+#if defined(BOOST_FILESYSTEM_DEPRECATED) && BOOST_FILESYSTEM_VERSION < 4
+
+template< typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(std::vector< char > const& source, Callback cb, const codecvt_type* cvt, range_type_tag)
+{
+ const char* data = nullptr, *data_end = nullptr;
+ if (!source.empty())
+ {
+ data = &source[0];
+ data_end = data + source.size();
+ }
+ return cb(data, data_end, cvt);
+}
+
+template< typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(std::vector< wchar_t > const& source, Callback cb, const codecvt_type* cvt, range_type_tag)
+{
+ const wchar_t* data = nullptr, *data_end = nullptr;
+ if (!source.empty())
+ {
+ data = &source[0];
+ data_end = data + source.size();
+ }
+ return cb(data, data_end, cvt);
+}
+
+#endif // defined(BOOST_FILESYSTEM_DEPRECATED) && BOOST_FILESYSTEM_VERSION < 4
+
+// Defined in directory.hpp to avoid circular header dependencies
+template< typename Callback >
+typename Callback::result_type dispatch(directory_entry const& de, Callback cb, const codecvt_type* cvt, directory_entry_tag);
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(Source const& source, Callback cb, const codecvt_type* cvt)
+{
+ return path_traits::dispatch(source, cb, cvt,
+ typename path_traits::path_source_traits< typename std::remove_cv< Source >::type >::tag_type());
+}
+
+
+typedef char yes_type;
+struct no_type { char buf[2]; };
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+namespace is_convertible_to_path_source_impl {
+
+yes_type check_convertible(const char*);
+yes_type check_convertible(const wchar_t*);
+yes_type check_convertible(std::string const&);
+yes_type check_convertible(std::wstring const&);
+yes_type check_convertible(boost::container::basic_string< char, std::char_traits< char >, void > const&);
+yes_type check_convertible(boost::container::basic_string< wchar_t, std::char_traits< wchar_t >, void > const&);
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+yes_type check_convertible(std::string_view const&);
+yes_type check_convertible(std::wstring_view const&);
+#endif
+yes_type check_convertible(boost::basic_string_view< char, std::char_traits< char > > const&);
+yes_type check_convertible(boost::basic_string_view< wchar_t, std::char_traits< wchar_t > > const&);
+no_type check_convertible(std::nullptr_t);
+no_type check_convertible(...);
+
+} // namespace is_convertible_to_path_source_impl
+
+//! The type trait indicates whether the type has a conversion path to one of the path source types
+template< typename T >
+struct is_convertible_to_path_source :
+ public std::integral_constant<
+ bool,
+ sizeof(is_convertible_to_path_source_impl::check_convertible(std::declval< T const& >())) == sizeof(yes_type)
+ >
+{
+};
+
+#else // !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+// Note: We use separate checks for convertibility to std::string_view and other types to avoid ambiguity with an implicit range constructor
+// of std::string_view in the early C++23 draft (N4892). If a user's type is convertible to e.g. std::string and also satisfies
+// ranges::contiguous_range and ranges::sized_range concepts then the conversion is ambiguous: the type is convertible to std::string
+// through the conversion operator in the user's class and is also convertible to std::string_view through the implicit conversion
+// constructor in std::string_view. The solution is to check convertibility to std::string_view separately first.
+
+namespace is_convertible_to_std_string_view_impl {
+
+yes_type check_convertible(std::string_view const&);
+yes_type check_convertible(std::wstring_view const&);
+no_type check_convertible(std::nullptr_t);
+no_type check_convertible(...);
+
+} // namespace is_convertible_to_std_string_view_impl
+
+template< typename T >
+struct is_convertible_to_std_string_view :
+ public std::integral_constant<
+ bool,
+ sizeof(is_convertible_to_std_string_view_impl::check_convertible(std::declval< T const& >())) == sizeof(yes_type)
+ >
+{
+};
+
+namespace is_convertible_to_path_source_non_std_string_view_impl {
+
+yes_type check_convertible(const char*);
+yes_type check_convertible(const wchar_t*);
+yes_type check_convertible(std::string const&);
+yes_type check_convertible(std::wstring const&);
+yes_type check_convertible(boost::container::basic_string< char, std::char_traits< char >, void > const&);
+yes_type check_convertible(boost::container::basic_string< wchar_t, std::char_traits< wchar_t >, void > const&);
+yes_type check_convertible(boost::basic_string_view< char, std::char_traits< char > > const&);
+yes_type check_convertible(boost::basic_string_view< wchar_t, std::char_traits< wchar_t > > const&);
+no_type check_convertible(std::nullptr_t);
+no_type check_convertible(...);
+
+} // namespace is_convertible_to_path_source_non_std_string_view_impl
+
+template< typename T >
+struct is_convertible_to_path_source_non_std_string_view :
+ public std::integral_constant<
+ bool,
+ sizeof(is_convertible_to_path_source_non_std_string_view_impl::check_convertible(std::declval< T const& >())) == sizeof(yes_type)
+ >
+{
+};
+
+//! The type trait indicates whether the type has a conversion path to one of the path source types
+template< typename T >
+struct is_convertible_to_path_source :
+ public std::integral_constant<
+ bool,
+ detail::disjunction<
+ is_convertible_to_std_string_view< T >,
+ is_convertible_to_path_source_non_std_string_view< T >
+ >::value
+ >
+{
+};
+
+#endif // !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+//! The type trait makes \a T dependent on the second template argument. Used to delay type resolution and name binding.
+template< typename T, typename >
+struct make_dependent
+{
+ typedef T type;
+};
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(const char* source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< const char*, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(const wchar_t* source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< const wchar_t*, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(std::string const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::string, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(std::wstring const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::wstring, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl
+(
+ boost::container::basic_string< char, std::char_traits< char >, void > const& source,
+ Callback cb,
+ const codecvt_type* cvt
+)
+{
+ typedef typename path_traits::make_dependent< boost::container::basic_string< char, std::char_traits< char >, void >, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl
+(
+ boost::container::basic_string< wchar_t, std::char_traits< wchar_t >, void > const& source,
+ Callback cb,
+ const codecvt_type* cvt
+)
+{
+ typedef typename path_traits::make_dependent< boost::container::basic_string< wchar_t, std::char_traits< wchar_t >, void >, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl
+(
+ boost::basic_string_view< char, std::char_traits< char > > const& source,
+ Callback cb,
+ const codecvt_type* cvt
+)
+{
+ typedef typename path_traits::make_dependent< boost::basic_string_view< char, std::char_traits< char > >, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl
+(
+ boost::basic_string_view< wchar_t, std::char_traits< wchar_t > > const& source,
+ Callback cb,
+ const codecvt_type* cvt
+)
+{
+ typedef typename path_traits::make_dependent< boost::basic_string_view< wchar_t, std::char_traits< wchar_t > >, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(std::string_view const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::string_view, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_impl(std::wstring_view const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::wstring_view, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+#endif // !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible(Source const& source, Callback cb, const codecvt_type* cvt = nullptr)
+{
+ typedef typename std::remove_cv< Source >::type source_t;
+ return path_traits::dispatch_convertible_impl< source_t >(source, cb, cvt);
+}
+
+#else // !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_sv_impl(std::string_view const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::string_view, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch_convertible_sv_impl(std::wstring_view const& source, Callback cb, const codecvt_type* cvt)
+{
+ typedef typename path_traits::make_dependent< std::wstring_view, Source >::type source_t;
+ return path_traits::dispatch(static_cast< source_t const& >(source), cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename std::enable_if<
+ !is_convertible_to_std_string_view< typename std::remove_cv< Source >::type >::value,
+ typename Callback::result_type
+>::type dispatch_convertible(Source const& source, Callback cb, const codecvt_type* cvt = nullptr)
+{
+ typedef typename std::remove_cv< Source >::type source_t;
+ return path_traits::dispatch_convertible_impl< source_t >(source, cb, cvt);
+}
+
+template< typename Source, typename Callback >
+BOOST_FORCEINLINE typename std::enable_if<
+ is_convertible_to_std_string_view< typename std::remove_cv< Source >::type >::value,
+ typename Callback::result_type
+>::type dispatch_convertible(Source const& source, Callback cb, const codecvt_type* cvt = nullptr)
+{
+ typedef typename std::remove_cv< Source >::type source_t;
+ return path_traits::dispatch_convertible_sv_impl< source_t >(source, cb, cvt);
+}
+
+#endif // !defined(BOOST_FILESYSTEM_DETAIL_CXX23_STRING_VIEW_HAS_IMPLICIT_RANGE_CTOR)
+
+} // namespace path_traits
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_DETAIL_PATH_TRAITS_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/conjunction.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/conjunction.hpp
new file mode 100644
index 0000000000..9d7cd830cc
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/conjunction.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2024 Andrey Semashev
+ */
+/*!
+ * \file filesystem/detail/type_traits/conjunction.hpp
+ *
+ * This header contains definition of \c conjunction type trait.
+ */
+
+#ifndef BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
+#define BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/filesystem/config.hpp>
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using std::conjunction;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/conjunction.hpp>
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using boost::conjunction;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#endif
+
+#endif // BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/disjunction.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/disjunction.hpp
new file mode 100644
index 0000000000..e9adc9d3e6
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/disjunction.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2024 Andrey Semashev
+ */
+/*!
+ * \file filesystem/detail/type_traits/disjunction.hpp
+ *
+ * This header contains definition of \c disjunction type trait.
+ */
+
+#ifndef BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
+#define BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/filesystem/config.hpp>
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using std::disjunction;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/disjunction.hpp>
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using boost::disjunction;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#endif
+
+#endif // BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/negation.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/negation.hpp
new file mode 100644
index 0000000000..a0c86b4011
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/type_traits/negation.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2024 Andrey Semashev
+ */
+/*!
+ * \file filesystem/detail/type_traits/negation.hpp
+ *
+ * This header contains definition of \c negation type trait.
+ */
+
+#ifndef BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
+#define BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/filesystem/config.hpp>
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using std::negation;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/negation.hpp>
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+using boost::negation;
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#endif
+
+#endif // BOOST_FILESYSTEM_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/utf8_codecvt_facet.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/utf8_codecvt_facet.hpp
new file mode 100644
index 0000000000..4df9a7c620
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/detail/utf8_codecvt_facet.hpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
+// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
+
+// Distributed under the Boost Software License, Version 1.0.
+// (See http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef BOOST_FILESYSTEM_UTF8_CODECVT_FACET_HPP
+#define BOOST_FILESYSTEM_UTF8_CODECVT_FACET_HPP
+
+#include <boost/filesystem/config.hpp>
+
+#include <boost/filesystem/detail/header.hpp>
+
+#define BOOST_UTF8_BEGIN_NAMESPACE \
+ namespace boost { \
+ namespace filesystem { \
+ namespace detail {
+
+#define BOOST_UTF8_END_NAMESPACE \
+ } \
+ } \
+ }
+#define BOOST_UTF8_DECL BOOST_FILESYSTEM_DECL
+
+#include <boost/detail/utf8_codecvt_facet.hpp>
+
+#undef BOOST_UTF8_BEGIN_NAMESPACE
+#undef BOOST_UTF8_END_NAMESPACE
+#undef BOOST_UTF8_DECL
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_UTF8_CODECVT_FACET_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/directory.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/directory.hpp
new file mode 100644
index 0000000000..821b1a97bb
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/directory.hpp
@@ -0,0 +1,1039 @@
+// boost/filesystem/directory.hpp ---------------------------------------------------//
+
+// Copyright Beman Dawes 2002-2009
+// Copyright Jan Langer 2002
+// Copyright Dietmar Kuehl 2001
+// Copyright Vladimir Prus 2002
+// Copyright Andrey Semashev 2019, 2022
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_DIRECTORY_HPP
+#define BOOST_FILESYSTEM_DIRECTORY_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/file_status.hpp>
+#include <boost/filesystem/detail/path_traits.hpp>
+
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include <boost/assert.hpp>
+#include <boost/detail/bitmask.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+#include <boost/smart_ptr/intrusive_ref_counter.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/iterator/iterator_categories.hpp>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+
+enum class directory_options : unsigned int
+{
+ none = 0u,
+ skip_permission_denied = 1u, // if a directory cannot be opened because of insufficient permissions, pretend that the directory is empty
+ follow_directory_symlink = 1u << 1u, // recursive_directory_iterator: follow directory symlinks
+ skip_dangling_symlinks = 1u << 2u, // non-standard extension for recursive_directory_iterator: don't follow dangling directory symlinks,
+ pop_on_error = 1u << 3u, // non-standard extension for recursive_directory_iterator: instead of producing an end iterator on errors,
+ // repeatedly invoke pop() until it succeeds or the iterator becomes equal to end iterator
+ _detail_no_follow = 1u << 4u, // internal use only
+ _detail_no_push = 1u << 5u // internal use only
+};
+
+BOOST_BITMASK(directory_options)
+
+class directory_iterator;
+class recursive_directory_iterator;
+
+namespace detail {
+
+struct directory_iterator_params;
+
+BOOST_FILESYSTEM_DECL void directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, directory_iterator_params* params, system::error_code* ec);
+BOOST_FILESYSTEM_DECL void directory_iterator_increment(directory_iterator& it, system::error_code* ec);
+
+struct recur_dir_itr_imp;
+
+BOOST_FILESYSTEM_DECL void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, directory_options opts, system::error_code* ec);
+BOOST_FILESYSTEM_DECL void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec);
+BOOST_FILESYSTEM_DECL void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec);
+
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_entry //
+// //
+//--------------------------------------------------------------------------------------//
+
+// GCC has a problem with a member function named path within a namespace or
+// sub-namespace that also has a class named path. The workaround is to always
+// fully qualify the name path when it refers to the class name.
+
+class directory_entry
+{
+ friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, detail::directory_iterator_params* params, system::error_code* ec);
+ friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_increment(directory_iterator& it, system::error_code* ec);
+
+ friend BOOST_FILESYSTEM_DECL void detail::recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec);
+
+public:
+ typedef boost::filesystem::path::value_type value_type; // enables class path ctor taking directory_entry
+
+ directory_entry() noexcept {}
+
+ explicit directory_entry(boost::filesystem::path const& p);
+
+#if BOOST_FILESYSTEM_VERSION >= 4
+ directory_entry(boost::filesystem::path const& p, system::error_code& ec) :
+ m_path(p)
+ {
+ refresh_impl(&ec);
+ if (ec)
+ m_path.clear();
+ }
+#else
+ directory_entry(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status()) :
+ m_path(p), m_status(st), m_symlink_status(symlink_st)
+ {
+ }
+#endif
+
+ directory_entry(directory_entry const& rhs) :
+ m_path(rhs.m_path), m_status(rhs.m_status), m_symlink_status(rhs.m_symlink_status)
+ {
+ }
+
+ directory_entry& operator=(directory_entry const& rhs)
+ {
+ m_path = rhs.m_path;
+ m_status = rhs.m_status;
+ m_symlink_status = rhs.m_symlink_status;
+ return *this;
+ }
+
+ directory_entry(directory_entry&& rhs) noexcept :
+ m_path(static_cast< boost::filesystem::path&& >(rhs.m_path)),
+ m_status(static_cast< file_status&& >(rhs.m_status)),
+ m_symlink_status(static_cast< file_status&& >(rhs.m_symlink_status))
+ {
+ }
+
+ directory_entry& operator=(directory_entry&& rhs) noexcept
+ {
+ m_path = static_cast< boost::filesystem::path&& >(rhs.m_path);
+ m_status = static_cast< file_status&& >(rhs.m_status);
+ m_symlink_status = static_cast< file_status&& >(rhs.m_symlink_status);
+ return *this;
+ }
+
+ void assign(boost::filesystem::path&& p);
+
+#if BOOST_FILESYSTEM_VERSION >= 4
+ void assign(boost::filesystem::path&& p, system::error_code& ec)
+ {
+ m_path = static_cast< boost::filesystem::path&& >(p);
+ refresh_impl(&ec);
+ }
+#else
+ void assign(boost::filesystem::path&& p, file_status st, file_status symlink_st = file_status())
+ {
+ assign_with_status(static_cast< boost::filesystem::path&& >(p), st, symlink_st);
+ }
+#endif
+
+ void assign(boost::filesystem::path const& p);
+
+#if BOOST_FILESYSTEM_VERSION >= 4
+ void assign(boost::filesystem::path const& p, system::error_code& ec)
+ {
+ m_path = p;
+ refresh_impl(&ec);
+ }
+#else
+ void assign(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status())
+ {
+ assign_with_status(p, st, symlink_st);
+ }
+#endif
+
+ void replace_filename(boost::filesystem::path const& p);
+
+#if BOOST_FILESYSTEM_VERSION >= 4
+ void replace_filename(boost::filesystem::path const& p, system::error_code& ec)
+ {
+ m_path.replace_filename(p);
+ refresh_impl(&ec);
+ }
+#else
+ void replace_filename(boost::filesystem::path const& p, file_status st, file_status symlink_st = file_status())
+ {
+ replace_filename_with_status(p, st, symlink_st);
+ }
+
+ BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use directory_entry::replace_filename() instead")
+ void replace_leaf(boost::filesystem::path const& p, file_status st, file_status symlink_st)
+ {
+ replace_filename_with_status(p, st, symlink_st);
+ }
+#endif
+
+ boost::filesystem::path const& path() const noexcept { return m_path; }
+ operator boost::filesystem::path const&() const noexcept { return m_path; }
+
+ void refresh() { refresh_impl(); }
+ void refresh(system::error_code& ec) noexcept { refresh_impl(&ec); }
+
+ file_status status() const
+ {
+ if (!filesystem::status_known(m_status))
+ refresh_impl();
+ return m_status;
+ }
+
+ file_status status(system::error_code& ec) const noexcept
+ {
+ ec.clear();
+
+ if (!filesystem::status_known(m_status))
+ refresh_impl(&ec);
+ return m_status;
+ }
+
+ file_status symlink_status() const
+ {
+ if (!filesystem::status_known(m_symlink_status))
+ refresh_impl();
+ return m_symlink_status;
+ }
+
+ file_status symlink_status(system::error_code& ec) const noexcept
+ {
+ ec.clear();
+
+ if (!filesystem::status_known(m_symlink_status))
+ refresh_impl(&ec);
+ return m_symlink_status;
+ }
+
+ filesystem::file_type file_type() const
+ {
+ if (!filesystem::type_present(m_status))
+ refresh_impl();
+ return m_status.type();
+ }
+
+ filesystem::file_type file_type(system::error_code& ec) const noexcept
+ {
+ ec.clear();
+
+ if (!filesystem::type_present(m_status))
+ refresh_impl(&ec);
+ return m_status.type();
+ }
+
+ filesystem::file_type symlink_file_type() const
+ {
+ if (!filesystem::type_present(m_symlink_status))
+ refresh_impl();
+ return m_symlink_status.type();
+ }
+
+ filesystem::file_type symlink_file_type(system::error_code& ec) const noexcept
+ {
+ ec.clear();
+
+ if (!filesystem::type_present(m_symlink_status))
+ refresh_impl(&ec);
+ return m_symlink_status.type();
+ }
+
+ bool exists() const
+ {
+ filesystem::file_type ft = this->file_type();
+ return ft != filesystem::status_error && ft != filesystem::file_not_found;
+ }
+
+ bool exists(system::error_code& ec) const noexcept
+ {
+ filesystem::file_type ft = this->file_type(ec);
+ return ft != filesystem::status_error && ft != filesystem::file_not_found;
+ }
+
+ bool is_regular_file() const
+ {
+ return this->file_type() == filesystem::regular_file;
+ }
+
+ bool is_regular_file(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::regular_file;
+ }
+
+ bool is_directory() const
+ {
+ return this->file_type() == filesystem::directory_file;
+ }
+
+ bool is_directory(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::directory_file;
+ }
+
+ bool is_symlink() const
+ {
+ return this->symlink_file_type() == filesystem::symlink_file;
+ }
+
+ bool is_symlink(system::error_code& ec) const noexcept
+ {
+ return this->symlink_file_type(ec) == filesystem::symlink_file;
+ }
+
+ bool is_block_file() const
+ {
+ return this->file_type() == filesystem::block_file;
+ }
+
+ bool is_block_file(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::block_file;
+ }
+
+ bool is_character_file() const
+ {
+ return this->file_type() == filesystem::character_file;
+ }
+
+ bool is_character_file(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::character_file;
+ }
+
+ bool is_fifo() const
+ {
+ return this->file_type() == filesystem::fifo_file;
+ }
+
+ bool is_fifo(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::fifo_file;
+ }
+
+ bool is_socket() const
+ {
+ return this->file_type() == filesystem::socket_file;
+ }
+
+ bool is_socket(system::error_code& ec) const noexcept
+ {
+ return this->file_type(ec) == filesystem::socket_file;
+ }
+
+ bool is_reparse_file() const
+ {
+ return this->symlink_file_type() == filesystem::reparse_file;
+ }
+
+ bool is_reparse_file(system::error_code& ec) const noexcept
+ {
+ return this->symlink_file_type(ec) == filesystem::reparse_file;
+ }
+
+ bool is_other() const
+ {
+ filesystem::file_type ft = this->file_type();
+ return ft != filesystem::status_error && ft != filesystem::file_not_found &&
+ ft != filesystem::regular_file && ft != filesystem::directory_file;
+ }
+
+ bool is_other(system::error_code& ec) const noexcept
+ {
+ filesystem::file_type ft = this->file_type(ec);
+ return ft != filesystem::status_error && ft != filesystem::file_not_found &&
+ ft != filesystem::regular_file && ft != filesystem::directory_file;
+ }
+
+ bool operator==(directory_entry const& rhs) const { return m_path == rhs.m_path; }
+ bool operator!=(directory_entry const& rhs) const { return m_path != rhs.m_path; }
+ bool operator<(directory_entry const& rhs) const { return m_path < rhs.m_path; }
+ bool operator<=(directory_entry const& rhs) const { return m_path <= rhs.m_path; }
+ bool operator>(directory_entry const& rhs) const { return m_path > rhs.m_path; }
+ bool operator>=(directory_entry const& rhs) const { return m_path >= rhs.m_path; }
+
+private:
+ BOOST_FILESYSTEM_DECL void refresh_impl(system::error_code* ec = nullptr) const;
+
+ void assign_with_status(boost::filesystem::path&& p, file_status st, file_status symlink_st)
+ {
+ m_path = static_cast< boost::filesystem::path&& >(p);
+ m_status = static_cast< file_status&& >(st);
+ m_symlink_status = static_cast< file_status&& >(symlink_st);
+ }
+
+ void assign_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st)
+ {
+ m_path = p;
+ m_status = static_cast< file_status&& >(st);
+ m_symlink_status = static_cast< file_status&& >(symlink_st);
+ }
+
+ void replace_filename_with_status(boost::filesystem::path const& p, file_status st, file_status symlink_st)
+ {
+ m_path.replace_filename(p);
+ m_status = static_cast< file_status&& >(st);
+ m_symlink_status = static_cast< file_status&& >(symlink_st);
+ }
+
+private:
+ boost::filesystem::path m_path;
+ mutable file_status m_status; // stat()-like
+ mutable file_status m_symlink_status; // lstat()-like
+};
+
+#if !defined(BOOST_FILESYSTEM_SOURCE)
+
+inline directory_entry::directory_entry(boost::filesystem::path const& p) :
+ m_path(p)
+{
+#if BOOST_FILESYSTEM_VERSION >= 4
+ refresh_impl();
+#endif
+}
+
+inline void directory_entry::assign(boost::filesystem::path&& p)
+{
+ m_path = static_cast< boost::filesystem::path&& >(p);
+#if BOOST_FILESYSTEM_VERSION >= 4
+ refresh_impl();
+#else
+ m_status = file_status();
+ m_symlink_status = file_status();
+#endif
+}
+
+inline void directory_entry::assign(boost::filesystem::path const& p)
+{
+ m_path = p;
+#if BOOST_FILESYSTEM_VERSION >= 4
+ refresh_impl();
+#else
+ m_status = file_status();
+ m_symlink_status = file_status();
+#endif
+}
+
+inline void directory_entry::replace_filename(boost::filesystem::path const& p)
+{
+ m_path.replace_filename(p);
+#if BOOST_FILESYSTEM_VERSION >= 4
+ refresh_impl();
+#else
+ m_status = file_status();
+ m_symlink_status = file_status();
+#endif
+}
+
+#endif // !defined(BOOST_FILESYSTEM_SOURCE)
+
+namespace detail {
+namespace path_traits {
+
+// Dispatch function for integration with path class
+template< typename Callback >
+BOOST_FORCEINLINE typename Callback::result_type dispatch(directory_entry const& de, Callback cb, const codecvt_type* cvt, directory_entry_tag)
+{
+ boost::filesystem::path::string_type const& source = de.path().native();
+ return cb(source.data(), source.data() + source.size(), cvt);
+}
+
+} // namespace path_traits
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_entry overloads //
+// //
+//--------------------------------------------------------------------------------------//
+
+// Without these functions, calling (for example) 'is_directory' with a 'directory_entry' results in:
+// - a conversion to 'path' using 'operator boost::filesystem::path const&()',
+// - then a call to 'is_directory(path const& p)' which recomputes the status with 'detail::status(p)'.
+//
+// These functions avoid a costly recomputation of the status if one calls 'is_directory(e)' instead of 'is_directory(e.status())'
+
+inline file_status status(directory_entry const& e)
+{
+ return e.status();
+}
+
+inline file_status status(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.status(ec);
+}
+
+inline file_status symlink_status(directory_entry const& e)
+{
+ return e.symlink_status();
+}
+
+inline file_status symlink_status(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.symlink_status(ec);
+}
+
+inline bool type_present(directory_entry const& e)
+{
+ return e.file_type() != filesystem::status_error;
+}
+
+inline bool type_present(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.file_type(ec) != filesystem::status_error;
+}
+
+inline bool status_known(directory_entry const& e)
+{
+ return filesystem::status_known(e.status());
+}
+
+inline bool status_known(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return filesystem::status_known(e.status(ec));
+}
+
+inline bool exists(directory_entry const& e)
+{
+ return e.exists();
+}
+
+inline bool exists(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.exists(ec);
+}
+
+inline bool is_regular_file(directory_entry const& e)
+{
+ return e.is_regular_file();
+}
+
+inline bool is_regular_file(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_regular_file(ec);
+}
+
+inline bool is_directory(directory_entry const& e)
+{
+ return e.is_directory();
+}
+
+inline bool is_directory(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_directory(ec);
+}
+
+inline bool is_symlink(directory_entry const& e)
+{
+ return e.is_symlink();
+}
+
+inline bool is_symlink(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_symlink(ec);
+}
+
+inline bool is_block_file(directory_entry const& e)
+{
+ return e.is_block_file();
+}
+
+inline bool is_block_file(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_block_file(ec);
+}
+
+inline bool is_character_file(directory_entry const& e)
+{
+ return e.is_character_file();
+}
+
+inline bool is_character_file(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_character_file(ec);
+}
+
+inline bool is_fifo(directory_entry const& e)
+{
+ return e.is_fifo();
+}
+
+inline bool is_fifo(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_fifo(ec);
+}
+
+inline bool is_socket(directory_entry const& e)
+{
+ return e.is_socket();
+}
+
+inline bool is_socket(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_socket(ec);
+}
+
+inline bool is_reparse_file(directory_entry const& e)
+{
+ return e.is_reparse_file();
+}
+
+inline bool is_reparse_file(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_reparse_file(ec);
+}
+
+inline bool is_other(directory_entry const& e)
+{
+ return e.is_other();
+}
+
+inline bool is_other(directory_entry const& e, system::error_code& ec) noexcept
+{
+ return e.is_other(ec);
+}
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_iterator helpers //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace detail {
+
+struct dir_itr_imp :
+ public boost::intrusive_ref_counter< dir_itr_imp >
+{
+#ifdef BOOST_WINDOWS_API
+ bool close_handle;
+ unsigned char extra_data_format;
+ std::size_t current_offset;
+#endif
+ directory_entry dir_entry;
+ void* handle;
+
+ dir_itr_imp() noexcept :
+#ifdef BOOST_WINDOWS_API
+ close_handle(false),
+ extra_data_format(0u),
+ current_offset(0u),
+#endif
+ handle(nullptr)
+ {
+ }
+ BOOST_FILESYSTEM_DECL ~dir_itr_imp() noexcept;
+
+ BOOST_FILESYSTEM_DECL static void* operator new(std::size_t class_size, std::size_t extra_size) noexcept;
+ BOOST_FILESYSTEM_DECL static void operator delete(void* p, std::size_t extra_size) noexcept;
+ BOOST_FILESYSTEM_DECL static void operator delete(void* p) noexcept;
+};
+
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_iterator //
+// //
+//--------------------------------------------------------------------------------------//
+
+class directory_iterator :
+ public boost::iterator_facade<
+ directory_iterator,
+ directory_entry,
+ boost::single_pass_traversal_tag
+ >
+{
+ friend class boost::iterator_core_access;
+
+ friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, detail::directory_iterator_params* params, system::error_code* ec);
+ friend BOOST_FILESYSTEM_DECL void detail::directory_iterator_increment(directory_iterator& it, system::error_code* ec);
+
+ friend BOOST_FILESYSTEM_DECL void detail::recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec);
+
+public:
+ directory_iterator() noexcept {} // creates the "end" iterator
+
+ // iterator_facade derived classes don't seem to like implementations in
+ // separate translation unit dll's, so forward to detail functions
+ explicit directory_iterator(path const& p, directory_options opts = directory_options::none)
+ {
+ detail::directory_iterator_construct(*this, p, opts, nullptr, nullptr);
+ }
+
+ directory_iterator(path const& p, system::error_code& ec) noexcept
+ {
+ detail::directory_iterator_construct(*this, p, directory_options::none, nullptr, &ec);
+ }
+
+ directory_iterator(path const& p, directory_options opts, system::error_code& ec) noexcept
+ {
+ detail::directory_iterator_construct(*this, p, opts, nullptr, &ec);
+ }
+
+ directory_iterator(directory_iterator const&) = default;
+ directory_iterator& operator=(directory_iterator const&) = default;
+
+ directory_iterator(directory_iterator&& that) noexcept :
+ m_imp(static_cast< boost::intrusive_ptr< detail::dir_itr_imp >&& >(that.m_imp))
+ {
+ }
+
+ directory_iterator& operator=(directory_iterator&& that) noexcept
+ {
+ m_imp = static_cast< boost::intrusive_ptr< detail::dir_itr_imp >&& >(that.m_imp);
+ return *this;
+ }
+
+ directory_iterator& increment(system::error_code& ec) noexcept
+ {
+ detail::directory_iterator_increment(*this, &ec);
+ return *this;
+ }
+
+private:
+ boost::iterator_facade<
+ directory_iterator,
+ directory_entry,
+ boost::single_pass_traversal_tag
+ >::reference dereference() const
+ {
+ BOOST_ASSERT_MSG(!is_end(), "attempt to dereference end directory iterator");
+ return m_imp->dir_entry;
+ }
+
+ void increment() { detail::directory_iterator_increment(*this, nullptr); }
+
+ bool equal(directory_iterator const& rhs) const noexcept
+ {
+ return m_imp == rhs.m_imp || (is_end() && rhs.is_end());
+ }
+
+ bool is_end() const noexcept
+ {
+ // Note: The check for handle is needed because the iterator can be copied and the copy
+ // can be incremented to end while the original iterator still refers to the same dir_itr_imp.
+ return !m_imp || !m_imp->handle;
+ }
+
+private:
+ // intrusive_ptr provides the shallow-copy semantics required for single pass iterators
+ // (i.e. InputIterators). The end iterator is indicated by is_end().
+ boost::intrusive_ptr< detail::dir_itr_imp > m_imp;
+};
+
+// enable directory_iterator C++11 range-based for statement use --------------------//
+
+// begin() and end() are only used by a range-based for statement in the context of
+// auto - thus the top-level const is stripped - so returning const is harmless and
+// emphasizes begin() is just a pass through.
+inline directory_iterator const& begin(directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline directory_iterator end(directory_iterator const&) noexcept
+{
+ return directory_iterator();
+}
+
+// enable C++14 generic accessors for range const iterators
+inline directory_iterator const& cbegin(directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline directory_iterator cend(directory_iterator const&) noexcept
+{
+ return directory_iterator();
+}
+
+// enable directory_iterator BOOST_FOREACH -----------------------------------------//
+
+inline directory_iterator& range_begin(directory_iterator& iter) noexcept
+{
+ return iter;
+}
+
+inline directory_iterator range_begin(directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline directory_iterator range_end(directory_iterator&) noexcept
+{
+ return directory_iterator();
+}
+
+inline directory_iterator range_end(directory_iterator const&) noexcept
+{
+ return directory_iterator();
+}
+
+} // namespace filesystem
+
+// namespace boost template specializations
+template< typename C, typename Enabler >
+struct range_mutable_iterator;
+
+template<>
+struct range_mutable_iterator< boost::filesystem::directory_iterator, void >
+{
+ typedef boost::filesystem::directory_iterator type;
+};
+
+template< typename C, typename Enabler >
+struct range_const_iterator;
+
+template<>
+struct range_const_iterator< boost::filesystem::directory_iterator, void >
+{
+ typedef boost::filesystem::directory_iterator type;
+};
+
+namespace filesystem {
+
+//--------------------------------------------------------------------------------------//
+// //
+// recursive_directory_iterator helpers //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace detail {
+
+struct recur_dir_itr_imp :
+ public boost::intrusive_ref_counter< recur_dir_itr_imp >
+{
+ typedef directory_iterator element_type;
+ std::vector< element_type > m_stack;
+ directory_options m_options;
+
+ explicit recur_dir_itr_imp(directory_options opts) noexcept : m_options(opts) {}
+};
+
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// recursive_directory_iterator //
+// //
+//--------------------------------------------------------------------------------------//
+
+class recursive_directory_iterator :
+ public boost::iterator_facade<
+ recursive_directory_iterator,
+ directory_entry,
+ boost::single_pass_traversal_tag
+ >
+{
+ friend class boost::iterator_core_access;
+
+ friend BOOST_FILESYSTEM_DECL void detail::recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, directory_options opts, system::error_code* ec);
+ friend BOOST_FILESYSTEM_DECL void detail::recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec);
+ friend BOOST_FILESYSTEM_DECL void detail::recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec);
+
+public:
+ recursive_directory_iterator() noexcept {} // creates the "end" iterator
+
+ explicit recursive_directory_iterator(path const& dir_path)
+ {
+ detail::recursive_directory_iterator_construct(*this, dir_path, directory_options::none, nullptr);
+ }
+
+ recursive_directory_iterator(path const& dir_path, system::error_code& ec)
+ {
+ detail::recursive_directory_iterator_construct(*this, dir_path, directory_options::none, &ec);
+ }
+
+ recursive_directory_iterator(path const& dir_path, directory_options opts)
+ {
+ detail::recursive_directory_iterator_construct(*this, dir_path, opts, nullptr);
+ }
+
+ recursive_directory_iterator(path const& dir_path, directory_options opts, system::error_code& ec)
+ {
+ detail::recursive_directory_iterator_construct(*this, dir_path, opts, &ec);
+ }
+
+ recursive_directory_iterator(recursive_directory_iterator const&) = default;
+ recursive_directory_iterator& operator=(recursive_directory_iterator const&) = default;
+
+ recursive_directory_iterator(recursive_directory_iterator&& that) noexcept :
+ m_imp(static_cast< boost::intrusive_ptr< detail::recur_dir_itr_imp >&& >(that.m_imp))
+ {
+ }
+
+ recursive_directory_iterator& operator=(recursive_directory_iterator&& that) noexcept
+ {
+ m_imp = static_cast< boost::intrusive_ptr< detail::recur_dir_itr_imp >&& >(that.m_imp);
+ return *this;
+ }
+
+ recursive_directory_iterator& increment(system::error_code& ec) noexcept
+ {
+ detail::recursive_directory_iterator_increment(*this, &ec);
+ return *this;
+ }
+
+ int depth() const noexcept
+ {
+ BOOST_ASSERT_MSG(!is_end(), "depth() on end recursive_directory_iterator");
+ return static_cast< int >(m_imp->m_stack.size() - 1u);
+ }
+
+ bool recursion_pending() const noexcept
+ {
+ BOOST_ASSERT_MSG(!is_end(), "recursion_pending() on end recursive_directory_iterator");
+ return (m_imp->m_options & directory_options::_detail_no_push) == directory_options::none;
+ }
+
+ void pop()
+ {
+ detail::recursive_directory_iterator_pop(*this, nullptr);
+ }
+
+ void pop(system::error_code& ec) noexcept
+ {
+ detail::recursive_directory_iterator_pop(*this, &ec);
+ }
+
+ void disable_recursion_pending(bool value = true) noexcept
+ {
+ BOOST_ASSERT_MSG(!is_end(), "disable_recursion_pending() on end recursive_directory_iterator");
+ if (value)
+ m_imp->m_options |= directory_options::_detail_no_push;
+ else
+ m_imp->m_options &= ~directory_options::_detail_no_push;
+ }
+
+ file_status status() const
+ {
+ BOOST_ASSERT_MSG(!is_end(), "status() on end recursive_directory_iterator");
+ return m_imp->m_stack.back()->status();
+ }
+
+ file_status symlink_status() const
+ {
+ BOOST_ASSERT_MSG(!is_end(), "symlink_status() on end recursive_directory_iterator");
+ return m_imp->m_stack.back()->symlink_status();
+ }
+
+private:
+ boost::iterator_facade<
+ recursive_directory_iterator,
+ directory_entry,
+ boost::single_pass_traversal_tag
+ >::reference dereference() const
+ {
+ BOOST_ASSERT_MSG(!is_end(), "dereference of end recursive_directory_iterator");
+ return *m_imp->m_stack.back();
+ }
+
+ void increment() { detail::recursive_directory_iterator_increment(*this, nullptr); }
+
+ bool equal(recursive_directory_iterator const& rhs) const noexcept
+ {
+ return m_imp == rhs.m_imp || (is_end() && rhs.is_end());
+ }
+
+ bool is_end() const noexcept
+ {
+ // Note: The check for m_stack.empty() is needed because the iterator can be copied and the copy
+ // can be incremented to end while the original iterator still refers to the same recur_dir_itr_imp.
+ return !m_imp || m_imp->m_stack.empty();
+ }
+
+private:
+ // intrusive_ptr provides the shallow-copy semantics required for single pass iterators
+ // (i.e. InputIterators). The end iterator is indicated by is_end().
+ boost::intrusive_ptr< detail::recur_dir_itr_imp > m_imp;
+};
+
+// enable recursive directory iterator C++11 range-base for statement use ----------//
+
+// begin() and end() are only used by a range-based for statement in the context of
+// auto - thus the top-level const is stripped - so returning const is harmless and
+// emphasizes begin() is just a pass through.
+inline recursive_directory_iterator const& begin(recursive_directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline recursive_directory_iterator end(recursive_directory_iterator const&) noexcept
+{
+ return recursive_directory_iterator();
+}
+
+// enable C++14 generic accessors for range const iterators
+inline recursive_directory_iterator const& cbegin(recursive_directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline recursive_directory_iterator cend(recursive_directory_iterator const&) noexcept
+{
+ return recursive_directory_iterator();
+}
+
+// enable recursive directory iterator BOOST_FOREACH -------------------------------//
+
+inline recursive_directory_iterator& range_begin(recursive_directory_iterator& iter) noexcept
+{
+ return iter;
+}
+
+inline recursive_directory_iterator range_begin(recursive_directory_iterator const& iter) noexcept
+{
+ return iter;
+}
+
+inline recursive_directory_iterator range_end(recursive_directory_iterator&) noexcept
+{
+ return recursive_directory_iterator();
+}
+
+inline recursive_directory_iterator range_end(recursive_directory_iterator const&) noexcept
+{
+ return recursive_directory_iterator();
+}
+
+} // namespace filesystem
+
+// namespace boost template specializations
+template<>
+struct range_mutable_iterator< boost::filesystem::recursive_directory_iterator, void >
+{
+ typedef boost::filesystem::recursive_directory_iterator type;
+};
+
+template<>
+struct range_const_iterator< boost::filesystem::recursive_directory_iterator, void >
+{
+ typedef boost::filesystem::recursive_directory_iterator type;
+};
+
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_DIRECTORY_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/exception.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/exception.hpp
new file mode 100644
index 0000000000..033f520201
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/exception.hpp
@@ -0,0 +1,92 @@
+// boost/filesystem/exception.hpp -----------------------------------------------------//
+
+// Copyright Beman Dawes 2003
+// Copyright Andrey Semashev 2019
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+#ifndef BOOST_FILESYSTEM_EXCEPTION_HPP
+#define BOOST_FILESYSTEM_EXCEPTION_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include <string>
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+#include <boost/smart_ptr/intrusive_ref_counter.hpp>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+//--------------------------------------------------------------------------------------//
+// //
+// class filesystem_error //
+// //
+//--------------------------------------------------------------------------------------//
+
+class BOOST_SYMBOL_VISIBLE filesystem_error :
+ public system::system_error
+{
+ // see http://www.boost.org/more/error_handling.html for design rationale
+
+public:
+ BOOST_FILESYSTEM_DECL filesystem_error(const char* what_arg, system::error_code ec);
+ BOOST_FILESYSTEM_DECL filesystem_error(std::string const& what_arg, system::error_code ec);
+ BOOST_FILESYSTEM_DECL filesystem_error(const char* what_arg, path const& path1_arg, system::error_code ec);
+ BOOST_FILESYSTEM_DECL filesystem_error(std::string const& what_arg, path const& path1_arg, system::error_code ec);
+ BOOST_FILESYSTEM_DECL filesystem_error(const char* what_arg, path const& path1_arg, path const& path2_arg, system::error_code ec);
+ BOOST_FILESYSTEM_DECL filesystem_error(std::string const& what_arg, path const& path1_arg, path const& path2_arg, system::error_code ec);
+
+ BOOST_FILESYSTEM_DECL filesystem_error(filesystem_error const& that);
+ BOOST_FILESYSTEM_DECL filesystem_error& operator=(filesystem_error const& that);
+
+ BOOST_FILESYSTEM_DECL ~filesystem_error() noexcept;
+
+ path const& path1() const noexcept
+ {
+ return m_imp_ptr.get() ? m_imp_ptr->m_path1 : get_empty_path();
+ }
+ path const& path2() const noexcept
+ {
+ return m_imp_ptr.get() ? m_imp_ptr->m_path2 : get_empty_path();
+ }
+
+ BOOST_FILESYSTEM_DECL const char* what() const noexcept override;
+
+private:
+ BOOST_FILESYSTEM_DECL static path const& get_empty_path() noexcept;
+
+private:
+ struct impl :
+ public boost::intrusive_ref_counter< impl >
+ {
+ path m_path1; // may be empty()
+ path m_path2; // may be empty()
+ std::string m_what; // not built until needed
+
+ impl() = default;
+ explicit impl(path const& path1) :
+ m_path1(path1)
+ {
+ }
+ impl(path const& path1, path const& path2) :
+ m_path1(path1), m_path2(path2)
+ {
+ }
+ };
+ boost::intrusive_ptr< impl > m_imp_ptr;
+};
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_EXCEPTION_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/file_status.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/file_status.hpp
new file mode 100644
index 0000000000..c435c7775e
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/file_status.hpp
@@ -0,0 +1,252 @@
+// boost/filesystem/file_status.hpp --------------------------------------------------//
+
+// Copyright Beman Dawes 2002-2009
+// Copyright Jan Langer 2002
+// Copyright Dietmar Kuehl 2001
+// Copyright Vladimir Prus 2002
+// Copyright Andrey Semashev 2019
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_FILE_STATUS_HPP
+#define BOOST_FILESYSTEM_FILE_STATUS_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/detail/bitmask.hpp>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+
+//--------------------------------------------------------------------------------------//
+// file_type //
+//--------------------------------------------------------------------------------------//
+
+enum file_type
+{
+ status_error,
+ file_not_found,
+ regular_file,
+ directory_file,
+ // the following may not apply to some operating systems or file systems
+ symlink_file,
+ block_file,
+ character_file,
+ fifo_file,
+ socket_file,
+ reparse_file, // Windows: FILE_ATTRIBUTE_REPARSE_POINT that is not a symlink
+ type_unknown // file does exist, but isn't one of the above types or
+ // we don't have strong enough permission to find its type
+};
+
+//--------------------------------------------------------------------------------------//
+// perms //
+//--------------------------------------------------------------------------------------//
+
+enum perms
+{
+ no_perms = 0, // file_not_found is no_perms rather than perms_not_known
+
+ // POSIX equivalent macros given in comments.
+ // Values are from POSIX and are given in octal per the POSIX standard.
+
+ // permission bits
+
+ owner_read = 0400, // S_IRUSR, Read permission, owner
+ owner_write = 0200, // S_IWUSR, Write permission, owner
+ owner_exe = 0100, // S_IXUSR, Execute/search permission, owner
+ owner_all = 0700, // S_IRWXU, Read, write, execute/search by owner
+
+ group_read = 040, // S_IRGRP, Read permission, group
+ group_write = 020, // S_IWGRP, Write permission, group
+ group_exe = 010, // S_IXGRP, Execute/search permission, group
+ group_all = 070, // S_IRWXG, Read, write, execute/search by group
+
+ others_read = 04, // S_IROTH, Read permission, others
+ others_write = 02, // S_IWOTH, Write permission, others
+ others_exe = 01, // S_IXOTH, Execute/search permission, others
+ others_all = 07, // S_IRWXO, Read, write, execute/search by others
+
+ all_all = 0777, // owner_all|group_all|others_all
+
+ // other POSIX bits
+
+ set_uid_on_exe = 04000, // S_ISUID, Set-user-ID on execution
+ set_gid_on_exe = 02000, // S_ISGID, Set-group-ID on execution
+ sticky_bit = 01000, // S_ISVTX,
+ // (POSIX XSI) On directories, restricted deletion flag
+ // (V7) 'sticky bit': save swapped text even after use
+ // (SunOS) On non-directories: don't cache this file
+ // (SVID-v4.2) On directories: restricted deletion flag
+ // Also see http://en.wikipedia.org/wiki/Sticky_bit
+
+ perms_mask = 07777, // all_all|set_uid_on_exe|set_gid_on_exe|sticky_bit
+
+ perms_not_known = 0xFFFF, // present when directory_entry cache not loaded
+
+ // options for permissions() function
+
+ add_perms = 0x1000, // adds the given permission bits to the current bits
+ remove_perms = 0x2000, // removes the given permission bits from the current bits;
+ // choose add_perms or remove_perms, not both; if neither add_perms
+ // nor remove_perms is given, replace the current bits with
+ // the given bits.
+
+ symlink_perms = 0x4000, // on POSIX, don't resolve symlinks; implied on Windows
+
+ // BOOST_BITMASK op~ casts to int32_least_t, producing invalid enum values
+ _detail_extend_perms_32_1 = 0x7fffffff,
+ _detail_extend_perms_32_2 = -0x7fffffff - 1
+};
+
+BOOST_BITMASK(perms)
+
+//--------------------------------------------------------------------------------------//
+// file_status //
+//--------------------------------------------------------------------------------------//
+
+class file_status
+{
+public:
+ BOOST_CONSTEXPR file_status() noexcept :
+ m_value(status_error),
+ m_perms(perms_not_known)
+ {
+ }
+ explicit BOOST_CONSTEXPR file_status(file_type v) noexcept :
+ m_value(v),
+ m_perms(perms_not_known)
+ {
+ }
+ BOOST_CONSTEXPR file_status(file_type v, perms prms) noexcept :
+ m_value(v),
+ m_perms(prms)
+ {
+ }
+
+ BOOST_CONSTEXPR file_status(file_status const& rhs) noexcept :
+ m_value(rhs.m_value),
+ m_perms(rhs.m_perms)
+ {
+ }
+ BOOST_CXX14_CONSTEXPR file_status& operator=(file_status const& rhs) noexcept
+ {
+ m_value = rhs.m_value;
+ m_perms = rhs.m_perms;
+ return *this;
+ }
+
+ // Note: std::move is not constexpr in C++11, that's why we're not using it here
+ BOOST_CONSTEXPR file_status(file_status&& rhs) noexcept :
+ m_value(static_cast< file_type&& >(rhs.m_value)),
+ m_perms(static_cast< perms&& >(rhs.m_perms))
+ {
+ }
+ BOOST_CXX14_CONSTEXPR file_status& operator=(file_status&& rhs) noexcept
+ {
+ m_value = static_cast< file_type&& >(rhs.m_value);
+ m_perms = static_cast< perms&& >(rhs.m_perms);
+ return *this;
+ }
+
+ // observers
+ BOOST_CONSTEXPR file_type type() const noexcept { return m_value; }
+ BOOST_CONSTEXPR perms permissions() const noexcept { return m_perms; }
+
+ // modifiers
+ BOOST_CXX14_CONSTEXPR void type(file_type v) noexcept { m_value = v; }
+ BOOST_CXX14_CONSTEXPR void permissions(perms prms) noexcept { m_perms = prms; }
+
+ BOOST_CONSTEXPR bool operator==(file_status const& rhs) const noexcept
+ {
+ return type() == rhs.type() && permissions() == rhs.permissions();
+ }
+ BOOST_CONSTEXPR bool operator!=(file_status const& rhs) const noexcept
+ {
+ return !(*this == rhs);
+ }
+
+private:
+ file_type m_value;
+ perms m_perms;
+};
+
+inline BOOST_CONSTEXPR bool type_present(file_status f) noexcept
+{
+ return f.type() != filesystem::status_error;
+}
+
+inline BOOST_CONSTEXPR bool permissions_present(file_status f) noexcept
+{
+ return f.permissions() != filesystem::perms_not_known;
+}
+
+inline BOOST_CONSTEXPR bool status_known(file_status f) noexcept
+{
+ return filesystem::type_present(f) && filesystem::permissions_present(f);
+}
+
+inline BOOST_CONSTEXPR bool exists(file_status f) noexcept
+{
+ return f.type() != filesystem::status_error && f.type() != filesystem::file_not_found;
+}
+
+inline BOOST_CONSTEXPR bool is_regular_file(file_status f) noexcept
+{
+ return f.type() == filesystem::regular_file;
+}
+
+inline BOOST_CONSTEXPR bool is_directory(file_status f) noexcept
+{
+ return f.type() == filesystem::directory_file;
+}
+
+inline BOOST_CONSTEXPR bool is_symlink(file_status f) noexcept
+{
+ return f.type() == filesystem::symlink_file;
+}
+
+inline BOOST_CONSTEXPR bool is_block_file(file_status f) noexcept
+{
+ return f.type() == filesystem::block_file;
+}
+
+inline BOOST_CONSTEXPR bool is_character_file(file_status f) noexcept
+{
+ return f.type() == filesystem::character_file;
+}
+
+inline BOOST_CONSTEXPR bool is_fifo(file_status f) noexcept
+{
+ return f.type() == filesystem::fifo_file;
+}
+
+inline BOOST_CONSTEXPR bool is_socket(file_status f) noexcept
+{
+ return f.type() == filesystem::socket_file;
+}
+
+inline BOOST_CONSTEXPR bool is_reparse_file(file_status f) noexcept
+{
+ return f.type() == filesystem::reparse_file;
+}
+
+inline BOOST_CONSTEXPR bool is_other(file_status f) noexcept
+{
+ return filesystem::exists(f) && !filesystem::is_regular_file(f) && !filesystem::is_directory(f) && !filesystem::is_symlink(f);
+}
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp> // pops abi_prefix.hpp pragmas
+
+#endif // BOOST_FILESYSTEM_FILE_STATUS_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/fstream.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/fstream.hpp
new file mode 100644
index 0000000000..452f76426c
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/fstream.hpp
@@ -0,0 +1,257 @@
+// boost/filesystem/fstream.hpp ------------------------------------------------------//
+
+// Copyright Beman Dawes 2002
+// Copyright Andrey Semashev 2021-2023
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_FSTREAM_HPP
+#define BOOST_FILESYSTEM_FSTREAM_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <iosfwd>
+#include <fstream>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+#if defined(BOOST_WINDOWS_API)
+// On Windows, except for standard libaries known to have wchar_t overloads for
+// file stream I/O, use path::string() to get a narrow character c_str()
+#if (defined(_CPPLIB_VER) && _CPPLIB_VER >= 405 && !defined(_STLPORT_VERSION)) || \
+ (defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 7000 && defined(_LIBCPP_HAS_OPEN_WITH_WCHAR))
+// Use wide characters directly
+// Note: We don't use C++17 std::filesystem::path as a means to pass wide paths
+// to file streams because of various problems:
+// - std::filesystem is available in gcc 8 but it is broken there (fails to compile path definition
+// on Windows). Compilation errors seem to be fixed since gcc 9.
+// - In gcc 10.2 and clang 8.0.1 on Cygwin64, the path attempts to convert the wide string to narrow
+// and fails in runtime. This may be system locale dependent, and performing character code conversion
+// is against the purpose of using std::filesystem::path anyway.
+// - Other std::filesystem implementations were not tested, so it is not known if they actually work
+// with wide paths.
+#define BOOST_FILESYSTEM_C_STR(p) p.c_str()
+#else
+// Use narrow characters, since wide not available
+#define BOOST_FILESYSTEM_C_STR(p) p.string().c_str()
+#endif
+#endif // defined(BOOST_WINDOWS_API)
+
+#if !defined(BOOST_FILESYSTEM_C_STR)
+#define BOOST_FILESYSTEM_C_STR(p) p.c_str()
+#endif
+
+#if defined(BOOST_MSVC)
+#pragma warning(push)
+// 'boost::filesystem::basic_fstream<Char>' : inherits 'std::basic_istream<_Elem,_Traits>::std::basic_istream<_Elem,_Traits>::_Add_vtordisp1' via dominance
+#pragma warning(disable : 4250)
+#endif
+
+namespace boost {
+namespace filesystem {
+
+//--------------------------------------------------------------------------------------//
+// basic_filebuf //
+//--------------------------------------------------------------------------------------//
+
+template< class Char, class Traits = std::char_traits< Char > >
+class basic_filebuf :
+ public std::basic_filebuf< Char, Traits >
+{
+private:
+ typedef std::basic_filebuf< Char, Traits > base_type;
+
+public:
+ basic_filebuf() = default;
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS)
+ basic_filebuf(basic_filebuf&&) = default;
+ basic_filebuf& operator= (basic_filebuf&&) = default;
+#endif // !defined(BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS)
+
+ basic_filebuf(basic_filebuf const&) = delete;
+ basic_filebuf const& operator= (basic_filebuf const&) = delete;
+
+public:
+ basic_filebuf* open(path const& p, std::ios_base::openmode mode)
+ {
+ return base_type::open(BOOST_FILESYSTEM_C_STR(p), mode) ? this : nullptr;
+ }
+};
+
+//--------------------------------------------------------------------------------------//
+// basic_ifstream //
+//--------------------------------------------------------------------------------------//
+
+template< class Char, class Traits = std::char_traits< Char > >
+class basic_ifstream :
+ public std::basic_ifstream< Char, Traits >
+{
+private:
+ typedef std::basic_ifstream< Char, Traits > base_type;
+
+public:
+ basic_ifstream() = default;
+
+ // use two signatures, rather than one signature with default second
+ // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416)
+
+ explicit basic_ifstream(path const& p) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), std::ios_base::in) {}
+
+ basic_ifstream(path const& p, std::ios_base::openmode mode) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), mode) {}
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS)
+ basic_ifstream(basic_ifstream&& that) :
+ base_type(static_cast< base_type&& >(that)) {}
+
+ basic_ifstream& operator= (basic_ifstream&& that)
+ {
+ *static_cast< base_type* >(this) = static_cast< base_type&& >(that);
+ return *this;
+ }
+#endif
+
+ basic_ifstream(basic_ifstream const&) = delete;
+ basic_ifstream const& operator= (basic_ifstream const&) = delete;
+
+public:
+ void open(path const& p)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), std::ios_base::in);
+ }
+
+ void open(path const& p, std::ios_base::openmode mode)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), mode);
+ }
+};
+
+//--------------------------------------------------------------------------------------//
+// basic_ofstream //
+//--------------------------------------------------------------------------------------//
+
+template< class Char, class Traits = std::char_traits< Char > >
+class basic_ofstream :
+ public std::basic_ofstream< Char, Traits >
+{
+private:
+ typedef std::basic_ofstream< Char, Traits > base_type;
+
+public:
+ basic_ofstream() = default;
+
+ // use two signatures, rather than one signature with default second
+ // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416)
+
+ explicit basic_ofstream(path const& p) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), std::ios_base::out) {}
+
+ basic_ofstream(path const& p, std::ios_base::openmode mode) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), mode) {}
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS)
+ basic_ofstream(basic_ofstream&& that) :
+ base_type(static_cast< base_type&& >(that)) {}
+
+ basic_ofstream& operator= (basic_ofstream&& that)
+ {
+ *static_cast< base_type* >(this) = static_cast< base_type&& >(that);
+ return *this;
+ }
+#endif
+
+ basic_ofstream(basic_ofstream const&) = delete;
+ basic_ofstream const& operator= (basic_ofstream const&) = delete;
+
+public:
+ void open(path const& p)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), std::ios_base::out);
+ }
+
+ void open(path const& p, std::ios_base::openmode mode)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), mode);
+ }
+};
+
+//--------------------------------------------------------------------------------------//
+// basic_fstream //
+//--------------------------------------------------------------------------------------//
+
+template< class Char, class Traits = std::char_traits< Char > >
+class basic_fstream :
+ public std::basic_fstream< Char, Traits >
+{
+private:
+ typedef std::basic_fstream< Char, Traits > base_type;
+
+public:
+ basic_fstream() = default;
+
+ // use two signatures, rather than one signature with default second
+ // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416)
+
+ explicit basic_fstream(path const& p) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), std::ios_base::in | std::ios_base::out) {}
+
+ basic_fstream(path const& p, std::ios_base::openmode mode) :
+ base_type(BOOST_FILESYSTEM_C_STR(p), mode) {}
+
+#if !defined(BOOST_FILESYSTEM_DETAIL_NO_CXX11_MOVABLE_FSTREAMS)
+ basic_fstream(basic_fstream&& that) :
+ base_type(static_cast< base_type&& >(that)) {}
+
+ basic_fstream& operator= (basic_fstream&& that)
+ {
+ *static_cast< base_type* >(this) = static_cast< base_type&& >(that);
+ return *this;
+ }
+#endif
+
+ basic_fstream(basic_fstream const&) = delete;
+ basic_fstream const& operator= (basic_fstream const&) = delete;
+
+public:
+ void open(path const& p)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), std::ios_base::in | std::ios_base::out);
+ }
+
+ void open(path const& p, std::ios_base::openmode mode)
+ {
+ base_type::open(BOOST_FILESYSTEM_C_STR(p), mode);
+ }
+};
+
+//--------------------------------------------------------------------------------------//
+// typedefs //
+//--------------------------------------------------------------------------------------//
+
+typedef basic_filebuf< char > filebuf;
+typedef basic_ifstream< char > ifstream;
+typedef basic_ofstream< char > ofstream;
+typedef basic_fstream< char > fstream;
+
+typedef basic_filebuf< wchar_t > wfilebuf;
+typedef basic_ifstream< wchar_t > wifstream;
+typedef basic_ofstream< wchar_t > wofstream;
+typedef basic_fstream< wchar_t > wfstream;
+
+} // namespace filesystem
+} // namespace boost
+
+#if defined(BOOST_MSVC)
+#pragma warning(pop)
+#endif
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_FSTREAM_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/operations.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/operations.hpp
new file mode 100644
index 0000000000..5cd40a30fe
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/operations.hpp
@@ -0,0 +1,724 @@
+// boost/filesystem/operations.hpp ---------------------------------------------------//
+
+// Copyright Beman Dawes 2002-2009
+// Copyright Jan Langer 2002
+// Copyright Dietmar Kuehl 2001
+// Copyright Vladimir Prus 2002
+// Copyright Andrey Semashev 2020-2024
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_OPERATIONS_HPP
+#define BOOST_FILESYSTEM_OPERATIONS_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/file_status.hpp>
+
+#include <boost/detail/bitmask.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/cstdint.hpp>
+#include <ctime>
+#include <string>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+
+struct space_info
+{
+ // all values are byte counts
+ boost::uintmax_t capacity;
+ boost::uintmax_t free; // <= capacity
+ boost::uintmax_t available; // <= free
+};
+
+enum class copy_options : unsigned int
+{
+ none = 0u, // Default. For copy_file: error if the target file exists. For copy: do not recurse, follow symlinks, copy file contents.
+
+ // copy_file options:
+ skip_existing = 1u, // Don't overwrite the existing target file, don't report an error
+ overwrite_existing = 1u << 1u, // Overwrite existing file
+ update_existing = 1u << 2u, // Overwrite existing file if its last write time is older than the replacement file
+ synchronize_data = 1u << 3u, // Flush all buffered data written to the target file to permanent storage
+ synchronize = 1u << 4u, // Flush all buffered data and attributes written to the target file to permanent storage
+ ignore_attribute_errors = 1u << 5u, // Ignore errors of copying file attributes
+
+ // copy options:
+ recursive = 1u << 8u, // Recurse into sub-directories
+ copy_symlinks = 1u << 9u, // Copy symlinks as symlinks instead of copying the referenced file
+ skip_symlinks = 1u << 10u, // Don't copy symlinks
+ directories_only = 1u << 11u, // Only copy directory structure, do not copy non-directory files
+ create_symlinks = 1u << 12u, // Create symlinks instead of copying files
+ create_hard_links = 1u << 13u, // Create hard links instead of copying files
+ _detail_recursing = 1u << 14u // Internal use only, do not use
+};
+
+BOOST_BITMASK(copy_options)
+
+//--------------------------------------------------------------------------------------//
+// implementation details //
+//--------------------------------------------------------------------------------------//
+
+namespace detail {
+
+BOOST_FILESYSTEM_DECL
+path absolute_v3(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path absolute_v4(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+file_status status(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+file_status symlink_status(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool is_empty(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path initial_path(system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path canonical_v3(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path canonical_v4(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void copy(path const& from, path const& to, copy_options options, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool copy_file(path const& from, path const& to, copy_options options, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void copy_symlink(path const& existing_symlink, path const& new_symlink, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool create_directories(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool create_directory(path const& p, const path* existing, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void create_directory_symlink(path const& to, path const& from, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void create_hard_link(path const& to, path const& from, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void create_symlink(path const& to, path const& from, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path current_path(system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void current_path(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool equivalent_v3(path const& p1, path const& p2, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool equivalent_v4(path const& p1, path const& p2, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+boost::uintmax_t file_size(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+boost::uintmax_t hard_link_count(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+std::time_t creation_time(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+std::time_t last_write_time(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void last_write_time(path const& p, const std::time_t new_time, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void permissions(path const& p, perms prms, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path read_symlink(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path relative(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+bool remove(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+boost::uintmax_t remove_all(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void rename(path const& old_p, path const& new_p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+void resize_file(path const& p, uintmax_t size, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+space_info space(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path system_complete(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path temp_directory_path(system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path unique_path(path const& p, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec = nullptr);
+BOOST_FILESYSTEM_DECL
+path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec = nullptr);
+
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// status query functions //
+// //
+//--------------------------------------------------------------------------------------//
+
+inline file_status status(path const& p)
+{
+ return detail::status(p);
+}
+
+inline file_status status(path const& p, system::error_code& ec) noexcept
+{
+ return detail::status(p, &ec);
+}
+
+inline file_status symlink_status(path const& p)
+{
+ return detail::symlink_status(p);
+}
+
+inline file_status symlink_status(path const& p, system::error_code& ec) noexcept
+{
+ return detail::symlink_status(p, &ec);
+}
+
+inline bool exists(path const& p)
+{
+ return filesystem::exists(detail::status(p));
+}
+
+inline bool exists(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::exists(detail::status(p, &ec));
+}
+
+inline bool is_regular_file(path const& p)
+{
+ return filesystem::is_regular_file(detail::status(p));
+}
+
+inline bool is_regular_file(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_regular_file(detail::status(p, &ec));
+}
+
+inline bool is_directory(path const& p)
+{
+ return filesystem::is_directory(detail::status(p));
+}
+
+inline bool is_directory(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_directory(detail::status(p, &ec));
+}
+
+inline bool is_symlink(path const& p)
+{
+ return filesystem::is_symlink(detail::symlink_status(p));
+}
+
+inline bool is_symlink(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_symlink(detail::symlink_status(p, &ec));
+}
+
+inline bool is_block_file(path const& p)
+{
+ return filesystem::is_block_file(detail::status(p));
+}
+
+inline bool is_block_file(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_block_file(detail::status(p, &ec));
+}
+
+inline bool is_character_file(path const& p)
+{
+ return filesystem::is_character_file(detail::status(p));
+}
+
+inline bool is_character_file(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_character_file(detail::status(p, &ec));
+}
+
+inline bool is_fifo(path const& p)
+{
+ return filesystem::is_fifo(detail::status(p));
+}
+
+inline bool is_fifo(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_fifo(detail::status(p, &ec));
+}
+
+inline bool is_socket(path const& p)
+{
+ return filesystem::is_socket(detail::status(p));
+}
+
+inline bool is_socket(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_socket(detail::status(p, &ec));
+}
+
+inline bool is_reparse_file(path const& p)
+{
+ return filesystem::is_reparse_file(detail::symlink_status(p));
+}
+
+inline bool is_reparse_file(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_reparse_file(detail::symlink_status(p, &ec));
+}
+
+inline bool is_other(path const& p)
+{
+ return filesystem::is_other(detail::status(p));
+}
+
+inline bool is_other(path const& p, system::error_code& ec) noexcept
+{
+ return filesystem::is_other(detail::status(p, &ec));
+}
+
+inline bool is_empty(path const& p)
+{
+ return detail::is_empty(p);
+}
+
+inline bool is_empty(path const& p, system::error_code& ec)
+{
+ return detail::is_empty(p, &ec);
+}
+
+//--------------------------------------------------------------------------------------//
+// //
+// operational functions //
+// //
+//--------------------------------------------------------------------------------------//
+
+inline path initial_path()
+{
+ return detail::initial_path();
+}
+
+inline path initial_path(system::error_code& ec)
+{
+ return detail::initial_path(&ec);
+}
+
+template< class Path >
+path initial_path()
+{
+ return initial_path();
+}
+template< class Path >
+path initial_path(system::error_code& ec)
+{
+ return detail::initial_path(&ec);
+}
+
+inline path current_path()
+{
+ return detail::current_path();
+}
+
+inline path current_path(system::error_code& ec)
+{
+ return detail::current_path(&ec);
+}
+
+inline void current_path(path const& p)
+{
+ detail::current_path(p);
+}
+
+inline void current_path(path const& p, system::error_code& ec) noexcept
+{
+ detail::current_path(p, &ec);
+}
+
+inline void copy(path const& from, path const& to)
+{
+ detail::copy(from, to, copy_options::none);
+}
+
+inline void copy(path const& from, path const& to, system::error_code& ec) noexcept
+{
+ detail::copy(from, to, copy_options::none, &ec);
+}
+
+inline void copy(path const& from, path const& to, copy_options options)
+{
+ detail::copy(from, to, options);
+}
+
+inline void copy(path const& from, path const& to, copy_options options, system::error_code& ec) noexcept
+{
+ detail::copy(from, to, options, &ec);
+}
+
+inline bool copy_file(path const& from, path const& to)
+{
+ return detail::copy_file(from, to, copy_options::none);
+}
+
+inline bool copy_file(path const& from, path const& to, system::error_code& ec) noexcept
+{
+ return detail::copy_file(from, to, copy_options::none, &ec);
+}
+
+inline bool copy_file(path const& from, path const& to, copy_options options)
+{
+ return detail::copy_file(from, to, options);
+}
+
+inline bool copy_file(path const& from, path const& to, copy_options options, system::error_code& ec) noexcept
+{
+ return detail::copy_file(from, to, options, &ec);
+}
+
+inline void copy_symlink(path const& existing_symlink, path const& new_symlink)
+{
+ detail::copy_symlink(existing_symlink, new_symlink);
+}
+
+inline void copy_symlink(path const& existing_symlink, path const& new_symlink, system::error_code& ec) noexcept
+{
+ detail::copy_symlink(existing_symlink, new_symlink, &ec);
+}
+
+inline bool create_directories(path const& p)
+{
+ return detail::create_directories(p);
+}
+
+inline bool create_directories(path const& p, system::error_code& ec) noexcept
+{
+ return detail::create_directories(p, &ec);
+}
+
+inline bool create_directory(path const& p)
+{
+ return detail::create_directory(p, nullptr);
+}
+
+inline bool create_directory(path const& p, system::error_code& ec) noexcept
+{
+ return detail::create_directory(p, nullptr, &ec);
+}
+
+inline bool create_directory(path const& p, path const& existing)
+{
+ return detail::create_directory(p, &existing);
+}
+
+inline bool create_directory(path const& p, path const& existing, system::error_code& ec) noexcept
+{
+ return detail::create_directory(p, &existing, &ec);
+}
+
+inline void create_directory_symlink(path const& to, path const& from)
+{
+ detail::create_directory_symlink(to, from);
+}
+
+inline void create_directory_symlink(path const& to, path const& from, system::error_code& ec) noexcept
+{
+ detail::create_directory_symlink(to, from, &ec);
+}
+
+inline void create_hard_link(path const& to, path const& new_hard_link)
+{
+ detail::create_hard_link(to, new_hard_link);
+}
+
+inline void create_hard_link(path const& to, path const& new_hard_link, system::error_code& ec) noexcept
+{
+ detail::create_hard_link(to, new_hard_link, &ec);
+}
+
+inline void create_symlink(path const& to, path const& new_symlink)
+{
+ detail::create_symlink(to, new_symlink);
+}
+
+inline void create_symlink(path const& to, path const& new_symlink, system::error_code& ec) noexcept
+{
+ detail::create_symlink(to, new_symlink, &ec);
+}
+
+inline boost::uintmax_t file_size(path const& p)
+{
+ return detail::file_size(p);
+}
+
+inline boost::uintmax_t file_size(path const& p, system::error_code& ec) noexcept
+{
+ return detail::file_size(p, &ec);
+}
+
+inline boost::uintmax_t hard_link_count(path const& p)
+{
+ return detail::hard_link_count(p);
+}
+
+inline boost::uintmax_t hard_link_count(path const& p, system::error_code& ec) noexcept
+{
+ return detail::hard_link_count(p, &ec);
+}
+
+inline std::time_t creation_time(path const& p)
+{
+ return detail::creation_time(p);
+}
+
+inline std::time_t creation_time(path const& p, system::error_code& ec) noexcept
+{
+ return detail::creation_time(p, &ec);
+}
+
+inline std::time_t last_write_time(path const& p)
+{
+ return detail::last_write_time(p);
+}
+
+inline std::time_t last_write_time(path const& p, system::error_code& ec) noexcept
+{
+ return detail::last_write_time(p, &ec);
+}
+
+inline void last_write_time(path const& p, const std::time_t new_time)
+{
+ detail::last_write_time(p, new_time);
+}
+
+inline void last_write_time(path const& p, const std::time_t new_time, system::error_code& ec) noexcept
+{
+ detail::last_write_time(p, new_time, &ec);
+}
+
+inline void permissions(path const& p, perms prms)
+{
+ detail::permissions(p, prms);
+}
+
+inline void permissions(path const& p, perms prms, system::error_code& ec) noexcept
+{
+ detail::permissions(p, prms, &ec);
+}
+
+inline path read_symlink(path const& p)
+{
+ return detail::read_symlink(p);
+}
+
+inline path read_symlink(path const& p, system::error_code& ec)
+{
+ return detail::read_symlink(p, &ec);
+}
+
+inline bool remove(path const& p)
+{
+ return detail::remove(p);
+}
+
+inline bool remove(path const& p, system::error_code& ec) noexcept
+{
+ return detail::remove(p, &ec);
+}
+
+inline boost::uintmax_t remove_all(path const& p)
+{
+ return detail::remove_all(p);
+}
+
+inline boost::uintmax_t remove_all(path const& p, system::error_code& ec) noexcept
+{
+ return detail::remove_all(p, &ec);
+}
+
+inline void rename(path const& old_p, path const& new_p)
+{
+ detail::rename(old_p, new_p);
+}
+
+inline void rename(path const& old_p, path const& new_p, system::error_code& ec) noexcept
+{
+ detail::rename(old_p, new_p, &ec);
+}
+
+// name suggested by Scott McMurray
+inline void resize_file(path const& p, uintmax_t size)
+{
+ detail::resize_file(p, size);
+}
+
+inline void resize_file(path const& p, uintmax_t size, system::error_code& ec) noexcept
+{
+ detail::resize_file(p, size, &ec);
+}
+
+inline path relative(path const& p, path const& base = current_path())
+{
+ return detail::relative(p, base);
+}
+
+inline path relative(path const& p, system::error_code& ec)
+{
+ path base = current_path(ec);
+ if (ec)
+ return path();
+ return detail::relative(p, base, &ec);
+}
+
+inline path relative(path const& p, path const& base, system::error_code& ec)
+{
+ return detail::relative(p, base, &ec);
+}
+
+inline space_info space(path const& p)
+{
+ return detail::space(p);
+}
+
+inline space_info space(path const& p, system::error_code& ec) noexcept
+{
+ return detail::space(p, &ec);
+}
+
+inline path system_complete(path const& p)
+{
+ return detail::system_complete(p);
+}
+
+inline path system_complete(path const& p, system::error_code& ec)
+{
+ return detail::system_complete(p, &ec);
+}
+
+inline path temp_directory_path()
+{
+ return detail::temp_directory_path();
+}
+
+inline path temp_directory_path(system::error_code& ec)
+{
+ return detail::temp_directory_path(&ec);
+}
+
+inline path unique_path(path const& p =
+#if defined(BOOST_WINDOWS_API)
+ L"%%%%-%%%%-%%%%-%%%%"
+#else
+ "%%%%-%%%%-%%%%-%%%%"
+#endif
+)
+{
+ return detail::unique_path(p);
+}
+
+inline path unique_path(system::error_code& ec)
+{
+ return detail::unique_path
+ (
+#if defined(BOOST_WINDOWS_API)
+ L"%%%%-%%%%-%%%%-%%%%",
+#else
+ "%%%%-%%%%-%%%%-%%%%",
+#endif
+ &ec
+ );
+}
+
+inline path unique_path(path const& p, system::error_code& ec)
+{
+ return detail::unique_path(p, &ec);
+}
+
+namespace BOOST_FILESYSTEM_VERSION_NAMESPACE {
+
+inline path absolute(path const& p, path const& base = current_path())
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::absolute)(p, base);
+}
+
+inline path absolute(path const& p, system::error_code& ec)
+{
+ path base = current_path(ec);
+ if (ec)
+ return path();
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::absolute)(p, base, &ec);
+}
+
+inline path absolute(path const& p, path const& base, system::error_code& ec)
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::absolute)(p, base, &ec);
+}
+
+inline path canonical(path const& p, path const& base = current_path())
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::canonical)(p, base);
+}
+
+inline path canonical(path const& p, system::error_code& ec)
+{
+ path base = current_path(ec);
+ if (ec)
+ return path();
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::canonical)(p, base, &ec);
+}
+
+inline path canonical(path const& p, path const& base, system::error_code& ec)
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::canonical)(p, base, &ec);
+}
+
+inline bool equivalent(path const& p1, path const& p2)
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::equivalent)(p1, p2);
+}
+
+inline bool equivalent(path const& p1, path const& p2, system::error_code& ec) noexcept
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::equivalent)(p1, p2, &ec);
+}
+
+inline path weakly_canonical(path const& p, path const& base = current_path())
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::weakly_canonical)(p, base);
+}
+
+inline path weakly_canonical(path const& p, system::error_code& ec)
+{
+ path base = current_path(ec);
+ if (ec)
+ return path();
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::weakly_canonical)(p, base, &ec);
+}
+
+inline path weakly_canonical(path const& p, path const& base, system::error_code& ec)
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::weakly_canonical)(p, base, &ec);
+}
+
+} // namespace BOOST_FILESYSTEM_VERSION_NAMESPACE
+
+using BOOST_FILESYSTEM_VERSION_NAMESPACE::absolute;
+using BOOST_FILESYSTEM_VERSION_NAMESPACE::canonical;
+using BOOST_FILESYSTEM_VERSION_NAMESPACE::equivalent;
+using BOOST_FILESYSTEM_VERSION_NAMESPACE::weakly_canonical;
+
+// test helper -----------------------------------------------------------------------//
+
+// Not part of the documented interface since false positives are possible;
+// there is no law that says that an OS that has large stat.st_size
+// actually supports large file sizes.
+
+namespace detail {
+
+BOOST_FILESYSTEM_DECL bool possible_large_file_size_support();
+
+} // namespace detail
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_OPERATIONS_HPP
diff --git a/contrib/restricted/boost/filesystem/include/boost/filesystem/path.hpp b/contrib/restricted/boost/filesystem/include/boost/filesystem/path.hpp
new file mode 100644
index 0000000000..f6e38f4b22
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/include/boost/filesystem/path.hpp
@@ -0,0 +1,1710 @@
+// filesystem path.hpp ---------------------------------------------------------------//
+
+// Copyright Vladimir Prus 2002
+// Copyright Beman Dawes 2002-2005, 2009
+// Copyright Andrey Semashev 2021-2024
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+// path::stem(), extension(), and replace_extension() are based on
+// basename(), extension(), and change_extension() from the original
+// filesystem/convenience.hpp header by Vladimir Prus.
+
+#ifndef BOOST_FILESYSTEM_PATH_HPP
+#define BOOST_FILESYSTEM_PATH_HPP
+
+#include <boost/filesystem/config.hpp>
+#include <cstddef>
+#include <iosfwd>
+#include <locale>
+#include <string>
+#include <iterator>
+#include <type_traits>
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+#include <string_view>
+#endif
+#include <boost/assert.hpp>
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/iterator/iterator_categories.hpp>
+#include <boost/io/quoted.hpp>
+#include <boost/functional/hash_fwd.hpp>
+#include <boost/filesystem/detail/path_traits.hpp>
+#include <boost/filesystem/detail/type_traits/negation.hpp>
+#include <boost/filesystem/detail/type_traits/conjunction.hpp>
+#include <boost/filesystem/detail/type_traits/disjunction.hpp>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+class path;
+
+namespace path_detail { // intentionally don't use filesystem::detail to not bring internal Boost.Filesystem functions into ADL via path_constants
+
+template< typename Char, Char Separator, Char PreferredSeparator, Char Dot >
+struct path_constants
+{
+ typedef path_constants< Char, Separator, PreferredSeparator, Dot > path_constants_base;
+ typedef Char value_type;
+ static BOOST_CONSTEXPR_OR_CONST value_type separator = Separator;
+ static BOOST_CONSTEXPR_OR_CONST value_type preferred_separator = PreferredSeparator;
+ static BOOST_CONSTEXPR_OR_CONST value_type dot = Dot;
+};
+
+#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
+template< typename Char, Char Separator, Char PreferredSeparator, Char Dot >
+BOOST_CONSTEXPR_OR_CONST typename path_constants< Char, Separator, PreferredSeparator, Dot >::value_type
+path_constants< Char, Separator, PreferredSeparator, Dot >::separator;
+template< typename Char, Char Separator, Char PreferredSeparator, Char Dot >
+BOOST_CONSTEXPR_OR_CONST typename path_constants< Char, Separator, PreferredSeparator, Dot >::value_type
+path_constants< Char, Separator, PreferredSeparator, Dot >::preferred_separator;
+template< typename Char, Char Separator, Char PreferredSeparator, Char Dot >
+BOOST_CONSTEXPR_OR_CONST typename path_constants< Char, Separator, PreferredSeparator, Dot >::value_type
+path_constants< Char, Separator, PreferredSeparator, Dot >::dot;
+#endif
+
+class path_iterator;
+class path_reverse_iterator;
+
+} // namespace path_detail
+
+namespace detail {
+
+struct path_algorithms
+{
+ // A struct that denotes a contiguous range of characters in a string. A lightweight alternative to string_view.
+ struct substring
+ {
+ std::size_t pos;
+ std::size_t size;
+ };
+
+ typedef path_traits::path_native_char_type value_type;
+ typedef std::basic_string< value_type > string_type;
+
+ static bool has_filename_v3(path const& p);
+ static bool has_filename_v4(path const& p);
+ BOOST_FILESYSTEM_DECL static path filename_v3(path const& p);
+ static path filename_v4(path const& p);
+
+ BOOST_FILESYSTEM_DECL static path stem_v3(path const& p);
+ BOOST_FILESYSTEM_DECL static path stem_v4(path const& p);
+ BOOST_FILESYSTEM_DECL static path extension_v3(path const& p);
+ static path extension_v4(path const& p);
+
+ BOOST_FILESYSTEM_DECL static void remove_filename_v3(path& p);
+ BOOST_FILESYSTEM_DECL static void remove_filename_v4(path& p);
+
+ BOOST_FILESYSTEM_DECL static void replace_extension_v3(path& p, path const& new_extension);
+ BOOST_FILESYSTEM_DECL static void replace_extension_v4(path& p, path const& new_extension);
+
+ BOOST_FILESYSTEM_DECL static path lexically_normal_v3(path const& p);
+ BOOST_FILESYSTEM_DECL static path lexically_normal_v4(path const& p);
+
+ BOOST_FILESYSTEM_DECL static path generic_path_v3(path const& p);
+ BOOST_FILESYSTEM_DECL static path generic_path_v4(path const& p);
+
+#if defined(BOOST_WINDOWS_API)
+ BOOST_FILESYSTEM_DECL static void make_preferred_v3(path& p);
+ BOOST_FILESYSTEM_DECL static void make_preferred_v4(path& p);
+#endif
+
+ BOOST_FILESYSTEM_DECL static int compare_v3(path const& left, path const& right);
+ BOOST_FILESYSTEM_DECL static int compare_v4(path const& left, path const& right);
+
+ BOOST_FILESYSTEM_DECL static void append_v3(path& p, const value_type* b, const value_type* e);
+ BOOST_FILESYSTEM_DECL static void append_v4(path& p, const value_type* b, const value_type* e);
+ static void append_v4(path& left, path const& right);
+
+ // Returns: If separator is to be appended, m_pathname.size() before append. Otherwise 0.
+ // Note: An append is never performed if size()==0, so a returned 0 is unambiguous.
+ BOOST_FILESYSTEM_DECL static string_type::size_type append_separator_if_needed(path& p);
+ BOOST_FILESYSTEM_DECL static void erase_redundant_separator(path& p, string_type::size_type sep_pos);
+
+ BOOST_FILESYSTEM_DECL static string_type::size_type find_root_name_size(path const& p);
+ BOOST_FILESYSTEM_DECL static string_type::size_type find_root_path_size(path const& p);
+ BOOST_FILESYSTEM_DECL static substring find_root_directory(path const& p);
+ BOOST_FILESYSTEM_DECL static substring find_relative_path(path const& p);
+ BOOST_FILESYSTEM_DECL static string_type::size_type find_parent_path_size(path const& p);
+ BOOST_FILESYSTEM_DECL static string_type::size_type find_filename_v4_size(path const& p);
+ BOOST_FILESYSTEM_DECL static string_type::size_type find_extension_v4_size(path const& p);
+
+ BOOST_FILESYSTEM_DECL static int lex_compare_v3
+ (
+ path_detail::path_iterator first1, path_detail::path_iterator const& last1,
+ path_detail::path_iterator first2, path_detail::path_iterator const& last2
+ );
+ BOOST_FILESYSTEM_DECL static int lex_compare_v4
+ (
+ path_detail::path_iterator first1, path_detail::path_iterator const& last1,
+ path_detail::path_iterator first2, path_detail::path_iterator const& last2
+ );
+
+ BOOST_FILESYSTEM_DECL static void increment_v3(path_detail::path_iterator& it);
+ BOOST_FILESYSTEM_DECL static void increment_v4(path_detail::path_iterator& it);
+ BOOST_FILESYSTEM_DECL static void decrement_v3(path_detail::path_iterator& it);
+ BOOST_FILESYSTEM_DECL static void decrement_v4(path_detail::path_iterator& it);
+};
+
+} // namespace detail
+
+//------------------------------------------------------------------------------------//
+// //
+// class path //
+// //
+//------------------------------------------------------------------------------------//
+
+class path :
+ public filesystem::path_detail::path_constants<
+#ifdef BOOST_WINDOWS_API
+ detail::path_traits::path_native_char_type, L'/', L'\\', L'.'
+#else
+ detail::path_traits::path_native_char_type, '/', '/', '.'
+#endif
+ >
+{
+ friend class path_detail::path_iterator;
+ friend class path_detail::path_reverse_iterator;
+ friend struct detail::path_algorithms;
+
+public:
+ // value_type is the character type used by the operating system API to
+ // represent paths.
+
+ typedef detail::path_algorithms::value_type value_type;
+ typedef detail::path_algorithms::string_type string_type;
+ typedef detail::path_traits::codecvt_type codecvt_type;
+
+ // ----- character encoding conversions -----
+
+ // Following the principle of least astonishment, path input arguments
+ // passed to or obtained from the operating system via objects of
+ // class path behave as if they were directly passed to or
+ // obtained from the O/S API, unless conversion is explicitly requested.
+ //
+ // POSIX specfies that path strings are passed unchanged to and from the
+ // API. Note that this is different from the POSIX command line utilities,
+ // which convert according to a locale.
+ //
+ // Thus for POSIX, char strings do not undergo conversion. wchar_t strings
+ // are converted to/from char using the path locale or, if a conversion
+ // argument is given, using a conversion object modeled on
+ // std::wstring_convert.
+ //
+ // The path locale, which is global to the thread, can be changed by the
+ // imbue() function. It is initialized to an implementation defined locale.
+ //
+ // For Windows, wchar_t strings do not undergo conversion. char strings
+ // are converted using the "ANSI" or "OEM" code pages, as determined by
+ // the AreFileApisANSI() function, or, if a conversion argument is given,
+ // using a conversion object modeled on std::wstring_convert.
+ //
+ // See m_pathname comments for further important rationale.
+
+ // TODO: rules needed for operating systems that use / or .
+ // differently, or format directory paths differently from file paths.
+ //
+ // **********************************************************************************
+ //
+ // More work needed: How to handle an operating system that may have
+ // slash characters or dot characters in valid filenames, either because
+ // it doesn't follow the POSIX standard, or because it allows MBCS
+ // filename encodings that may contain slash or dot characters. For
+ // example, ISO/IEC 2022 (JIS) encoding which allows switching to
+ // JIS x0208-1983 encoding. A valid filename in this set of encodings is
+ // 0x1B 0x24 0x42 [switch to X0208-1983] 0x24 0x2F [U+304F Kiragana letter KU]
+ // ^^^^
+ // Note that 0x2F is the ASCII slash character
+ //
+ // **********************************************************************************
+
+ // Supported source arguments: half-open iterator range, container, c-array,
+ // and single pointer to null terminated string.
+
+ // All source arguments except pointers to null terminated byte strings support
+ // multi-byte character strings which may have embedded nulls. Embedded null
+ // support is required for some Asian languages on Windows.
+
+ // "const codecvt_type& cvt=codecvt()" default arguments are not used because this
+ // limits the impact of locale("") initialization failures on POSIX systems to programs
+ // that actually depend on locale(""). It further ensures that exceptions thrown
+ // as a result of such failues occur after main() has started, so can be caught.
+
+private:
+ //! Assignment operation
+ class assign_op
+ {
+ private:
+ path& m_self;
+
+ public:
+ typedef void result_type;
+
+ explicit assign_op(path& self) noexcept : m_self(self) {}
+
+ result_type operator() (const value_type* source, const value_type* source_end, const codecvt_type* = nullptr) const
+ {
+ m_self.m_pathname.assign(source, source_end);
+ }
+
+ template< typename OtherChar >
+ result_type operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt = nullptr) const
+ {
+ m_self.m_pathname.clear();
+ detail::path_traits::convert(source, source_end, m_self.m_pathname, cvt);
+ }
+ };
+
+ //! Concatenation operation
+ class concat_op
+ {
+ private:
+ path& m_self;
+
+ public:
+ typedef void result_type;
+
+ explicit concat_op(path& self) noexcept : m_self(self) {}
+
+ result_type operator() (const value_type* source, const value_type* source_end, const codecvt_type* = nullptr) const
+ {
+ m_self.m_pathname.append(source, source_end);
+ }
+
+ template< typename OtherChar >
+ result_type operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt = nullptr) const
+ {
+ detail::path_traits::convert(source, source_end, m_self.m_pathname, cvt);
+ }
+ };
+
+ //! Path appending operation
+ class append_op
+ {
+ private:
+ path& m_self;
+
+ public:
+ typedef void result_type;
+
+ explicit append_op(path& self) noexcept : m_self(self) {}
+
+ BOOST_FORCEINLINE result_type operator() (const value_type* source, const value_type* source_end, const codecvt_type* = nullptr) const
+ {
+ m_self.append(source, source_end);
+ }
+
+ template< typename OtherChar >
+ BOOST_FORCEINLINE result_type operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt = nullptr) const
+ {
+ string_type src;
+ detail::path_traits::convert(source, source_end, src, cvt);
+ m_self.append(src.data(), src.data() + src.size());
+ }
+ };
+
+ //! Path comparison operation
+ class compare_op
+ {
+ private:
+ path const& m_self;
+
+ public:
+ typedef int result_type;
+
+ explicit compare_op(path const& self) noexcept : m_self(self) {}
+
+ result_type operator() (const value_type* source, const value_type* source_end, const codecvt_type* = nullptr) const;
+
+ template< typename OtherChar >
+ result_type operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt = nullptr) const;
+ };
+
+public:
+ typedef path_detail::path_iterator iterator;
+ typedef iterator const_iterator;
+ typedef path_detail::path_reverse_iterator reverse_iterator;
+ typedef reverse_iterator const_reverse_iterator;
+
+public:
+ // ----- constructors -----
+
+ path() noexcept {}
+ path(path const& p) : m_pathname(p.m_pathname) {}
+ path(path const& p, codecvt_type const&) : m_pathname(p.m_pathname) {}
+
+ path(const value_type* s) : m_pathname(s) {}
+ path(const value_type* s, codecvt_type const&) : m_pathname(s) {}
+ path(string_type const& s) : m_pathname(s) {}
+ path(string_type const& s, codecvt_type const&) : m_pathname(s) {}
+#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
+ path(std::basic_string_view< value_type > const& s) : m_pathname(s) {}
+ path(std::basic_string_view< value_type > const& s, codecvt_type const&) : m_pathname(s) {}
+#endif
+
+ template<
+ typename Source,
+ typename = typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_native_path_source< typename std::remove_cv< Source >::type > >
+ >::value
+ >::type
+ >
+ path(Source const& source)
+ {
+ assign(source);
+ }
+
+ template<
+ typename Source,
+ typename = typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_native_path_source< typename std::remove_cv< Source >::type > >
+ >::value
+ >::type
+ >
+ explicit path(Source const& source, codecvt_type const& cvt)
+ {
+ assign(source, cvt);
+ }
+
+ path(path&& p) noexcept : m_pathname(static_cast< string_type&& >(p.m_pathname))
+ {
+ }
+ path(path&& p, codecvt_type const&) noexcept : m_pathname(static_cast< string_type&& >(p.m_pathname))
+ {
+ }
+ path& operator=(path&& p) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p.m_pathname);
+ return *this;
+ }
+ path& assign(path&& p) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p.m_pathname);
+ return *this;
+ }
+ path& assign(path&& p, codecvt_type const&) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p.m_pathname);
+ return *this;
+ }
+
+ path(string_type&& s) noexcept : m_pathname(static_cast< string_type&& >(s))
+ {
+ }
+ path(string_type&& s, codecvt_type const&) noexcept : m_pathname(static_cast< string_type&& >(s))
+ {
+ }
+ path& operator=(string_type&& p) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p);
+ return *this;
+ }
+ path& assign(string_type&& p) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p);
+ return *this;
+ }
+ path& assign(string_type&& p, codecvt_type const&) noexcept
+ {
+ m_pathname = static_cast< string_type&& >(p);
+ return *this;
+ }
+
+ path(const value_type* begin, const value_type* end) : m_pathname(begin, end) {}
+ path(const value_type* begin, const value_type* end, codecvt_type const&) : m_pathname(begin, end) {}
+
+ template<
+ typename InputIterator,
+ typename = typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value
+ >::type
+ >
+ path(InputIterator begin, InputIterator end)
+ {
+ if (begin != end)
+ {
+ typedef std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source_t;
+ source_t source(begin, end);
+ assign(static_cast< source_t&& >(source));
+ }
+ }
+
+ template<
+ typename InputIterator,
+ typename = typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value
+ >::type
+ >
+ path(InputIterator begin, InputIterator end, codecvt_type const& cvt)
+ {
+ if (begin != end)
+ {
+ typedef std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source_t;
+ source_t source(begin, end);
+ assign(static_cast< source_t&& >(source), cvt);
+ }
+ }
+
+ path(std::nullptr_t) = delete;
+ path& operator= (std::nullptr_t) = delete;
+
+public:
+ // ----- assignments -----
+
+ // We need to explicitly define copy assignment as otherwise it will be implicitly defined as deleted because there is move assignment
+ path& operator=(path const& p);
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::disjunction<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ path&
+ >::type operator=(Source const& source)
+ {
+ return assign(source);
+ }
+
+ path& assign(path const& p)
+ {
+ m_pathname = p.m_pathname;
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type assign(Source const& source)
+ {
+ detail::path_traits::dispatch(source, assign_op(*this));
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type assign(Source const& source)
+ {
+ detail::path_traits::dispatch_convertible(source, assign_op(*this));
+ return *this;
+ }
+
+ path& assign(path const& p, codecvt_type const&)
+ {
+ m_pathname = p.m_pathname;
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type assign(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch(source, assign_op(*this), &cvt);
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type assign(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch_convertible(source, assign_op(*this), &cvt);
+ return *this;
+ }
+
+ path& assign(const value_type* begin, const value_type* end)
+ {
+ m_pathname.assign(begin, end);
+ return *this;
+ }
+
+ template< typename InputIterator >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type assign(InputIterator begin, InputIterator end)
+ {
+ m_pathname.clear();
+ if (begin != end)
+ {
+ typedef std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source_t;
+ source_t source(begin, end);
+ assign(static_cast< source_t&& >(source));
+ }
+ return *this;
+ }
+
+ path& assign(const value_type* begin, const value_type* end, codecvt_type const&)
+ {
+ m_pathname.assign(begin, end);
+ return *this;
+ }
+
+ template< typename InputIterator >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type assign(InputIterator begin, InputIterator end, codecvt_type const& cvt)
+ {
+ m_pathname.clear();
+ if (begin != end)
+ {
+ typedef std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source_t;
+ source_t source(begin, end);
+ assign(static_cast< source_t&& >(source), cvt);
+ }
+ return *this;
+ }
+
+ // ----- concatenation -----
+
+ path& operator+=(path const& p);
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type operator+=(Source const& source)
+ {
+ return concat(source);
+ }
+
+ path& operator+=(value_type c)
+ {
+ m_pathname.push_back(c);
+ return *this;
+ }
+
+ template< typename CharT >
+ typename std::enable_if<
+ detail::path_traits::is_path_char_type< CharT >::value,
+ path&
+ >::type operator+=(CharT c)
+ {
+ CharT tmp[2];
+ tmp[0] = c;
+ tmp[1] = static_cast< CharT >(0);
+ concat_op(*this)(tmp, tmp + 1);
+ return *this;
+ }
+
+ path& concat(path const& p)
+ {
+ m_pathname.append(p.m_pathname);
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type concat(Source const& source)
+ {
+ detail::path_traits::dispatch(source, concat_op(*this));
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type concat(Source const& source)
+ {
+ detail::path_traits::dispatch_convertible(source, concat_op(*this));
+ return *this;
+ }
+
+ path& concat(path const& p, codecvt_type const&)
+ {
+ m_pathname.append(p.m_pathname);
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type concat(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch(source, concat_op(*this), &cvt);
+ return *this;
+ }
+
+ template< typename Source >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type concat(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch_convertible(source, concat_op(*this), &cvt);
+ return *this;
+ }
+
+ path& concat(const value_type* begin, const value_type* end)
+ {
+ m_pathname.append(begin, end);
+ return *this;
+ }
+
+ template< typename InputIterator >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type concat(InputIterator begin, InputIterator end)
+ {
+ if (begin != end)
+ {
+ std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source(begin, end);
+ detail::path_traits::dispatch(source, concat_op(*this));
+ }
+ return *this;
+ }
+
+ path& concat(const value_type* begin, const value_type* end, codecvt_type const&)
+ {
+ m_pathname.append(begin, end);
+ return *this;
+ }
+
+ template< typename InputIterator >
+ typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type concat(InputIterator begin, InputIterator end, codecvt_type const& cvt)
+ {
+ if (begin != end)
+ {
+ std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source(begin, end);
+ detail::path_traits::dispatch(source, concat_op(*this), &cvt);
+ }
+ return *this;
+ }
+
+ // ----- appends -----
+
+ // if a separator is added, it is the preferred separator for the platform;
+ // slash for POSIX, backslash for Windows
+
+ path& operator/=(path const& p);
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type operator/=(Source const& source)
+ {
+ return append(source);
+ }
+
+ path& append(path const& p);
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type append(Source const& source)
+ {
+ detail::path_traits::dispatch(source, append_op(*this));
+ return *this;
+ }
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type append(Source const& source)
+ {
+ detail::path_traits::dispatch_convertible(source, append_op(*this));
+ return *this;
+ }
+
+ path& append(path const& p, codecvt_type const&);
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ path&
+ >::type append(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch(source, append_op(*this), &cvt);
+ return *this;
+ }
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ path&
+ >::type append(Source const& source, codecvt_type const& cvt)
+ {
+ detail::path_traits::dispatch_convertible(source, append_op(*this), &cvt);
+ return *this;
+ }
+
+ path& append(const value_type* begin, const value_type* end);
+
+ template< typename InputIterator >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type append(InputIterator begin, InputIterator end)
+ {
+ std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source(begin, end);
+ detail::path_traits::dispatch(source, append_op(*this));
+ return *this;
+ }
+
+ path& append(const value_type* begin, const value_type* end, codecvt_type const&);
+
+ template< typename InputIterator >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_path_source_iterator< InputIterator >,
+ detail::negation< detail::path_traits::is_native_char_ptr< InputIterator > >
+ >::value,
+ path&
+ >::type append(InputIterator begin, InputIterator end, const codecvt_type& cvt)
+ {
+ std::basic_string< typename std::iterator_traits< InputIterator >::value_type > source(begin, end);
+ detail::path_traits::dispatch(source, append_op(*this), &cvt);
+ return *this;
+ }
+
+ // ----- modifiers -----
+
+ void clear() noexcept { m_pathname.clear(); }
+ path& make_preferred();
+ path& remove_filename();
+ BOOST_FILESYSTEM_DECL path& remove_filename_and_trailing_separators();
+ BOOST_FILESYSTEM_DECL path& remove_trailing_separator();
+ BOOST_FILESYSTEM_DECL path& replace_filename(path const& replacement);
+ path& replace_extension(path const& new_extension = path());
+
+ void swap(path& rhs) noexcept { m_pathname.swap(rhs.m_pathname); }
+
+ // ----- observers -----
+
+ // For operating systems that format file paths differently than directory
+ // paths, return values from observers are formatted as file names unless there
+ // is a trailing separator, in which case returns are formatted as directory
+ // paths. POSIX and Windows make no such distinction.
+
+ // Implementations are permitted to return const values or const references.
+
+ // The string or path returned by an observer are specified as being formatted
+ // as "native" or "generic".
+ //
+ // For POSIX, these are all the same format; slashes and backslashes are as input and
+ // are not modified.
+ //
+ // For Windows, native: as input; slashes and backslashes are not modified;
+ // this is the format of the internally stored string.
+ // generic: backslashes are converted to slashes
+
+ // ----- native format observers -----
+
+ string_type const& native() const noexcept { return m_pathname; }
+ const value_type* c_str() const noexcept { return m_pathname.c_str(); }
+ string_type::size_type size() const noexcept { return m_pathname.size(); }
+
+ template< typename String >
+ String string() const;
+
+ template< typename String >
+ String string(codecvt_type const& cvt) const;
+
+#ifdef BOOST_WINDOWS_API
+ std::string string() const
+ {
+ std::string tmp;
+ if (!m_pathname.empty())
+ detail::path_traits::convert(m_pathname.data(), m_pathname.data() + m_pathname.size(), tmp);
+ return tmp;
+ }
+ std::string string(codecvt_type const& cvt) const
+ {
+ std::string tmp;
+ if (!m_pathname.empty())
+ detail::path_traits::convert(m_pathname.data(), m_pathname.data() + m_pathname.size(), tmp, &cvt);
+ return tmp;
+ }
+
+ // string_type is std::wstring, so there is no conversion
+ std::wstring const& wstring() const { return m_pathname; }
+ std::wstring const& wstring(codecvt_type const&) const { return m_pathname; }
+#else // BOOST_POSIX_API
+ // string_type is std::string, so there is no conversion
+ std::string const& string() const { return m_pathname; }
+ std::string const& string(codecvt_type const&) const { return m_pathname; }
+
+ std::wstring wstring() const
+ {
+ std::wstring tmp;
+ if (!m_pathname.empty())
+ detail::path_traits::convert(m_pathname.data(), m_pathname.data() + m_pathname.size(), tmp);
+ return tmp;
+ }
+ std::wstring wstring(codecvt_type const& cvt) const
+ {
+ std::wstring tmp;
+ if (!m_pathname.empty())
+ detail::path_traits::convert(m_pathname.data(), m_pathname.data() + m_pathname.size(), tmp, &cvt);
+ return tmp;
+ }
+#endif
+
+ // ----- generic format observers -----
+
+ // Experimental generic function returning generic formatted path (i.e. separators
+ // are forward slashes). Motivation: simpler than a family of generic_*string
+ // functions.
+ path generic_path() const;
+
+ template< typename String >
+ String generic_string() const;
+
+ template< typename String >
+ String generic_string(codecvt_type const& cvt) const;
+
+ std::string generic_string() const { return generic_path().string(); }
+ std::string generic_string(codecvt_type const& cvt) const { return generic_path().string(cvt); }
+ std::wstring generic_wstring() const { return generic_path().wstring(); }
+ std::wstring generic_wstring(codecvt_type const& cvt) const { return generic_path().wstring(cvt); }
+
+ // ----- compare -----
+
+ int compare(path const& p) const; // generic, lexicographical
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ int
+ >::type compare(Source const& source) const
+ {
+ return detail::path_traits::dispatch(source, compare_op(*this));
+ }
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ int
+ >::type compare(Source const& source) const
+ {
+ return detail::path_traits::dispatch_convertible(source, compare_op(*this));
+ }
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_path_source< typename std::remove_cv< Source >::type >::value,
+ int
+ >::type compare(Source const& source, codecvt_type const& cvt) const
+ {
+ return detail::path_traits::dispatch(source, compare_op(*this), &cvt);
+ }
+
+ template< typename Source >
+ BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >,
+ detail::negation< detail::path_traits::is_path_source< typename std::remove_cv< Source >::type > >
+ >::value,
+ int
+ >::type compare(Source const& source, codecvt_type const& cvt) const
+ {
+ return detail::path_traits::dispatch_convertible(source, compare_op(*this), &cvt);
+ }
+
+ // ----- decomposition -----
+
+ path root_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_root_path_size(*this)); }
+ // returns 0 or 1 element path even on POSIX, root_name() is non-empty() for network paths
+ path root_name() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_root_name_size(*this)); }
+
+ // returns 0 or 1 element path
+ path root_directory() const
+ {
+ detail::path_algorithms::substring root_dir = detail::path_algorithms::find_root_directory(*this);
+ const value_type* p = m_pathname.c_str() + root_dir.pos;
+ return path(p, p + root_dir.size);
+ }
+
+ path relative_path() const
+ {
+ detail::path_algorithms::substring rel_path = detail::path_algorithms::find_relative_path(*this);
+ const value_type* p = m_pathname.c_str() + rel_path.pos;
+ return path(p, p + rel_path.size);
+ }
+
+ path parent_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_parent_path_size(*this)); }
+
+ path filename() const; // returns 0 or 1 element path
+ path stem() const; // returns 0 or 1 element path
+ path extension() const; // returns 0 or 1 element path
+
+ // ----- query -----
+
+ bool empty() const noexcept { return m_pathname.empty(); }
+ bool filename_is_dot() const;
+ bool filename_is_dot_dot() const;
+ bool has_root_path() const { return detail::path_algorithms::find_root_path_size(*this) > 0; }
+ bool has_root_name() const { return detail::path_algorithms::find_root_name_size(*this) > 0; }
+ bool has_root_directory() const { return detail::path_algorithms::find_root_directory(*this).size > 0; }
+ bool has_relative_path() const { return detail::path_algorithms::find_relative_path(*this).size > 0; }
+ bool has_parent_path() const { return detail::path_algorithms::find_parent_path_size(*this) > 0; }
+ bool has_filename() const;
+ bool has_stem() const { return !stem().empty(); }
+ bool has_extension() const { return !extension().empty(); }
+ bool is_relative() const { return !is_absolute(); }
+ bool is_absolute() const
+ {
+#if defined(BOOST_WINDOWS_API)
+ return has_root_name() && has_root_directory();
+#else
+ return has_root_directory();
+#endif
+ }
+
+ // ----- lexical operations -----
+
+ path lexically_normal() const;
+ BOOST_FILESYSTEM_DECL path lexically_relative(path const& base) const;
+ path lexically_proximate(path const& base) const;
+
+ // ----- iterators -----
+
+ BOOST_FILESYSTEM_DECL iterator begin() const;
+ BOOST_FILESYSTEM_DECL iterator end() const;
+ reverse_iterator rbegin() const;
+ reverse_iterator rend() const;
+
+ // ----- static member functions -----
+
+ static BOOST_FILESYSTEM_DECL std::locale imbue(std::locale const& loc);
+ static BOOST_FILESYSTEM_DECL codecvt_type const& codecvt();
+
+ //--------------------------------------------------------------------------------------//
+ // class path private members //
+ //--------------------------------------------------------------------------------------//
+private:
+ /*
+ * m_pathname has the type, encoding, and format required by the native
+ * operating system. Thus for POSIX and Windows there is no conversion for
+ * passing m_pathname.c_str() to the O/S API or when obtaining a path from the
+ * O/S API. POSIX encoding is unspecified other than for dot and slash
+ * characters; POSIX just treats paths as a sequence of bytes. Windows
+ * encoding is UCS-2 or UTF-16 depending on the version.
+ */
+ string_type m_pathname; // Windows: as input; backslashes NOT converted to slashes,
+ // slashes NOT converted to backslashes
+};
+
+namespace detail {
+BOOST_FILESYSTEM_DECL path const& dot_path();
+BOOST_FILESYSTEM_DECL path const& dot_dot_path();
+} // namespace detail
+
+namespace path_detail {
+
+//------------------------------------------------------------------------------------//
+// class path::iterator //
+//------------------------------------------------------------------------------------//
+
+class path_iterator :
+ public boost::iterator_facade<
+ path_iterator,
+ const path,
+ boost::bidirectional_traversal_tag
+ >
+{
+private:
+ friend class boost::iterator_core_access;
+ friend class boost::filesystem::path;
+ friend class path_reverse_iterator;
+ friend struct boost::filesystem::detail::path_algorithms;
+
+ path const& dereference() const { return m_element; }
+
+ bool equal(path_iterator const& rhs) const noexcept
+ {
+ return m_path_ptr == rhs.m_path_ptr && m_pos == rhs.m_pos;
+ }
+
+ void increment();
+ void decrement();
+
+private:
+ // current element
+ path m_element;
+ // path being iterated over
+ const path* m_path_ptr;
+ // position of m_element in m_path_ptr->m_pathname.
+ // if m_element is implicit dot, m_pos is the
+ // position of the last separator in the path.
+ // end() iterator is indicated by
+ // m_pos == m_path_ptr->m_pathname.size()
+ path::string_type::size_type m_pos;
+};
+
+//------------------------------------------------------------------------------------//
+// class path::reverse_iterator //
+//------------------------------------------------------------------------------------//
+
+class path_reverse_iterator :
+ public boost::iterator_facade<
+ path_reverse_iterator,
+ const path,
+ boost::bidirectional_traversal_tag
+ >
+{
+public:
+ explicit path_reverse_iterator(path_iterator itr) :
+ m_itr(itr)
+ {
+ if (itr != itr.m_path_ptr->begin())
+ m_element = *--itr;
+ }
+
+private:
+ friend class boost::iterator_core_access;
+ friend class boost::filesystem::path;
+
+ path const& dereference() const { return m_element; }
+ bool equal(path_reverse_iterator const& rhs) const noexcept { return m_itr == rhs.m_itr; }
+
+ void increment()
+ {
+ --m_itr;
+ if (m_itr != m_itr.m_path_ptr->begin())
+ {
+ path_iterator tmp = m_itr;
+ m_element = *--tmp;
+ }
+ }
+
+ void decrement()
+ {
+ m_element = *m_itr;
+ ++m_itr;
+ }
+
+private:
+ path_iterator m_itr;
+ path m_element;
+};
+
+// std::lexicographical_compare would infinitely recurse because path iterators
+// yield paths, so provide a path aware version
+bool lexicographical_compare(path_iterator first1, path_iterator const& last1, path_iterator first2, path_iterator const& last2);
+
+} // namespace path_detail
+
+using path_detail::lexicographical_compare;
+
+//------------------------------------------------------------------------------------//
+// //
+// non-member functions //
+// //
+//------------------------------------------------------------------------------------//
+
+BOOST_FORCEINLINE bool operator==(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) == 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator==(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) == 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator==(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) == 0;
+}
+
+BOOST_FORCEINLINE bool operator!=(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) != 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator!=(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) != 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator!=(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) != 0;
+}
+
+BOOST_FORCEINLINE bool operator<(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) < 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator<(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) < 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator<(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) > 0;
+}
+
+BOOST_FORCEINLINE bool operator<=(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) <= 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator<=(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) <= 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator<=(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) >= 0;
+}
+
+BOOST_FORCEINLINE bool operator>(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) > 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator>(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) > 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator>(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) < 0;
+}
+
+BOOST_FORCEINLINE bool operator>=(path const& lhs, path const& rhs)
+{
+ return lhs.compare(rhs) >= 0;
+}
+
+template< typename Path, typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator>=(Path const& lhs, Source const& rhs)
+{
+ return lhs.compare(rhs) >= 0;
+}
+
+template< typename Source, typename Path >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::conjunction<
+ std::is_same< Path, path >,
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >
+ >::value,
+ bool
+>::type operator>=(Source const& lhs, Path const& rhs)
+{
+ return rhs.compare(lhs) <= 0;
+}
+
+
+// Note: Declared as a template to delay binding to Boost.ContainerHash functions and make the dependency optional
+template< typename Path >
+inline typename std::enable_if<
+ std::is_same< Path, path >::value,
+ std::size_t
+>::type hash_value(Path const& p) noexcept
+{
+#ifdef BOOST_WINDOWS_API
+ std::size_t seed = 0u;
+ for (typename Path::value_type const* it = p.c_str(); *it; ++it)
+ hash_combine(seed, *it == L'/' ? L'\\' : *it);
+ return seed;
+#else // BOOST_POSIX_API
+ return hash_range(p.native().begin(), p.native().end());
+#endif
+}
+
+inline void swap(path& lhs, path& rhs) noexcept
+{
+ lhs.swap(rhs);
+}
+
+BOOST_FORCEINLINE path operator/(path lhs, path const& rhs)
+{
+ lhs.append(rhs);
+ return lhs;
+}
+
+template< typename Source >
+BOOST_FORCEINLINE typename std::enable_if<
+ detail::path_traits::is_convertible_to_path_source< typename std::remove_cv< Source >::type >::value,
+ path
+>::type operator/(path lhs, Source const& rhs)
+{
+ lhs.append(rhs);
+ return lhs;
+}
+
+// inserters and extractors
+// use boost::io::quoted() to handle spaces in paths
+// use '&' as escape character to ease use for Windows paths
+
+template< typename Char, typename Traits >
+inline std::basic_ostream< Char, Traits >&
+operator<<(std::basic_ostream< Char, Traits >& os, path const& p)
+{
+ return os << boost::io::quoted(p.template string< std::basic_string< Char > >(), static_cast< Char >('&'));
+}
+
+template< typename Char, typename Traits >
+inline std::basic_istream< Char, Traits >&
+operator>>(std::basic_istream< Char, Traits >& is, path& p)
+{
+ std::basic_string< Char > str;
+ is >> boost::io::quoted(str, static_cast< Char >('&'));
+ p = str;
+ return is;
+}
+
+// name_checks
+
+// These functions are holdovers from version 1. It isn't clear they have much
+// usefulness, or how to generalize them for later versions.
+
+BOOST_FILESYSTEM_DECL bool portable_posix_name(std::string const& name);
+BOOST_FILESYSTEM_DECL bool windows_name(std::string const& name);
+BOOST_FILESYSTEM_DECL bool portable_name(std::string const& name);
+BOOST_FILESYSTEM_DECL bool portable_directory_name(std::string const& name);
+BOOST_FILESYSTEM_DECL bool portable_file_name(std::string const& name);
+BOOST_FILESYSTEM_DECL bool native(std::string const& name);
+
+namespace detail {
+
+// For POSIX, is_directory_separator() and is_element_separator() are identical since
+// a forward slash is the only valid directory separator and also the only valid
+// element separator. For Windows, forward slash and back slash are the possible
+// directory separators, but colon (example: "c:foo") is also an element separator.
+inline bool is_directory_separator(path::value_type c) noexcept
+{
+ return c == path::separator
+#ifdef BOOST_WINDOWS_API
+ || c == path::preferred_separator
+#endif
+ ;
+}
+
+inline bool is_element_separator(path::value_type c) noexcept
+{
+ return c == path::separator
+#ifdef BOOST_WINDOWS_API
+ || c == path::preferred_separator || c == L':'
+#endif
+ ;
+}
+
+} // namespace detail
+
+//------------------------------------------------------------------------------------//
+// class path miscellaneous function implementations //
+//------------------------------------------------------------------------------------//
+
+namespace detail {
+
+inline bool path_algorithms::has_filename_v3(path const& p)
+{
+ return !p.m_pathname.empty();
+}
+
+inline bool path_algorithms::has_filename_v4(path const& p)
+{
+ return path_algorithms::find_filename_v4_size(p) > 0;
+}
+
+inline path path_algorithms::filename_v4(path const& p)
+{
+ string_type::size_type filename_size = path_algorithms::find_filename_v4_size(p);
+ string_type::size_type pos = p.m_pathname.size() - filename_size;
+ const value_type* ptr = p.m_pathname.c_str() + pos;
+ return path(ptr, ptr + filename_size);
+}
+
+inline path path_algorithms::extension_v4(path const& p)
+{
+ string_type::size_type extension_size = path_algorithms::find_extension_v4_size(p);
+ string_type::size_type pos = p.m_pathname.size() - extension_size;
+ const value_type* ptr = p.m_pathname.c_str() + pos;
+ return path(ptr, ptr + extension_size);
+}
+
+inline void path_algorithms::append_v4(path& left, path const& right)
+{
+ path_algorithms::append_v4(left, right.m_pathname.c_str(), right.m_pathname.c_str() + right.m_pathname.size());
+}
+
+} // namespace detail
+
+// Note: Because of the range constructor in C++23 std::string_view that involves a check for contiguous_range concept,
+// any non-template function call that requires a check whether the source argument (which may be fs::path)
+// is convertible to std::string_view must be made after fs::path::iterator is defined. This includes overload
+// resolution and SFINAE checks. Otherwise, the concept check result formally changes between fs::path::iterator
+// is not defined and defined, which causes compilation errors with gcc 11 and later.
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106808
+
+BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const value_type* source, const value_type* source_end, const codecvt_type*) const
+{
+ path src;
+ src.m_pathname.assign(source, source_end);
+ return m_self.compare(src);
+}
+
+template< typename OtherChar >
+BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt) const
+{
+ path src;
+ detail::path_traits::convert(source, source_end, src.m_pathname, cvt);
+ return m_self.compare(src);
+}
+
+inline path& path::operator=(path const& p)
+{
+ return assign(p);
+}
+
+inline path& path::operator+=(path const& p)
+{
+ return concat(p);
+}
+
+BOOST_FORCEINLINE path& path::operator/=(path const& p)
+{
+ return append(p);
+}
+
+inline path path::lexically_proximate(path const& base) const
+{
+ path tmp(lexically_relative(base));
+ return tmp.empty() ? *this : tmp;
+}
+
+inline path::reverse_iterator path::rbegin() const
+{
+ return reverse_iterator(end());
+}
+
+inline path::reverse_iterator path::rend() const
+{
+ return reverse_iterator(begin());
+}
+
+inline bool path::filename_is_dot() const
+{
+ // implicit dot is tricky, so actually call filename(); see path::filename() example
+ // in reference.html
+ path p(filename());
+ return p.size() == 1 && *p.c_str() == dot;
+}
+
+inline bool path::filename_is_dot_dot() const
+{
+ return size() >= 2 && m_pathname[size() - 1] == dot && m_pathname[size() - 2] == dot && (m_pathname.size() == 2 || detail::is_element_separator(m_pathname[size() - 3]));
+ // use detail::is_element_separator() rather than detail::is_directory_separator
+ // to deal with "c:.." edge case on Windows when ':' acts as a separator
+}
+
+// The following functions are defined differently, depending on Boost.Filesystem version in use.
+// To avoid ODR violation, these functions are not defined when the library itself is built.
+// This makes sure they are not compiled when the library is built, and the only version there is
+// is the one in user's code. Users are supposed to consistently use the same Boost.Filesystem version
+// in all their translation units.
+#if !defined(BOOST_FILESYSTEM_SOURCE)
+
+BOOST_FORCEINLINE path& path::append(path const& p)
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size());
+ return *this;
+}
+
+BOOST_FORCEINLINE path& path::append(path const& p, codecvt_type const&)
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size());
+ return *this;
+}
+
+BOOST_FORCEINLINE path& path::append(const value_type* begin, const value_type* end)
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, begin, end);
+ return *this;
+}
+
+BOOST_FORCEINLINE path& path::append(const value_type* begin, const value_type* end, codecvt_type const&)
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, begin, end);
+ return *this;
+}
+
+BOOST_FORCEINLINE path& path::remove_filename()
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::remove_filename)(*this);
+ return *this;
+}
+
+BOOST_FORCEINLINE path& path::replace_extension(path const& new_extension)
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::replace_extension)(*this, new_extension);
+ return *this;
+}
+
+BOOST_FORCEINLINE int path::compare(path const& p) const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::compare)(*this, p);
+}
+
+BOOST_FORCEINLINE path path::filename() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::filename)(*this);
+}
+
+BOOST_FORCEINLINE path path::stem() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::stem)(*this);
+}
+
+BOOST_FORCEINLINE path path::extension() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::extension)(*this);
+}
+
+BOOST_FORCEINLINE bool path::has_filename() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::has_filename)(*this);
+}
+
+BOOST_FORCEINLINE path path::lexically_normal() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::lexically_normal)(*this);
+}
+
+BOOST_FORCEINLINE path path::generic_path() const
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::generic_path)(*this);
+}
+
+BOOST_FORCEINLINE path& path::make_preferred()
+{
+ // No effect on POSIX
+#if defined(BOOST_WINDOWS_API)
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::make_preferred)(*this);
+#endif
+ return *this;
+}
+
+namespace path_detail {
+
+BOOST_FORCEINLINE void path_iterator::increment()
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::increment)(*this);
+}
+
+BOOST_FORCEINLINE void path_iterator::decrement()
+{
+ BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::decrement)(*this);
+}
+
+BOOST_FORCEINLINE bool lexicographical_compare(path_iterator first1, path_iterator const& last1, path_iterator first2, path_iterator const& last2)
+{
+ return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::lex_compare)(first1, last1, first2, last2) < 0;
+}
+
+} // namespace path_detail
+
+#endif // !defined(BOOST_FILESYSTEM_SOURCE)
+
+//--------------------------------------------------------------------------------------//
+// class path member template specializations //
+//--------------------------------------------------------------------------------------//
+
+template< >
+inline std::string path::string< std::string >() const
+{
+ return string();
+}
+
+template< >
+inline std::wstring path::string< std::wstring >() const
+{
+ return wstring();
+}
+
+template< >
+inline std::string path::string< std::string >(codecvt_type const& cvt) const
+{
+ return string(cvt);
+}
+
+template< >
+inline std::wstring path::string< std::wstring >(codecvt_type const& cvt) const
+{
+ return wstring(cvt);
+}
+
+template< >
+inline std::string path::generic_string< std::string >() const
+{
+ return generic_string();
+}
+
+template< >
+inline std::wstring path::generic_string< std::wstring >() const
+{
+ return generic_wstring();
+}
+
+template< >
+inline std::string path::generic_string< std::string >(codecvt_type const& cvt) const
+{
+ return generic_string(cvt);
+}
+
+template< >
+inline std::wstring path::generic_string< std::wstring >(codecvt_type const& cvt) const
+{
+ return generic_wstring(cvt);
+}
+
+} // namespace filesystem
+} // namespace boost
+
+//----------------------------------------------------------------------------//
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_PATH_HPP
diff --git a/contrib/restricted/boost/filesystem/src/atomic_ref.hpp b/contrib/restricted/boost/filesystem/src/atomic_ref.hpp
new file mode 100644
index 0000000000..6e8208d39e
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/atomic_ref.hpp
@@ -0,0 +1,32 @@
+// atomic.hpp ------------------------------------------------------------------------//
+
+// Copyright 2021 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_ATOMIC_REF_HPP_
+#define BOOST_FILESYSTEM_SRC_ATOMIC_REF_HPP_
+
+#include <boost/filesystem/config.hpp>
+
+#if !defined(BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF)
+
+#include <atomic>
+
+namespace atomic_ns = std;
+
+#else // !defined(BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF)
+
+#include <boost/memory_order.hpp>
+#include <boost/atomic/atomic_ref.hpp>
+
+namespace atomic_ns = boost;
+
+#endif // !defined(BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF)
+
+#endif // BOOST_FILESYSTEM_SRC_ATOMIC_REF_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/atomic_tools.hpp b/contrib/restricted/boost/filesystem/src/atomic_tools.hpp
new file mode 100644
index 0000000000..a60e5d325a
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/atomic_tools.hpp
@@ -0,0 +1,69 @@
+// atomic_tools.hpp ------------------------------------------------------------------//
+
+// Copyright 2021 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_ATOMIC_TOOLS_HPP_
+#define BOOST_FILESYSTEM_SRC_ATOMIC_TOOLS_HPP_
+
+#include <boost/filesystem/config.hpp>
+
+#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+
+#include "atomic_ref.hpp"
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+//! Atomically loads the value
+template< typename T >
+BOOST_FORCEINLINE T atomic_load_relaxed(T& a)
+{
+ return atomic_ns::atomic_ref< T >(a).load(atomic_ns::memory_order_relaxed);
+}
+
+//! Atomically stores the value
+template< typename T >
+BOOST_FORCEINLINE void atomic_store_relaxed(T& a, T val)
+{
+ atomic_ns::atomic_ref< T >(a).store(val, atomic_ns::memory_order_relaxed);
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+//! Atomically loads the value
+template< typename T >
+BOOST_FORCEINLINE T atomic_load_relaxed(T const& a)
+{
+ return a;
+}
+
+//! Atomically stores the value
+template< typename T >
+BOOST_FORCEINLINE void atomic_store_relaxed(T& a, T val)
+{
+ a = val;
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+
+#endif // BOOST_FILESYSTEM_SRC_ATOMIC_TOOLS_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp
new file mode 100644
index 0000000000..dc3df54d36
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp
@@ -0,0 +1,120 @@
+// codecvt_error_category implementation file ----------------------------------------//
+
+// Copyright 2009 Beman Dawes
+// Copyright 2022 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt)
+
+// Library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/config/warning_disable.hpp>
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/detail/path_traits.hpp>
+#include <boost/system/error_category.hpp>
+#include <locale>
+#include <string>
+
+#include "private_config.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+
+namespace {
+
+#if (defined(BOOST_GCC) && BOOST_GCC >= 40600) || defined(BOOST_CLANG)
+#pragma GCC diagnostic push
+// '(anonymous namespace)::codecvt_error_cat' has virtual functions but non-virtual destructor
+// This is not a problem as instances of codecvt_error_cat are never destroyed through a pointer to base.
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+class codecvt_error_cat final :
+ public boost::system::error_category
+{
+public:
+ // clang up to version 3.8 requires a user-defined default constructor in order to be able to declare a static constant of the error category.
+ BOOST_SYSTEM_CONSTEXPR codecvt_error_cat() noexcept {}
+ const char* name() const noexcept override;
+ std::string message(int ev) const override;
+};
+
+const char* codecvt_error_cat::name() const noexcept
+{
+ return "codecvt";
+}
+
+std::string codecvt_error_cat::message(int ev) const
+{
+ std::string str;
+ switch (ev)
+ {
+ case std::codecvt_base::ok:
+ str = "ok";
+ break;
+ case std::codecvt_base::partial:
+ str = "partial";
+ break;
+ case std::codecvt_base::error:
+ str = "error";
+ break;
+ case std::codecvt_base::noconv:
+ str = "noconv";
+ break;
+ default:
+ str = "unknown error";
+ break;
+ }
+ return str;
+}
+
+#if (defined(BOOST_GCC) && BOOST_GCC >= 40600) || defined(BOOST_CLANG)
+#pragma GCC diagnostic pop
+#endif
+
+} // unnamed namespace
+
+BOOST_FILESYSTEM_DECL boost::system::error_category const& codecvt_error_category() noexcept
+{
+ static
+#if defined(BOOST_SYSTEM_HAS_CONSTEXPR)
+ constexpr
+#else
+ const
+#endif
+ codecvt_error_cat codecvt_error_cat_const;
+ return codecvt_error_cat_const;
+}
+
+// Try to initialize the error category instance as early as possible to make sure it is
+// available during global deinitialization stage. For MSVC, codecvt_error_category() will
+// be called early by MSVC-specific initialization routine in path.cpp.
+#if !defined(BOOST_SYSTEM_HAS_CONSTEXPR) && !defined(_MSC_VER)
+
+namespace {
+
+struct codecvt_error_category_initializer
+{
+ codecvt_error_category_initializer() { boost::filesystem::codecvt_error_category(); }
+};
+
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+const codecvt_error_category_initializer g_codecvt_error_category_initializer;
+
+} // namespace
+
+#endif // !defined(BOOST_SYSTEM_HAS_CONSTEXPR) && !defined(_MSC_VER)
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/directory.cpp b/contrib/restricted/boost/filesystem/src/directory.cpp
new file mode 100644
index 0000000000..576a460dd3
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/directory.cpp
@@ -0,0 +1,1772 @@
+// directory.cpp --------------------------------------------------------------------//
+
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2019, 2022-2024 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/directory.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/file_status.hpp>
+
+#include <cstddef>
+#include <cerrno>
+#include <cstring>
+#include <cstdlib> // std::malloc, std::free
+#include <new> // std::nothrow, std::bad_alloc
+#include <limits>
+#include <string>
+#include <utility> // std::move
+#include <boost/assert.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+
+#ifdef BOOST_POSIX_API
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <memory>
+#include <boost/scope/unique_fd.hpp>
+
+#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS >= 0) && defined(_SC_THREAD_SAFE_FUNCTIONS) && \
+ !defined(__CYGWIN__) && \
+ !(defined(linux) || defined(__linux) || defined(__linux__)) && \
+ !defined(__ANDROID__) && \
+ (!defined(__hpux) || defined(_REENTRANT)) && \
+ (!defined(_AIX) || defined(__THREAD_SAFE)) && \
+ !defined(__wasm)
+#define BOOST_FILESYSTEM_USE_READDIR_R
+#endif
+
+// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#define BOOST_FILESYSTEM_NO_O_CLOEXEC
+#endif
+
+#include "posix_tools.hpp"
+
+#else // BOOST_WINDOWS_API
+
+#include <cwchar>
+#include <windows.h>
+#include <boost/winapi/basic_types.hpp> // NTSTATUS_
+
+#include "windows_tools.hpp"
+
+#endif // BOOST_WINDOWS_API
+
+#include "atomic_tools.hpp"
+#include "error_handling.hpp"
+#include "private_config.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace fs = boost::filesystem;
+using boost::system::error_code;
+using boost::system::system_category;
+
+namespace boost {
+namespace filesystem {
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_entry //
+// //
+//--------------------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const
+{
+ m_status = filesystem::file_status();
+ m_symlink_status = filesystem::file_status();
+
+ m_symlink_status = detail::symlink_status(m_path, ec);
+
+ if (!filesystem::is_symlink(m_symlink_status))
+ {
+ // Also works if symlink_status fails - set m_status to status_error as well
+ m_status = m_symlink_status;
+ }
+ else
+ {
+ m_status = detail::status(m_path, ec);
+ }
+}
+
+//--------------------------------------------------------------------------------------//
+// //
+// directory_iterator //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace detail {
+
+#if defined(BOOST_POSIX_API)
+
+//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
+boost::scope::unique_fd open_directory(path const& p, directory_options opts, system::error_code& ec)
+{
+ ec.clear();
+
+ int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ if ((opts & directory_options::_detail_no_follow) != directory_options::none)
+ flags |= O_NOFOLLOW;
+#endif
+
+ int res;
+ while (true)
+ {
+ res = ::open(p.c_str(), flags);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ const int err = errno;
+ if (err == EINTR)
+ continue;
+ ec = system::error_code(err, system::system_category());
+ return boost::scope::unique_fd();
+ }
+
+ break;
+ }
+
+#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
+ boost::scope::unique_fd fd(res);
+
+ res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ const int err = errno;
+ ec = system::error_code(err, system::system_category());
+ return boost::scope::unique_fd();
+ }
+
+ return fd;
+#else
+ return boost::scope::unique_fd(res);
+#endif
+}
+
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
+boost::scope::unique_fd openat_directory(int basedir_fd, path const& p, directory_options opts, system::error_code& ec)
+{
+ ec.clear();
+
+ int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ if ((opts & directory_options::_detail_no_follow) != directory_options::none)
+ flags |= O_NOFOLLOW;
+#endif
+
+ int res;
+ while (true)
+ {
+ res = ::openat(basedir_fd, p.c_str(), flags);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ const int err = errno;
+ if (err == EINTR)
+ continue;
+ ec = system::error_code(err, system::system_category());
+ return boost::scope::unique_fd();
+ }
+
+ break;
+ }
+
+#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
+ boost::scope::unique_fd fd(res);
+
+ res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ const int err = errno;
+ ec = system::error_code(err, system::system_category());
+ return boost::scope::unique_fd();
+ }
+
+ return fd;
+#else
+ return boost::scope::unique_fd(res);
+#endif
+}
+
+#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+#endif // defined(BOOST_POSIX_API)
+
+BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u;
+
+BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t extra_size) noexcept
+{
+ if (extra_size > 0)
+ class_size = (class_size + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
+ std::size_t total_size = class_size + extra_size;
+
+ // Return nullptr on OOM
+ void* p = std::malloc(total_size);
+ if (BOOST_LIKELY(p != nullptr))
+ std::memset(p, 0, total_size);
+ return p;
+}
+
+BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) noexcept
+{
+ std::free(p);
+}
+
+BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) noexcept
+{
+ std::free(p);
+}
+
+namespace {
+
+inline void* get_dir_itr_imp_extra_data(dir_itr_imp* imp) noexcept
+{
+ BOOST_CONSTEXPR_OR_CONST std::size_t extra_data_offset = (sizeof(dir_itr_imp) + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
+ return reinterpret_cast< unsigned char* >(imp) + extra_data_offset;
+}
+
+#ifdef BOOST_POSIX_API
+
+inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept
+{
+ if (imp.handle != nullptr)
+ {
+ DIR* h = static_cast< DIR* >(imp.handle);
+ imp.handle = nullptr;
+ int err = 0;
+ if (BOOST_UNLIKELY(::closedir(h) != 0))
+ {
+ err = errno;
+ return system::error_code(err, system::system_category());
+ }
+ }
+
+ return error_code();
+}
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+// Obtains a file descriptor from the directory iterator
+inline int dir_itr_fd(dir_itr_imp const& imp, system::error_code& ec)
+{
+ int fd = ::dirfd(static_cast< DIR* >(imp.handle));
+ if (BOOST_UNLIKELY(fd < 0))
+ {
+ int err = errno;
+ ec = system::error_code(err, system::system_category());
+ }
+
+ return fd;
+}
+
+#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+// Obtains maximum length of a path, not including the terminating zero
+inline std::size_t get_path_max()
+{
+ // this code is based on Stevens and Rago, Advanced Programming in the
+ // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
+ std::size_t max = 0;
+ errno = 0;
+ long res = ::pathconf("/", _PC_PATH_MAX);
+ if (res < 0)
+ {
+#if defined(PATH_MAX)
+ max = PATH_MAX;
+#else
+ max = 4096;
+#endif
+ }
+ else
+ {
+ max = static_cast< std::size_t >(res); // relative root
+#if defined(PATH_MAX)
+ if (max < PATH_MAX)
+ max = PATH_MAX;
+#endif
+ }
+
+ if ((max + 1) < sizeof(dirent().d_name))
+ max = sizeof(dirent().d_name) - 1;
+
+ return max;
+}
+
+// Returns maximum length of a path, not including the terminating zero
+inline std::size_t path_max()
+{
+ static const std::size_t max = get_path_max();
+ return max;
+}
+
+#endif // BOOST_FILESYSTEM_USE_READDIR_R
+
+// *result set to nullptr on end of directory
+#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+inline
+#endif
+int readdir_impl(dir_itr_imp& imp, struct dirent** result)
+{
+ errno = 0;
+
+ struct dirent* p = ::readdir(static_cast< DIR* >(imp.handle));
+ *result = p;
+ if (!p)
+ return errno;
+ return 0;
+}
+
+#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
+{
+ return readdir_impl(imp, result);
+}
+
+#else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+int readdir_r_impl(dir_itr_imp& imp, struct dirent** result)
+{
+ return ::readdir_r
+ (
+ static_cast< DIR* >(imp.handle),
+ static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)),
+ result
+ );
+}
+
+int readdir_select_impl(dir_itr_imp& imp, struct dirent** result);
+
+typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result);
+
+//! Pointer to the actual implementation of the copy_file_data implementation
+readdir_impl_t* readdir_impl_ptr = &readdir_select_impl;
+
+void init_readdir_impl()
+{
+ readdir_impl_t* impl = &readdir_impl;
+ if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
+ impl = &readdir_r_impl;
+
+ filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl);
+}
+
+struct readdir_initializer
+{
+ readdir_initializer()
+ {
+ init_readdir_impl();
+ }
+};
+
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+const readdir_initializer readdir_init;
+
+int readdir_select_impl(dir_itr_imp& imp, struct dirent** result)
+{
+ init_readdir_impl();
+ return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
+}
+
+inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
+{
+ return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
+}
+
+#endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ dirent* result = nullptr;
+ int err = invoke_readdir(imp, &result);
+ if (BOOST_UNLIKELY(err != 0))
+ return system::error_code(err, system::system_category());
+ if (result == nullptr)
+ return dir_itr_close(imp);
+
+ filename = result->d_name;
+
+#if defined(BOOST_FILESYSTEM_HAS_DIRENT_D_TYPE)
+ if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
+ {
+ sf = symlink_sf = fs::file_status(fs::status_error);
+ }
+ else // filesystem supplies d_type value
+ {
+ if (result->d_type == DT_REG)
+ sf = symlink_sf = fs::file_status(fs::regular_file);
+ else if (result->d_type == DT_DIR)
+ sf = symlink_sf = fs::file_status(fs::directory_file);
+ else if (result->d_type == DT_LNK)
+ {
+ sf = fs::file_status(fs::status_error);
+ symlink_sf = fs::file_status(fs::symlink_file);
+ }
+ else
+ {
+ switch (result->d_type)
+ {
+ case DT_SOCK:
+ sf = symlink_sf = fs::file_status(fs::socket_file);
+ break;
+ case DT_FIFO:
+ sf = symlink_sf = fs::file_status(fs::fifo_file);
+ break;
+ case DT_BLK:
+ sf = symlink_sf = fs::file_status(fs::block_file);
+ break;
+ case DT_CHR:
+ sf = symlink_sf = fs::file_status(fs::character_file);
+ break;
+ default:
+ sf = symlink_sf = fs::file_status(fs::status_error);
+ break;
+ }
+ }
+ }
+#else
+ sf = symlink_sf = fs::file_status(fs::status_error);
+#endif
+ return system::error_code();
+}
+
+system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status&, fs::file_status&)
+{
+ std::size_t extra_size = 0u;
+#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
+ {
+ readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
+ if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl))
+ {
+ init_readdir_impl();
+ rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
+ }
+
+ if (rdimpl == &readdir_r_impl)
+ {
+ // According to readdir description, there's no reliable way to predict the length of the d_name string.
+ // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
+ // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
+ // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
+ // in favor of readdir.
+ extra_size = (sizeof(dirent) - sizeof(dirent().d_name)) + path_max() + 1u; // + 1 for "\0"
+ }
+ }
+#endif // defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+ boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (extra_size) detail::dir_itr_imp());
+ if (BOOST_UNLIKELY(!pimpl))
+ return make_error_code(system::errc::not_enough_memory);
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ boost::scope::unique_fd fd;
+ if (params && params->dir_fd)
+ {
+ fd = std::move(params->dir_fd);
+ }
+ else
+ {
+ system::error_code ec;
+ fd = open_directory(dir, opts, ec);
+ if (BOOST_UNLIKELY(!!ec))
+ return ec;
+ }
+
+ pimpl->handle = ::fdopendir(fd.get());
+ if (BOOST_UNLIKELY(!pimpl->handle))
+ {
+ const int err = errno;
+ return system::error_code(err, system::system_category());
+ }
+
+ // At this point fd will be closed by closedir
+ fd.release();
+#else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ pimpl->handle = ::opendir(dir.c_str());
+ if (BOOST_UNLIKELY(!pimpl->handle))
+ {
+ const int err = errno;
+ return system::error_code(err, system::system_category());
+ }
+#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+ // Force initial readdir call by the caller. This will initialize the actual first filename and statuses.
+ first_filename.assign(".");
+
+ imp.swap(pimpl);
+ return system::error_code();
+}
+
+BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT;
+
+#else // BOOST_WINDOWS_API
+
+inline void set_file_statuses(DWORD attrs, const ULONG* reparse_point_tag, fs::path const& filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ {
+ // Reparse points are complex, so don't try to resolve them here; instead just mark
+ // them as status_error which causes directory_entry caching to call status()
+ // and symlink_status() which do handle reparse points fully
+ if (reparse_point_tag)
+ {
+ // If we have a reparse point tag we can at least populate the symlink status,
+ // consistent with symlink_status() behavior
+ symlink_sf.type(is_reparse_point_tag_a_symlink(*reparse_point_tag) ? fs::symlink_file : fs::reparse_file);
+ symlink_sf.permissions(make_permissions(filename, attrs));
+ }
+ else
+ {
+ symlink_sf.type(fs::status_error);
+ }
+
+ sf.type(fs::status_error);
+ }
+ else
+ {
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
+ {
+ sf.type(fs::directory_file);
+ symlink_sf.type(fs::directory_file);
+ }
+ else
+ {
+ sf.type(fs::regular_file);
+ symlink_sf.type(fs::regular_file);
+ }
+
+ sf.permissions(make_permissions(filename, attrs));
+ symlink_sf.permissions(sf.permissions());
+ }
+}
+
+//! FILE_ID_128 definition from Windows SDK
+struct file_id_128
+{
+ BYTE Identifier[16];
+};
+
+//! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably).
+struct file_directory_information
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+};
+
+//! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista.
+struct file_id_both_dir_info
+{
+ DWORD NextEntryOffset;
+ DWORD FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ DWORD FileAttributes;
+ DWORD FileNameLength;
+ DWORD EaSize;
+ CCHAR ShortNameLength;
+ WCHAR ShortName[12];
+ LARGE_INTEGER FileId;
+ WCHAR FileName[1];
+};
+
+//! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8.
+struct file_full_dir_info
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ WCHAR FileName[1];
+};
+
+//! FILE_ID_EXTD_DIR_INFO definition from Windows SDK. Provides reparse point tag, which saves us querying it with a few separate syscalls. Supported since Windows 8.
+struct file_id_extd_dir_info
+{
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ ULONG ReparsePointTag;
+ file_id_128 FileId;
+ WCHAR FileName[1];
+};
+
+//! Indicates format of the extra data in the directory iterator
+enum extra_data_format
+{
+ file_directory_information_format,
+ file_id_both_dir_info_format,
+ file_full_dir_info_format,
+ file_id_extd_dir_info_format
+};
+
+//! Indicates extra data format that should be used by directory iterator by default
+extra_data_format g_extra_data_format = file_directory_information_format;
+
+/*!
+ * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator.
+ *
+ * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename.
+ * NTFS, VFAT, exFAT and ReFS support filenames up to 255 UTF-16/UCS-2 characters. (For ReFS, there is no information
+ * on the on-disk format, and it is possible that it supports longer filenames, up to 32768 UTF-16/UCS-2 characters.)
+ * The buffer cannot be larger than 64k, otherwise up to Windows 8.1, NtQueryDirectoryFile and GetFileInformationByHandleEx
+ * fail with ERROR_INVALID_PARAMETER when trying to retrieve filenames from a network share.
+ */
+BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = 65536u;
+
+inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept
+{
+ imp.extra_data_format = 0u;
+ imp.current_offset = 0u;
+
+ if (imp.handle != nullptr)
+ {
+ if (BOOST_LIKELY(imp.close_handle))
+ ::CloseHandle(imp.handle);
+ imp.handle = nullptr;
+ }
+
+ return error_code();
+}
+
+system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ void* extra_data = get_dir_itr_imp_extra_data(&imp);
+ const void* current_data = static_cast< const unsigned char* >(extra_data) + imp.current_offset;
+ switch (imp.extra_data_format)
+ {
+ case file_id_extd_dir_info_format:
+ {
+ const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_extd_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_id_extd_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_id_extd_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_full_dir_info_format:
+ {
+ const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_full_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_full_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_id_both_dir_info_format:
+ {
+ const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_both_directory_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ dir_itr_close(imp);
+ if (error == ERROR_NO_MORE_FILES)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_id_both_dir_info* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_id_both_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
+ }
+ break;
+
+ default:
+ {
+ const file_directory_information* data = static_cast< const file_directory_information* >(current_data);
+ if (data->NextEntryOffset == 0u)
+ {
+ io_status_block iosb;
+ boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api)
+ (
+ imp.handle,
+ nullptr, // Event
+ nullptr, // ApcRoutine
+ nullptr, // ApcContext
+ &iosb,
+ extra_data,
+ dir_itr_extra_size,
+ file_directory_information_class,
+ FALSE, // ReturnSingleEntry
+ nullptr, // FileName
+ FALSE // RestartScan
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ dir_itr_close(imp);
+ if (status == STATUS_NO_MORE_FILES)
+ goto done;
+
+ return system::error_code(translate_ntstatus(status), system::system_category());
+ }
+
+ imp.current_offset = 0u;
+ data = static_cast< const file_directory_information* >(extra_data);
+ }
+ else
+ {
+ imp.current_offset += data->NextEntryOffset;
+ data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
+ }
+
+ filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+ set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
+ }
+ break;
+ }
+
+done:
+ return system::error_code();
+}
+
+//! Returns \c true if the error code indicates that the OS or the filesystem does not support a particular directory info class
+inline bool is_dir_info_class_not_supported(DWORD error)
+{
+ // Some mounted filesystems may not support FILE_ID_128 identifiers, which will cause
+ // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER,
+ // even though in general the operation is supported by the kernel. SMBv1 returns a special error
+ // code ERROR_INVALID_LEVEL in this case.
+ // Some other filesystems also don't implement other info classes and return ERROR_INVALID_PARAMETER
+ // (e.g. see https://github.com/boostorg/filesystem/issues/266), ERROR_GEN_FAILURE, ERROR_INVALID_FUNCTION
+ // or ERROR_INTERNAL_ERROR (https://github.com/boostorg/filesystem/issues/286). Treat these error codes
+ // as "non-permanent", even though ERROR_INVALID_PARAMETER is also returned if GetFileInformationByHandleEx
+ // in general does not support a certain info class. Worst case, we will make extra syscalls on directory
+ // iterator construction.
+ // Also note that Wine returns ERROR_CALL_NOT_IMPLEMENTED for unimplemented info classes, and
+ // up until 7.21 it didn't implement FileIdExtdDirectoryRestartInfo and FileFullDirectoryRestartInfo.
+ // (https://bugs.winehq.org/show_bug.cgi?id=53590)
+ return error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER ||
+ error == ERROR_INVALID_LEVEL || error == ERROR_CALL_NOT_IMPLEMENTED ||
+ error == ERROR_GEN_FAILURE || error == ERROR_INVALID_FUNCTION ||
+ error == ERROR_INTERNAL_ERROR;
+}
+
+system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
+{
+ boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp());
+ if (BOOST_UNLIKELY(!pimpl))
+ return make_error_code(system::errc::not_enough_memory);
+
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
+
+ unique_handle h;
+ HANDLE iterator_handle;
+ bool close_handle = true;
+ if (params != nullptr && params->dir_handle != INVALID_HANDLE_VALUE)
+ {
+ // Operate on externally provided handle, which must be a directory handle
+ iterator_handle = params->dir_handle;
+ close_handle = params->close_handle;
+ }
+ else
+ {
+ DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if ((opts & directory_options::_detail_no_follow) != directory_options::none)
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+ h = create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, flags);
+ if (BOOST_UNLIKELY(!h))
+ {
+ return_last_error:
+ DWORD error = ::GetLastError();
+ return system::error_code(error, system::system_category());
+ }
+
+ iterator_handle = h.get();
+
+ if (BOOST_LIKELY(get_file_information_by_handle_ex != nullptr))
+ {
+ file_attribute_tag_info info;
+ BOOL res = get_file_information_by_handle_ex(iterator_handle, file_attribute_tag_info_class, &info, sizeof(info));
+ if (BOOST_UNLIKELY(!res))
+ {
+ // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER. See the comment in symlink_status.
+ DWORD error = ::GetLastError();
+ if (error == ERROR_INVALID_PARAMETER || error == ERROR_NOT_SUPPORTED)
+ goto use_get_file_information_by_handle;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
+ return make_error_code(system::errc::not_a_directory);
+
+ if ((opts & directory_options::_detail_no_follow) != directory_options::none)
+ {
+ if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag))
+ return make_error_code(system::errc::too_many_symbolic_link_levels);
+ }
+ }
+ else
+ {
+ use_get_file_information_by_handle:
+ BY_HANDLE_FILE_INFORMATION info;
+ BOOL res = ::GetFileInformationByHandle(iterator_handle, &info);
+ if (BOOST_UNLIKELY(!res))
+ goto return_last_error;
+
+ if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
+ return make_error_code(system::errc::not_a_directory);
+
+ if ((opts & directory_options::_detail_no_follow) != directory_options::none && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ {
+ error_code ec;
+ const ULONG reparse_point_tag = detail::get_reparse_point_tag_ioctl(iterator_handle, dir, &ec);
+ if (BOOST_UNLIKELY(!!ec))
+ return ec;
+
+ if (detail::is_reparse_point_tag_a_symlink(reparse_point_tag))
+ return make_error_code(system::errc::too_many_symbolic_link_levels);
+ }
+ }
+ }
+
+ void* extra_data = get_dir_itr_imp_extra_data(pimpl.get());
+ switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format))
+ {
+ case file_id_extd_dir_info_format:
+ {
+ if (!get_file_information_by_handle_ex(iterator_handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (is_dir_info_class_not_supported(error))
+ {
+ // Fall back to file_full_dir_info_format.
+ if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format);
+ goto fallback_to_file_full_dir_info_format;
+ }
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ pimpl->extra_data_format = file_id_extd_dir_info_format;
+
+ const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_full_dir_info_format:
+ fallback_to_file_full_dir_info_format:
+ {
+ if (!get_file_information_by_handle_ex(iterator_handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (is_dir_info_class_not_supported(error))
+ {
+ // Fall back to file_id_both_dir_info
+ if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format);
+ goto fallback_to_file_id_both_dir_info_format;
+ }
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ pimpl->extra_data_format = file_full_dir_info_format;
+
+ const file_full_dir_info* data = static_cast< const file_full_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ case file_id_both_dir_info_format:
+ fallback_to_file_id_both_dir_info_format:
+ {
+ if (!get_file_information_by_handle_ex(iterator_handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size))
+ {
+ DWORD error = ::GetLastError();
+
+ if (is_dir_info_class_not_supported(error))
+ {
+ // Fall back to file_directory_information
+ if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_directory_information_format);
+ goto fallback_to_file_directory_information_format;
+ }
+
+ if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ return system::error_code(error, system::system_category());
+ }
+
+ pimpl->extra_data_format = file_id_both_dir_info_format;
+
+ const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
+ }
+ break;
+
+ default:
+ fallback_to_file_directory_information_format:
+ {
+ NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api);
+ if (BOOST_UNLIKELY(!nt_query_directory_file))
+ return error_code(ERROR_NOT_SUPPORTED, system_category());
+
+ io_status_block iosb;
+ boost::winapi::NTSTATUS_ status = nt_query_directory_file
+ (
+ iterator_handle,
+ nullptr, // Event
+ nullptr, // ApcRoutine
+ nullptr, // ApcContext
+ &iosb,
+ extra_data,
+ dir_itr_extra_size,
+ file_directory_information_class,
+ FALSE, // ReturnSingleEntry
+ nullptr, // FileName
+ TRUE // RestartScan
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ // Note: an empty root directory has no "." or ".." entries, so this
+ // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW
+ // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we
+ // do not consider an error. It is treated as eof instead.
+ if (status == STATUS_NO_MORE_FILES || status == STATUS_NO_SUCH_FILE)
+ goto done;
+
+ return error_code(translate_ntstatus(status), system_category());
+ }
+
+ pimpl->extra_data_format = file_directory_information_format;
+
+ const file_directory_information* data = static_cast< const file_directory_information* >(extra_data);
+ first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
+
+ set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
+ }
+ break;
+ }
+
+ pimpl->handle = iterator_handle;
+ h.release();
+ pimpl->close_handle = close_handle;
+
+done:
+ imp.swap(pimpl);
+ return system::error_code();
+}
+
+BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND;
+
+#endif // BOOST_WINDOWS_API
+
+} // namespace
+
+#if defined(BOOST_POSIX_API)
+
+//! Tests if the directory is empty
+bool is_empty_directory(boost::scope::unique_fd&& fd, path const& p, error_code* ec)
+{
+#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+ // Use a more optimal implementation without the overhead of constructing the iterator state
+
+ struct closedir_deleter
+ {
+ using result_type = void;
+ result_type operator() (DIR* dir) const noexcept
+ {
+ ::closedir(dir);
+ }
+ };
+
+ int err;
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ std::unique_ptr< DIR, closedir_deleter > dir(::fdopendir(fd.get()));
+ if (BOOST_UNLIKELY(!dir))
+ {
+ err = errno;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::is_empty");
+ return false;
+ }
+
+ // At this point fd will be closed by closedir
+ fd.release();
+#else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ std::unique_ptr< DIR, closedir_deleter > dir(::opendir(p.c_str()));
+ if (BOOST_UNLIKELY(!dir))
+ {
+ err = errno;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::is_empty");
+ return false;
+ }
+#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+ while (true)
+ {
+ errno = 0;
+ struct dirent* const ent = ::readdir(dir.get());
+ if (!ent)
+ {
+ err = errno;
+ if (err != 0)
+ goto fail;
+
+ return true;
+ }
+
+ // Skip dot and dot-dot entries
+ if (!(ent->d_name[0] == path::dot
+ && (ent->d_name[1] == static_cast< path::string_type::value_type >('\0') ||
+ (ent->d_name[1] == path::dot && ent->d_name[2] == static_cast< path::string_type::value_type >('\0')))))
+ {
+ return false;
+ }
+ }
+
+#else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+
+ filesystem::directory_iterator itr;
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ filesystem::detail::directory_iterator_params params{ std::move(fd) };
+ filesystem::detail::directory_iterator_construct(itr, p, directory_options::none, &params, ec);
+#else
+ filesystem::detail::directory_iterator_construct(itr, p, directory_options::none, nullptr, ec);
+#endif
+ return itr == filesystem::directory_iterator();
+
+#endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
+}
+
+#else // BOOST_WINDOWS_API
+
+//! Tests if the directory is empty
+bool is_empty_directory(unique_handle&& h, path const& p, error_code* ec)
+{
+ filesystem::directory_iterator itr;
+ filesystem::detail::directory_iterator_params params{ h.get(), false };
+ filesystem::detail::directory_iterator_construct(itr, p, directory_options::none, &params, ec);
+ return itr == filesystem::directory_iterator();
+}
+
+//! Initializes directory iterator implementation
+void init_directory_iterator_impl() noexcept
+{
+ if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != nullptr)
+ {
+ // Enable the latest format we support. It will get downgraded, if needed, as we attempt
+ // to create the directory iterator the first time.
+ filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_extd_dir_info_format);
+ }
+}
+
+#endif // defined(BOOST_WINDOWS_API)
+
+BOOST_FILESYSTEM_DECL
+dir_itr_imp::~dir_itr_imp() noexcept
+{
+ dir_itr_close(*this);
+}
+
+BOOST_FILESYSTEM_DECL
+void directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, directory_iterator_params* params, system::error_code* ec)
+{
+ // At most one of the two options may be specified, and follow_directory_symlink is ignored for directory_iterator.
+ BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow));
+
+ if (BOOST_UNLIKELY(p.empty()))
+ {
+ emit_error(not_found_error_code, p, ec, "boost::filesystem::directory_iterator::construct");
+ return;
+ }
+
+ if (ec)
+ ec->clear();
+
+ try
+ {
+ boost::intrusive_ptr< detail::dir_itr_imp > imp;
+ path filename;
+ file_status file_stat, symlink_file_stat;
+ system::error_code result = dir_itr_create(imp, p, opts, params, filename, file_stat, symlink_file_stat);
+
+ while (true)
+ {
+ if (result)
+ {
+ if (result != make_error_condition(system::errc::permission_denied) ||
+ (opts & directory_options::skip_permission_denied) == directory_options::none)
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p, result));
+ *ec = result;
+ }
+
+ return;
+ }
+
+ if (imp->handle == nullptr) // eof, make end
+ return;
+
+ // Not eof
+ const path::string_type::value_type* filename_str = filename.c_str();
+ if (!(filename_str[0] == path::dot // dot or dot-dot
+ && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
+ (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
+ {
+ path full_path(p);
+ path_algorithms::append_v4(full_path, filename);
+ imp->dir_entry.assign_with_status
+ (
+ static_cast< path&& >(full_path),
+ file_stat,
+ symlink_file_stat
+ );
+ it.m_imp.swap(imp);
+ return;
+ }
+
+ // If dot or dot-dot name produced by the underlying API, skip it until the first actual file
+ result = dir_itr_increment(*imp, filename, file_stat, symlink_file_stat);
+ }
+ }
+ catch (std::bad_alloc&)
+ {
+ if (!ec)
+ throw;
+
+ *ec = make_error_code(system::errc::not_enough_memory);
+ it.m_imp.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL
+void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
+{
+ BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
+
+ if (ec)
+ ec->clear();
+
+ try
+ {
+ path filename;
+ file_status file_stat, symlink_file_stat;
+ system::error_code increment_ec;
+
+ while (true)
+ {
+ increment_ec = dir_itr_increment(*it.m_imp, filename, file_stat, symlink_file_stat);
+
+ if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
+ {
+ boost::intrusive_ptr< detail::dir_itr_imp > imp;
+ imp.swap(it.m_imp);
+ path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++", error_path, increment_ec));
+
+ *ec = increment_ec;
+ return;
+ }
+
+ if (it.m_imp->handle == nullptr) // eof, make end
+ {
+ it.m_imp.reset();
+ return;
+ }
+
+ const path::string_type::value_type* filename_str = filename.c_str();
+ if (!(filename_str[0] == path::dot // !(dot or dot-dot)
+ && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
+ (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
+ {
+ it.m_imp->dir_entry.replace_filename_with_status(filename, file_stat, symlink_file_stat);
+ return;
+ }
+ }
+ }
+ catch (std::bad_alloc&)
+ {
+ if (!ec)
+ throw;
+
+ it.m_imp.reset();
+ *ec = make_error_code(system::errc::not_enough_memory);
+ }
+}
+
+//--------------------------------------------------------------------------------------//
+// //
+// recursive_directory_iterator //
+// //
+//--------------------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL
+void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, directory_options opts, system::error_code* ec)
+{
+ // At most one of the two options may be specified
+ BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow));
+
+ if (ec)
+ ec->clear();
+
+ directory_iterator dir_it;
+ detail::directory_iterator_construct(dir_it, dir_path, opts, nullptr, ec);
+ if ((ec && *ec) || dir_it == directory_iterator())
+ return;
+
+ boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
+ if (!ec)
+ {
+ imp = new detail::recur_dir_itr_imp(opts);
+ }
+ else
+ {
+ imp = new (std::nothrow) detail::recur_dir_itr_imp(opts);
+ if (BOOST_UNLIKELY(!imp))
+ {
+ *ec = make_error_code(system::errc::not_enough_memory);
+ return;
+ }
+ }
+
+ try
+ {
+ imp->m_stack.push_back(std::move(dir_it));
+ it.m_imp.swap(imp);
+ }
+ catch (std::bad_alloc&)
+ {
+ if (ec)
+ {
+ *ec = make_error_code(system::errc::not_enough_memory);
+ return;
+ }
+
+ throw;
+ }
+}
+
+namespace {
+
+void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
+{
+ imp->m_stack.pop_back();
+
+ while (!imp->m_stack.empty())
+ {
+ directory_iterator& dir_it = imp->m_stack.back();
+ system::error_code increment_ec;
+ detail::directory_iterator_increment(dir_it, &increment_ec);
+ if (!increment_ec && dir_it != directory_iterator())
+ break;
+
+ imp->m_stack.pop_back();
+ }
+}
+
+} // namespace
+
+BOOST_FILESYSTEM_DECL
+void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
+{
+ BOOST_ASSERT_MSG(!it.is_end(), "pop() on end recursive_directory_iterator");
+ detail::recur_dir_itr_imp* const imp = it.m_imp.get();
+
+ if (ec)
+ ec->clear();
+
+ imp->m_stack.pop_back();
+
+ while (true)
+ {
+ if (imp->m_stack.empty())
+ {
+ it.m_imp.reset(); // done, so make end iterator
+ break;
+ }
+
+ directory_iterator& dir_it = imp->m_stack.back();
+ system::error_code increment_ec;
+ detail::directory_iterator_increment(dir_it, &increment_ec);
+ if (BOOST_UNLIKELY(!!increment_ec))
+ {
+ if ((imp->m_options & directory_options::pop_on_error) == directory_options::none)
+ {
+ // Make an end iterator on errors
+ it.m_imp.reset();
+ }
+ else
+ {
+ recursive_directory_iterator_pop_on_error(imp);
+
+ if (imp->m_stack.empty())
+ it.m_imp.reset(); // done, so make end iterator
+ }
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec));
+
+ *ec = increment_ec;
+ return;
+ }
+
+ if (dir_it != directory_iterator())
+ break;
+
+ imp->m_stack.pop_back();
+ }
+}
+
+BOOST_FILESYSTEM_DECL
+void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
+{
+ enum push_directory_result : unsigned int
+ {
+ directory_not_pushed = 0u,
+ directory_pushed = 1u,
+ keep_depth = 1u << 1u
+ };
+
+ struct local
+ {
+ //! Attempts to descend into a directory
+ static push_directory_result push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec)
+ {
+ push_directory_result result = directory_not_pushed;
+ try
+ {
+ // Discover if the iterator is for a directory that needs to be recursed into,
+ // taking symlinks and options into account.
+
+ if ((imp->m_options & directory_options::_detail_no_push) != directory_options::none)
+ {
+ imp->m_options &= ~directory_options::_detail_no_push;
+ return result;
+ }
+
+ file_type symlink_ft = status_error;
+
+#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ int parentdir_fd = -1;
+ path dir_it_filename;
+#elif defined(BOOST_WINDOWS_API)
+ unique_handle direntry_handle;
+#endif
+
+ // If we are not recursing into symlinks, we are going to have to know if the
+ // stack top is a symlink, so get symlink_status and verify no error occurred.
+ if ((imp->m_options & directory_options::follow_directory_symlink) == directory_options::none ||
+ (imp->m_options & directory_options::skip_dangling_symlinks) != directory_options::none)
+ {
+#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ directory_iterator const& dir_it = imp->m_stack.back();
+ if (filesystem::type_present(dir_it->m_symlink_status))
+ {
+ symlink_ft = dir_it->m_symlink_status.type();
+ }
+ else
+ {
+ parentdir_fd = dir_itr_fd(*dir_it.m_imp, ec);
+ if (ec)
+ return result;
+
+ dir_it_filename = detail::path_algorithms::filename_v4(dir_it->path());
+
+ symlink_ft = detail::symlink_status_impl(dir_it_filename, &ec, parentdir_fd).type();
+ if (ec)
+ return result;
+ }
+#elif defined(BOOST_WINDOWS_API)
+ directory_iterator const& dir_it = imp->m_stack.back();
+ if (filesystem::type_present(dir_it->m_symlink_status))
+ {
+ symlink_ft = dir_it->m_symlink_status.type();
+ }
+ else
+ {
+ boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
+ (
+ direntry_handle,
+ static_cast< HANDLE >(dir_it.m_imp->handle),
+ detail::path_algorithms::filename_v4(dir_it->path()),
+ 0u, // FileAttributes
+ FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ symlink_ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type();
+ }
+ else if (status == STATUS_NOT_IMPLEMENTED)
+ {
+ symlink_ft = dir_it->symlink_file_type(ec);
+ }
+ else
+ {
+ if (!not_found_ntstatus(status))
+ ec.assign(translate_ntstatus(status), system::system_category());
+
+ return result;
+ }
+
+ if (ec)
+ return result;
+ }
+#else
+ symlink_ft = imp->m_stack.back()->symlink_file_type(ec);
+ if (ec)
+ return result;
+#endif
+ }
+
+ // Logic for following predicate was contributed by Daniel Aarno to handle cyclic
+ // symlinks correctly and efficiently, fixing ticket #5652.
+ // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink
+ // || !is_symlink(m_stack.back()->symlink_status()))
+ // && is_directory(m_stack.back()->status())) ...
+ // The predicate code has since been rewritten to pass error_code arguments,
+ // per ticket #5653.
+
+ if ((imp->m_options & directory_options::follow_directory_symlink) != directory_options::none || symlink_ft != symlink_file)
+ {
+ directory_iterator const& dir_it = imp->m_stack.back();
+
+ // Don't query the file type from the filesystem yet, if not known. We will use dir_it for that below.
+ file_type ft = dir_it->m_status.type();
+ if (ft != status_error && ft != directory_file)
+ return result;
+
+#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ if (parentdir_fd < 0)
+ {
+ parentdir_fd = dir_itr_fd(*dir_it.m_imp, ec);
+ if (ec)
+ return result;
+
+ dir_it_filename = detail::path_algorithms::filename_v4(dir_it->path());
+ }
+
+ // Try to open the file as a directory right away. This effectively tests whether the file is a directory, and, if it is, opens the directory in one system call.
+ detail::directory_iterator_params params{ detail::openat_directory(parentdir_fd, dir_it_filename, imp->m_options, ec) };
+ if (!!ec)
+ {
+ if
+ (
+ // Skip non-directory files
+ ec == system::error_code(ENOTDIR, system::system_category()) ||
+ (
+ // Skip dangling symlink, if requested by options
+ ec == system::error_code(ENOENT, system::system_category()) && symlink_ft == symlink_file &&
+ (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)
+ )
+ )
+ {
+ ec.clear();
+ }
+
+ return result;
+ }
+#else // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+#if defined(BOOST_WINDOWS_API)
+ if (!!direntry_handle && symlink_ft == symlink_file)
+ {
+ // Close the symlink to reopen the target file below
+ direntry_handle.reset();
+ }
+
+ if (!direntry_handle)
+ {
+ boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
+ (
+ direntry_handle,
+ static_cast< HANDLE >(dir_it.m_imp->handle),
+ detail::path_algorithms::filename_v4(dir_it->path()),
+ 0u, // FileAttributes
+ FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ goto get_file_type_by_handle;
+ }
+ else if (status == STATUS_NOT_IMPLEMENTED)
+ {
+ ft = dir_it->file_type(ec);
+ }
+ else
+ {
+ ec.assign(translate_ntstatus(status), system::system_category());
+ }
+ }
+ else
+ {
+ get_file_type_by_handle:
+ ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type();
+ }
+#else // defined(BOOST_WINDOWS_API)
+ if (ft == status_error)
+ ft = dir_it->file_type(ec);
+#endif // defined(BOOST_WINDOWS_API)
+
+ if (BOOST_UNLIKELY(!!ec))
+ {
+ if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file &&
+ (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
+ {
+ // Skip dangling symlink and continue iteration on the current depth level
+ ec.clear();
+ }
+
+ return result;
+ }
+
+ if (ft != directory_file)
+ return result;
+#endif // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+ if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))
+ {
+ // We cannot let depth to overflow
+ ec = make_error_code(system::errc::value_too_large);
+ // When depth overflow happens, avoid popping the current directory iterator
+ // and attempt to continue iteration on the current depth.
+ result = keep_depth;
+ return result;
+ }
+
+#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ directory_iterator next;
+ detail::directory_iterator_construct(next, dir_it->path(), imp->m_options, &params, &ec);
+#elif defined(BOOST_WINDOWS_API)
+ detail::directory_iterator_params params;
+ params.dir_handle = direntry_handle.get();
+ params.close_handle = true;
+ directory_iterator next;
+ detail::directory_iterator_construct(next, dir_it->path(), imp->m_options, &params, &ec);
+#else
+ directory_iterator next(dir_it->path(), imp->m_options, ec);
+#endif
+ if (BOOST_LIKELY(!ec))
+ {
+#if defined(BOOST_WINDOWS_API)
+ direntry_handle.release();
+#endif
+ if (!next.is_end())
+ {
+ imp->m_stack.push_back(std::move(next)); // may throw
+ return directory_pushed;
+ }
+ }
+ }
+ }
+ catch (std::bad_alloc&)
+ {
+ ec = make_error_code(system::errc::not_enough_memory);
+ }
+
+ return result;
+ }
+ };
+
+ BOOST_ASSERT_MSG(!it.is_end(), "increment() on end recursive_directory_iterator");
+ detail::recur_dir_itr_imp* const imp = it.m_imp.get();
+
+ if (ec)
+ ec->clear();
+
+ system::error_code local_ec;
+
+ // if various conditions are met, push a directory_iterator into the iterator stack
+ push_directory_result push_result = local::push_directory(imp, local_ec);
+ if (push_result == directory_pushed)
+ return;
+
+ // report errors if any
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ on_error:
+ if ((imp->m_options & directory_options::pop_on_error) == directory_options::none)
+ {
+ // Make an end iterator on errors
+ it.m_imp.reset();
+ }
+ else
+ {
+ if ((push_result & keep_depth) != 0u)
+ {
+ system::error_code increment_ec;
+ directory_iterator& dir_it = imp->m_stack.back();
+ detail::directory_iterator_increment(dir_it, &increment_ec);
+ if (!increment_ec && !dir_it.is_end())
+ goto on_error_return;
+ }
+
+ recursive_directory_iterator_pop_on_error(imp);
+
+ if (imp->m_stack.empty())
+ it.m_imp.reset(); // done, so make end iterator
+ }
+
+ on_error_return:
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec));
+
+ *ec = local_ec;
+ return;
+ }
+
+ // Do the actual increment operation on the top iterator in the iterator
+ // stack, popping the stack if necessary, until either the stack is empty or a
+ // non-end iterator is reached.
+ while (true)
+ {
+ if (imp->m_stack.empty())
+ {
+ it.m_imp.reset(); // done, so make end iterator
+ break;
+ }
+
+ directory_iterator& dir_it = imp->m_stack.back();
+ detail::directory_iterator_increment(dir_it, &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ goto on_error;
+
+ if (!dir_it.is_end())
+ break;
+
+ imp->m_stack.pop_back();
+ }
+}
+
+} // namespace detail
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/error_handling.hpp b/contrib/restricted/boost/filesystem/src/error_handling.hpp
new file mode 100644
index 0000000000..a83d92e976
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/error_handling.hpp
@@ -0,0 +1,237 @@
+// error_handling.hpp --------------------------------------------------------------------//
+
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2019 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_ERROR_HANDLING_HPP_
+#define BOOST_FILESYSTEM_SRC_ERROR_HANDLING_HPP_
+
+#include <cerrno>
+#include <boost/system/error_code.hpp>
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/exception.hpp>
+
+#if defined(BOOST_WINDOWS_API)
+#include <boost/winapi/basic_types.hpp>
+#include <boost/winapi/get_last_error.hpp>
+#include <boost/winapi/error_codes.hpp>
+#endif
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+#if defined(BOOST_POSIX_API)
+
+typedef int err_t;
+
+// POSIX uses a 0 return to indicate success
+#define BOOST_ERRNO errno
+
+#define BOOST_ERROR_FILE_NOT_FOUND ENOENT
+#define BOOST_ERROR_ALREADY_EXISTS EEXIST
+#define BOOST_ERROR_NOT_SUPPORTED ENOSYS
+
+#else
+
+typedef boost::winapi::DWORD_ err_t;
+
+// Windows uses a non-0 return to indicate success
+#define BOOST_ERRNO boost::winapi::GetLastError()
+
+#define BOOST_ERROR_FILE_NOT_FOUND boost::winapi::ERROR_FILE_NOT_FOUND_
+#define BOOST_ERROR_ALREADY_EXISTS boost::winapi::ERROR_ALREADY_EXISTS_
+#define BOOST_ERROR_NOT_SUPPORTED boost::winapi::ERROR_NOT_SUPPORTED_
+
+// Note: Legacy MinGW doesn't have ntstatus.h and doesn't define NTSTATUS error codes other than STATUS_SUCCESS.
+#if !defined(NT_SUCCESS)
+#define NT_SUCCESS(Status) (((boost::winapi::NTSTATUS_)(Status)) >= 0)
+#endif
+#if !defined(STATUS_SUCCESS)
+#define STATUS_SUCCESS ((boost::winapi::NTSTATUS_)0x00000000l)
+#endif
+#if !defined(STATUS_NOT_IMPLEMENTED)
+#define STATUS_NOT_IMPLEMENTED ((boost::winapi::NTSTATUS_)0xC0000002l)
+#endif
+#if !defined(STATUS_INVALID_INFO_CLASS)
+#define STATUS_INVALID_INFO_CLASS ((boost::winapi::NTSTATUS_)0xC0000003l)
+#endif
+#if !defined(STATUS_INVALID_HANDLE)
+#define STATUS_INVALID_HANDLE ((boost::winapi::NTSTATUS_)0xC0000008l)
+#endif
+#if !defined(STATUS_INVALID_PARAMETER)
+#define STATUS_INVALID_PARAMETER ((boost::winapi::NTSTATUS_)0xC000000Dl)
+#endif
+#if !defined(STATUS_NO_SUCH_DEVICE)
+#define STATUS_NO_SUCH_DEVICE ((boost::winapi::NTSTATUS_)0xC000000El)
+#endif
+#if !defined(STATUS_NO_SUCH_FILE)
+#define STATUS_NO_SUCH_FILE ((boost::winapi::NTSTATUS_)0xC000000Fl)
+#endif
+#if !defined(STATUS_NO_MORE_FILES)
+#define STATUS_NO_MORE_FILES ((boost::winapi::NTSTATUS_)0x80000006l)
+#endif
+#if !defined(STATUS_BUFFER_OVERFLOW)
+#define STATUS_BUFFER_OVERFLOW ((boost::winapi::NTSTATUS_)0x80000005l)
+#endif
+#if !defined(STATUS_NO_MEMORY)
+#define STATUS_NO_MEMORY ((boost::winapi::NTSTATUS_)0xC0000017l)
+#endif
+#if !defined(STATUS_ACCESS_DENIED)
+#define STATUS_ACCESS_DENIED ((boost::winapi::NTSTATUS_)0xC0000022l)
+#endif
+#if !defined(STATUS_OBJECT_NAME_NOT_FOUND)
+#define STATUS_OBJECT_NAME_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC0000034l)
+#endif
+#if !defined(STATUS_OBJECT_PATH_NOT_FOUND)
+#define STATUS_OBJECT_PATH_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC000003Al)
+#endif
+#if !defined(STATUS_SHARING_VIOLATION)
+#define STATUS_SHARING_VIOLATION ((boost::winapi::NTSTATUS_)0xC0000043l)
+#endif
+#if !defined(STATUS_EAS_NOT_SUPPORTED)
+#define STATUS_EAS_NOT_SUPPORTED ((boost::winapi::NTSTATUS_)0xC000004Fl)
+#endif
+#if !defined(STATUS_NOT_SUPPORTED)
+#define STATUS_NOT_SUPPORTED ((boost::winapi::NTSTATUS_)0xC00000BBl)
+#endif
+#if !defined(STATUS_BAD_NETWORK_PATH)
+#define STATUS_BAD_NETWORK_PATH ((boost::winapi::NTSTATUS_)0xC00000BEl)
+#endif
+#if !defined(STATUS_DEVICE_DOES_NOT_EXIST)
+#define STATUS_DEVICE_DOES_NOT_EXIST ((boost::winapi::NTSTATUS_)0xC00000C0l)
+#endif
+#if !defined(STATUS_BAD_NETWORK_NAME)
+#define STATUS_BAD_NETWORK_NAME ((boost::winapi::NTSTATUS_)0xC00000CCl)
+#endif
+#if !defined(STATUS_DIRECTORY_NOT_EMPTY)
+#define STATUS_DIRECTORY_NOT_EMPTY ((boost::winapi::NTSTATUS_)0xC0000101l)
+#endif
+#if !defined(STATUS_NOT_A_DIRECTORY)
+#define STATUS_NOT_A_DIRECTORY ((boost::winapi::NTSTATUS_)0xC0000103l)
+#endif
+#if !defined(STATUS_NOT_FOUND)
+#define STATUS_NOT_FOUND ((boost::winapi::NTSTATUS_)0xC0000225l)
+#endif
+
+//! Converts NTSTATUS error codes to Win32 error codes for reporting
+inline boost::winapi::DWORD_ translate_ntstatus(boost::winapi::NTSTATUS_ status) noexcept
+{
+ // We have to cast to unsigned integral type to avoid signed overflow and narrowing conversion in the constants.
+ switch (static_cast< boost::winapi::ULONG_ >(status))
+ {
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_MEMORY):
+ return boost::winapi::ERROR_OUTOFMEMORY_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_BUFFER_OVERFLOW):
+ return boost::winapi::ERROR_BUFFER_OVERFLOW_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_HANDLE):
+ return boost::winapi::ERROR_INVALID_HANDLE_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_PARAMETER):
+ return boost::winapi::ERROR_INVALID_PARAMETER_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_MORE_FILES):
+ return boost::winapi::ERROR_NO_MORE_FILES_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_SUCH_DEVICE):
+ case static_cast< boost::winapi::ULONG_ >(STATUS_DEVICE_DOES_NOT_EXIST):
+ return boost::winapi::ERROR_DEV_NOT_EXIST_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NO_SUCH_FILE):
+ case static_cast< boost::winapi::ULONG_ >(STATUS_OBJECT_NAME_NOT_FOUND):
+ case static_cast< boost::winapi::ULONG_ >(STATUS_OBJECT_PATH_NOT_FOUND):
+ return boost::winapi::ERROR_FILE_NOT_FOUND_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_SHARING_VIOLATION):
+ return boost::winapi::ERROR_SHARING_VIOLATION_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_EAS_NOT_SUPPORTED):
+ return boost::winapi::ERROR_EAS_NOT_SUPPORTED_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_ACCESS_DENIED):
+ return boost::winapi::ERROR_ACCESS_DENIED_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_BAD_NETWORK_PATH):
+ return boost::winapi::ERROR_BAD_NETPATH_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_BAD_NETWORK_NAME):
+ return boost::winapi::ERROR_BAD_NET_NAME_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_DIRECTORY_NOT_EMPTY):
+ return boost::winapi::ERROR_DIR_NOT_EMPTY_;
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NOT_A_DIRECTORY):
+ return boost::winapi::ERROR_DIRECTORY_; // The directory name is invalid
+ case static_cast< boost::winapi::ULONG_ >(STATUS_NOT_FOUND):
+ return boost::winapi::ERROR_NOT_FOUND_;
+ // map "invalid info class" to "not supported" as this error likely indicates that the kernel does not support what we request
+ case static_cast< boost::winapi::ULONG_ >(STATUS_INVALID_INFO_CLASS):
+ default:
+ return boost::winapi::ERROR_NOT_SUPPORTED_;
+ }
+}
+
+//! Tests if the NTSTATUS indicates that the file is not found
+inline bool not_found_ntstatus(boost::winapi::NTSTATUS_ status) noexcept
+{
+ return status == STATUS_NO_SUCH_FILE || status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_OBJECT_PATH_NOT_FOUND ||
+ status == STATUS_BAD_NETWORK_PATH || status == STATUS_BAD_NETWORK_NAME;
+}
+
+#endif
+
+// error handling helpers ----------------------------------------------------------//
+
+// Implemented in exception.cpp
+void emit_error(err_t error_num, system::error_code* ec, const char* message);
+void emit_error(err_t error_num, path const& p, system::error_code* ec, const char* message);
+void emit_error(err_t error_num, path const& p1, path const& p2, system::error_code* ec, const char* message);
+
+inline bool error(err_t error_num, system::error_code* ec, const char* message)
+{
+ if (BOOST_LIKELY(!error_num))
+ {
+ if (ec)
+ ec->clear();
+ return false;
+ }
+ else
+ { // error
+ filesystem::emit_error(error_num, ec, message);
+ return true;
+ }
+}
+
+inline bool error(err_t error_num, path const& p, system::error_code* ec, const char* message)
+{
+ if (BOOST_LIKELY(!error_num))
+ {
+ if (ec)
+ ec->clear();
+ return false;
+ }
+ else
+ { // error
+ filesystem::emit_error(error_num, p, ec, message);
+ return true;
+ }
+}
+
+inline bool error(err_t error_num, path const& p1, path const& p2, system::error_code* ec, const char* message)
+{
+ if (BOOST_LIKELY(!error_num))
+ {
+ if (ec)
+ ec->clear();
+ return false;
+ }
+ else
+ { // error
+ filesystem::emit_error(error_num, p1, p2, ec, message);
+ return true;
+ }
+}
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_SRC_ERROR_HANDLING_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/exception.cpp b/contrib/restricted/boost/filesystem/src/exception.cpp
new file mode 100644
index 0000000000..0b92c0d9c1
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/exception.cpp
@@ -0,0 +1,188 @@
+// boost/filesystem/exception.hpp -----------------------------------------------------//
+
+// Copyright Beman Dawes 2003
+// Copyright Andrey Semashev 2019
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+#include "platform_config.hpp"
+
+#include <string>
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_category.hpp>
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/exception.hpp>
+
+#include "error_handling.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(const char* what_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl());
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(std::string const& what_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl());
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(const char* what_arg, path const& path1_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl(path1_arg));
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(std::string const& what_arg, path const& path1_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl(path1_arg));
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(const char* what_arg, path const& path1_arg, path const& path2_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl(path1_arg, path2_arg));
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(std::string const& what_arg, path const& path1_arg, path const& path2_arg, system::error_code ec) :
+ system::system_error(ec, what_arg)
+{
+ try
+ {
+ m_imp_ptr.reset(new impl(path1_arg, path2_arg));
+ }
+ catch (...)
+ {
+ m_imp_ptr.reset();
+ }
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::filesystem_error(filesystem_error const& that) :
+ system::system_error(static_cast< system::system_error const& >(that)),
+ m_imp_ptr(that.m_imp_ptr)
+{
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error& filesystem_error::operator=(filesystem_error const& that)
+{
+ static_cast< system::system_error& >(*this) = static_cast< system::system_error const& >(that);
+ m_imp_ptr = that.m_imp_ptr;
+ return *this;
+}
+
+BOOST_FILESYSTEM_DECL filesystem_error::~filesystem_error() noexcept
+{
+}
+
+BOOST_FILESYSTEM_DECL const char* filesystem_error::what() const noexcept
+{
+ if (m_imp_ptr.get()) try
+ {
+ if (m_imp_ptr->m_what.empty())
+ {
+ m_imp_ptr->m_what = system::system_error::what();
+ if (!m_imp_ptr->m_path1.empty())
+ {
+ m_imp_ptr->m_what += ": \"";
+ m_imp_ptr->m_what += m_imp_ptr->m_path1.string();
+ m_imp_ptr->m_what += "\"";
+ }
+ if (!m_imp_ptr->m_path2.empty())
+ {
+ m_imp_ptr->m_what += ", \"";
+ m_imp_ptr->m_what += m_imp_ptr->m_path2.string();
+ m_imp_ptr->m_what += "\"";
+ }
+ }
+
+ return m_imp_ptr->m_what.c_str();
+ }
+ catch (...)
+ {
+ m_imp_ptr->m_what.clear();
+ }
+
+ return system::system_error::what();
+}
+
+BOOST_FILESYSTEM_DECL path const& filesystem_error::get_empty_path() noexcept
+{
+ static const path empty_path;
+ return empty_path;
+}
+
+// error handling helpers declared in error_handling.hpp -----------------------------------------------------//
+
+void emit_error(err_t error_num, system::error_code* ec, const char* message)
+{
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error(message, system::error_code(error_num, system::system_category())));
+ else
+ ec->assign(error_num, system::system_category());
+}
+
+void emit_error(err_t error_num, path const& p, system::error_code* ec, const char* message)
+{
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error(message, p, system::error_code(error_num, system::system_category())));
+ else
+ ec->assign(error_num, system::system_category());
+}
+
+void emit_error(err_t error_num, path const& p1, path const& p2, system::error_code* ec, const char* message)
+{
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error(message, p1, p2, system::error_code(error_num, system::system_category())));
+ else
+ ec->assign(error_num, system::system_category());
+}
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/operations.cpp b/contrib/restricted/boost/filesystem/src/operations.cpp
new file mode 100644
index 0000000000..e2fa235451
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/operations.cpp
@@ -0,0 +1,5202 @@
+// operations.cpp --------------------------------------------------------------------//
+
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2018-2024 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/predef/os/bsd/open.h>
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/file_status.hpp>
+#include <boost/filesystem/exception.hpp>
+#include <boost/filesystem/directory.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/detail/workaround.hpp>
+#include <boost/core/bit.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/assert.hpp>
+#include <new> // std::bad_alloc, std::nothrow
+#include <limits>
+#include <memory>
+#include <string>
+#include <utility>
+#include <cstddef>
+#include <cstdlib> // for malloc, free
+#include <cstring>
+#include <cerrno>
+#include <stdio.h> // for rename
+
+// Default to POSIX under Emscripten
+// If BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI is set, use WASI instead
+#if defined(__wasm) && (!defined(__EMSCRIPTEN__) || defined(BOOST_FILESYSTEM_EMSCRIPTEN_USE_WASI))
+#define BOOST_FILESYSTEM_USE_WASI
+#endif
+
+#ifdef BOOST_POSIX_API
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(BOOST_FILESYSTEM_USE_WASI)
+// WASI does not have statfs or statvfs.
+#elif !defined(__APPLE__) && \
+ (!defined(__OpenBSD__) || BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(4, 4, 0)) && \
+ !defined(__ANDROID__) && \
+ !defined(__VXWORKS__)
+#include <sys/statvfs.h>
+#define BOOST_STATVFS statvfs
+#define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
+#else
+#ifdef __OpenBSD__
+#include <sys/param.h>
+#elif defined(__ANDROID__)
+#include <sys/vfs.h>
+#endif
+#if !defined(__VXWORKS__)
+#include <sys/mount.h>
+#endif
+#define BOOST_STATVFS statfs
+#define BOOST_STATVFS_F_FRSIZE static_cast< uintmax_t >(vfs.f_bsize)
+#endif // BOOST_STATVFS definition
+
+#include <unistd.h>
+#include <fcntl.h>
+#if !defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+#include <utime.h>
+#endif
+#include <limits.h>
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+
+#include <sys/vfs.h>
+#include <sys/utsname.h>
+#include <sys/syscall.h>
+#include <sys/sysmacros.h>
+#if !defined(BOOST_FILESYSTEM_DISABLE_SENDFILE)
+#include <sys/sendfile.h>
+#define BOOST_FILESYSTEM_USE_SENDFILE
+#endif // !defined(BOOST_FILESYSTEM_DISABLE_SENDFILE)
+#if !defined(BOOST_FILESYSTEM_DISABLE_COPY_FILE_RANGE) && defined(__NR_copy_file_range)
+#define BOOST_FILESYSTEM_USE_COPY_FILE_RANGE
+#endif // !defined(BOOST_FILESYSTEM_DISABLE_COPY_FILE_RANGE) && defined(__NR_copy_file_range)
+#if !defined(BOOST_FILESYSTEM_DISABLE_STATX) && (defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL))
+#if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
+#include <linux/stat.h>
+#endif
+#define BOOST_FILESYSTEM_USE_STATX
+#endif // !defined(BOOST_FILESYSTEM_DISABLE_STATX) && (defined(BOOST_FILESYSTEM_HAS_STATX) || defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL))
+
+#if defined(__has_include)
+#if __has_include(<linux/magic.h>)
+// This header was introduced in Linux kernel 2.6.19
+#include <linux/magic.h>
+#endif
+#endif
+
+// Some filesystem type magic constants are not defined in older kernel headers
+#ifndef PROC_SUPER_MAGIC
+#define PROC_SUPER_MAGIC 0x9fa0
+#endif
+#ifndef SYSFS_MAGIC
+#define SYSFS_MAGIC 0x62656572
+#endif
+#ifndef TRACEFS_MAGIC
+#define TRACEFS_MAGIC 0x74726163
+#endif
+#ifndef DEBUGFS_MAGIC
+#define DEBUGFS_MAGIC 0x64626720
+#endif
+
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+#include <boost/scope/unique_fd.hpp>
+
+#if defined(POSIX_FADV_SEQUENTIAL) && (!defined(__ANDROID__) || __ANDROID_API__ >= 21)
+#define BOOST_FILESYSTEM_HAS_POSIX_FADVISE
+#endif
+
+#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIM)
+#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtim.tv_nsec
+#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMESPEC)
+#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimespec.tv_nsec
+#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_MTIMENSEC)
+#define BOOST_FILESYSTEM_STAT_ST_MTIMENSEC st_mtimensec
+#endif
+
+#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIM)
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtim.tv_sec
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtim.tv_nsec
+#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMESPEC)
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtimespec.tv_sec
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimespec.tv_nsec
+#elif defined(BOOST_FILESYSTEM_HAS_STAT_ST_BIRTHTIMENSEC)
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIME st_birthtime
+#define BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC st_birthtimensec
+#endif
+
+#include "posix_tools.hpp"
+
+#else // BOOST_WINDOWS_API
+
+#include <boost/winapi/dll.hpp> // get_proc_address, GetModuleHandleW
+#include <cwchar>
+#include <io.h>
+#include <windows.h>
+#include <winnt.h>
+#if defined(__BORLANDC__) || defined(__MWERKS__)
+#if defined(BOOST_BORLANDC)
+using std::time_t;
+#endif
+#include <utime.h>
+#else
+#include <sys/utime.h>
+#endif
+
+#include "windows_tools.hpp"
+
+#endif // BOOST_WINDOWS_API
+
+#include "atomic_tools.hpp"
+#include "error_handling.hpp"
+#include "private_config.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace fs = boost::filesystem;
+using boost::filesystem::path;
+using boost::filesystem::filesystem_error;
+using boost::filesystem::perms;
+using boost::system::error_code;
+using boost::system::system_category;
+
+#if defined(BOOST_POSIX_API)
+
+// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#define BOOST_FILESYSTEM_NO_O_CLOEXEC
+#endif
+
+#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
+#define BOOST_FILESYSTEM_HAS_FDATASYNC
+#endif
+
+#else // defined(BOOST_POSIX_API)
+
+#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
+#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024)
+#endif
+
+#ifndef FSCTL_GET_REPARSE_POINT
+#define FSCTL_GET_REPARSE_POINT 0x900a8
+#endif
+
+#ifndef SYMLINK_FLAG_RELATIVE
+#define SYMLINK_FLAG_RELATIVE 1
+#endif
+
+// Fallback for MinGW/Cygwin
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+#define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
+#endif
+
+#endif // defined(BOOST_POSIX_API)
+
+// POSIX/Windows macros ----------------------------------------------------//
+
+// Portions of the POSIX and Windows API's are very similar, except for name,
+// order of arguments, and meaning of zero/non-zero returns. The macros below
+// abstract away those differences. They follow Windows naming and order of
+// arguments, and return true to indicate no error occurred. [POSIX naming,
+// order of arguments, and meaning of return were followed initially, but
+// found to be less clear and cause more coding errors.]
+
+#if defined(BOOST_POSIX_API)
+
+#define BOOST_SET_CURRENT_DIRECTORY(P) (::chdir(P) == 0)
+#define BOOST_MOVE_FILE(OLD, NEW) (::rename(OLD, NEW) == 0)
+#define BOOST_RESIZE_FILE(P, SZ) (::truncate(P, SZ) == 0)
+
+#else // BOOST_WINDOWS_API
+
+#define BOOST_SET_CURRENT_DIRECTORY(P) (::SetCurrentDirectoryW(P) != 0)
+#define BOOST_MOVE_FILE(OLD, NEW) (::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0)
+#define BOOST_RESIZE_FILE(P, SZ) (resize_file_impl(P, SZ) != 0)
+
+#endif
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+//! Initializes fill_random implementation pointer. Implemented in unique_path.cpp.
+void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver);
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+#if defined(BOOST_WINDOWS_API)
+//! Initializes directory iterator implementation. Implemented in directory.cpp.
+void init_directory_iterator_impl() noexcept;
+#endif // defined(BOOST_WINDOWS_API)
+
+//--------------------------------------------------------------------------------------//
+// //
+// helpers (all operating systems) //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace {
+
+// The number of retries remove_all should make if it detects that the directory it is about to enter has been replaced with a symlink or a regular file
+BOOST_CONSTEXPR_OR_CONST unsigned int remove_all_directory_replaced_retry_count = 5u;
+
+#if defined(BOOST_POSIX_API)
+
+// Size of a small buffer for a path that can be placed on stack, in character code units
+BOOST_CONSTEXPR_OR_CONST std::size_t small_path_size = 1024u;
+
+// Absolute maximum path length, in character code units, that we're willing to accept from various system calls.
+// This value is arbitrary, it is supposed to be a hard limit to avoid memory exhaustion
+// in some of the algorithms below in case of some corrupted or maliciously broken filesystem.
+// A few examples of path size limits:
+// - Windows: 32767 UTF-16 code units or 260 bytes for legacy multibyte APIs.
+// - Linux: 4096 bytes
+// - IRIX, HP-UX, Mac OS, QNX, FreeBSD, OpenBSD: 1024 bytes
+// - GNU/Hurd: no hard limit
+BOOST_CONSTEXPR_OR_CONST std::size_t absolute_path_max = 32u * 1024u;
+
+#endif // defined(BOOST_POSIX_API)
+
+// Maximum number of resolved symlinks before we register a loop
+BOOST_CONSTEXPR_OR_CONST unsigned int symloop_max =
+#if defined(SYMLOOP_MAX)
+ SYMLOOP_MAX < 40 ? 40 : SYMLOOP_MAX
+#else
+ 40
+#endif
+;
+
+// general helpers -----------------------------------------------------------------//
+
+#ifdef BOOST_POSIX_API
+
+//--------------------------------------------------------------------------------------//
+// //
+// POSIX-specific helpers //
+// //
+//--------------------------------------------------------------------------------------//
+
+inline bool not_found_error(int errval) noexcept
+{
+ return errval == ENOENT || errval == ENOTDIR;
+}
+
+/*!
+ * Closes a file descriptor and returns the result, similar to close(2). Unlike close(2), guarantees that the file descriptor is closed even if EINTR error happens.
+ *
+ * Some systems don't close the file descriptor in case if the thread is interrupted by a signal and close(2) returns EINTR.
+ * Other (most) systems do close the file descriptor even when when close(2) returns EINTR, and attempting to close it
+ * again could close a different file descriptor that was opened by a different thread. This function hides this difference in behavior.
+ *
+ * Future POSIX standards will likely fix this by introducing posix_close (see https://www.austingroupbugs.net/view.php?id=529)
+ * and prohibiting returning EINTR from close(2), but we still have to support older systems where this new behavior is not available and close(2)
+ * behaves differently between systems.
+ */
+inline int close_fd(int fd)
+{
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+ int res;
+ while (true)
+ {
+ res = ::close(fd);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+ }
+
+ break;
+ }
+
+ return res;
+#else
+ return ::close(fd);
+#endif
+}
+
+#if defined(BOOST_FILESYSTEM_HAS_STATX)
+
+//! A wrapper for statx libc function. Disable MSAN since at least on clang 10 it doesn't
+//! know which fields of struct statx are initialized by the syscall and misdetects errors.
+BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
+BOOST_FORCEINLINE int invoke_statx(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
+{
+ return ::statx(dirfd, path, flags, mask, stx);
+}
+
+#elif defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
+
+//! statx emulation through fstatat
+int statx_fstatat(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
+{
+ struct ::stat st;
+ flags &= AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW;
+ int res = ::fstatat(dirfd, path, &st, flags);
+ if (BOOST_LIKELY(res == 0))
+ {
+ std::memset(stx, 0, sizeof(*stx));
+ stx->stx_mask = STATX_BASIC_STATS;
+ stx->stx_blksize = st.st_blksize;
+ stx->stx_nlink = st.st_nlink;
+ stx->stx_uid = st.st_uid;
+ stx->stx_gid = st.st_gid;
+ stx->stx_mode = st.st_mode;
+ stx->stx_ino = st.st_ino;
+ stx->stx_size = st.st_size;
+ stx->stx_blocks = st.st_blocks;
+ stx->stx_atime.tv_sec = st.st_atim.tv_sec;
+ stx->stx_atime.tv_nsec = st.st_atim.tv_nsec;
+ stx->stx_ctime.tv_sec = st.st_ctim.tv_sec;
+ stx->stx_ctime.tv_nsec = st.st_ctim.tv_nsec;
+ stx->stx_mtime.tv_sec = st.st_mtim.tv_sec;
+ stx->stx_mtime.tv_nsec = st.st_mtim.tv_nsec;
+ stx->stx_rdev_major = major(st.st_rdev);
+ stx->stx_rdev_minor = minor(st.st_rdev);
+ stx->stx_dev_major = major(st.st_dev);
+ stx->stx_dev_minor = minor(st.st_dev);
+ }
+
+ return res;
+}
+
+typedef int statx_t(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx);
+
+//! Pointer to the actual implementation of the statx implementation
+statx_t* statx_ptr = &statx_fstatat;
+
+inline int invoke_statx(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx) noexcept
+{
+ return filesystem::detail::atomic_load_relaxed(statx_ptr)(dirfd, path, flags, mask, stx);
+}
+
+//! A wrapper for the statx syscall. Disable MSAN since at least on clang 10 it doesn't
+//! know which fields of struct statx are initialized by the syscall and misdetects errors.
+BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
+int statx_syscall(int dirfd, const char* path, int flags, unsigned int mask, struct ::statx* stx)
+{
+ int res = ::syscall(__NR_statx, dirfd, path, flags, mask, stx);
+ if (res < 0)
+ {
+ const int err = errno;
+ if (BOOST_UNLIKELY(err == ENOSYS))
+ {
+ filesystem::detail::atomic_store_relaxed(statx_ptr, &statx_fstatat);
+ return statx_fstatat(dirfd, path, flags, mask, stx);
+ }
+ }
+
+ return res;
+}
+
+#endif // defined(BOOST_FILESYSTEM_HAS_STATX)
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+
+//! Initializes statx implementation pointer
+inline void init_statx_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
+{
+#if !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
+ statx_t* stx = &statx_fstatat;
+ if (major_ver > 4u || (major_ver == 4u && minor_ver >= 11u))
+ stx = &statx_syscall;
+
+ filesystem::detail::atomic_store_relaxed(statx_ptr, stx);
+#endif // !defined(BOOST_FILESYSTEM_HAS_STATX) && defined(BOOST_FILESYSTEM_HAS_STATX_SYSCALL)
+}
+
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+
+//! Returns \c true if the two \c statx structures refer to the same file
+inline bool equivalent_stat(struct ::statx const& s1, struct ::statx const& s2) noexcept
+{
+ return s1.stx_dev_major == s2.stx_dev_major && s1.stx_dev_minor == s2.stx_dev_minor && s1.stx_ino == s2.stx_ino;
+}
+
+//! Returns file type/access mode from \c statx structure
+inline mode_t get_mode(struct ::statx const& st) noexcept
+{
+ return st.stx_mode;
+}
+
+//! Returns file size from \c statx structure
+inline uintmax_t get_size(struct ::statx const& st) noexcept
+{
+ return st.stx_size;
+}
+
+//! Returns optimal block size from \c statx structure
+inline std::size_t get_blksize(struct ::statx const& st) noexcept
+{
+ return st.stx_blksize;
+}
+
+#else // defined(BOOST_FILESYSTEM_USE_STATX)
+
+//! Returns \c true if the two \c stat structures refer to the same file
+inline bool equivalent_stat(struct ::stat const& s1, struct ::stat const& s2) noexcept
+{
+ // According to the POSIX stat specs, "The st_ino and st_dev fields
+ // taken together uniquely identify the file within the system."
+ return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino;
+}
+
+//! Returns file type/access mode from \c stat structure
+inline mode_t get_mode(struct ::stat const& st) noexcept
+{
+ return st.st_mode;
+}
+
+//! Returns file size from \c stat structure
+inline uintmax_t get_size(struct ::stat const& st) noexcept
+{
+ return st.st_size;
+}
+
+//! Returns optimal block size from \c stat structure
+inline std::size_t get_blksize(struct ::stat const& st) noexcept
+{
+#if defined(BOOST_FILESYSTEM_HAS_STAT_ST_BLKSIZE)
+ return st.st_blksize;
+#else
+ return 4096u; // a suitable default used on most modern SSDs/HDDs
+#endif
+}
+
+#endif // defined(BOOST_FILESYSTEM_USE_STATX)
+
+} // namespace
+
+//! status() implementation
+file_status status_impl
+(
+ path const& p,
+ system::error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
+ , int basedir_fd
+#endif
+)
+{
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx path_stat;
+ int err = invoke_statx(basedir_fd, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat);
+#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ struct ::stat path_stat;
+ int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_NO_AUTOMOUNT);
+#else
+ struct ::stat path_stat;
+ int err = ::stat(p.c_str(), &path_stat);
+#endif
+
+ if (err != 0)
+ {
+ err = errno;
+ if (ec) // always report errno, even though some
+ ec->assign(err, system::system_category()); // errno values are not status_errors
+
+ if (not_found_error(err))
+ return fs::file_status(fs::file_not_found, fs::no_perms);
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, system::error_code(err, system::system_category())));
+
+ return fs::file_status(fs::status_error);
+ }
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::status");
+ return fs::file_status(fs::status_error);
+ }
+#endif
+
+ const mode_t mode = get_mode(path_stat);
+ if (S_ISDIR(mode))
+ return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISREG(mode))
+ return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISBLK(mode))
+ return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISCHR(mode))
+ return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISFIFO(mode))
+ return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISSOCK(mode))
+ return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask);
+
+ return fs::file_status(fs::type_unknown);
+}
+
+//! symlink_status() implementation
+file_status symlink_status_impl
+(
+ path const& p,
+ system::error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
+ , int basedir_fd
+#endif
+)
+{
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx path_stat;
+ int err = invoke_statx(basedir_fd, p.c_str(), AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &path_stat);
+#elif defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ struct ::stat path_stat;
+ int err = ::fstatat(basedir_fd, p.c_str(), &path_stat, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT);
+#else
+ struct ::stat path_stat;
+ int err = ::lstat(p.c_str(), &path_stat);
+#endif
+
+ if (err != 0)
+ {
+ err = errno;
+ if (ec) // always report errno, even though some
+ ec->assign(err, system::system_category()); // errno values are not status_errors
+
+ if (not_found_error(err)) // these are not errors
+ return fs::file_status(fs::file_not_found, fs::no_perms);
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::symlink_status", p, system::error_code(err, system::system_category())));
+
+ return fs::file_status(fs::status_error);
+ }
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::symlink_status");
+ return fs::file_status(fs::status_error);
+ }
+#endif
+
+ const mode_t mode = get_mode(path_stat);
+ if (S_ISREG(mode))
+ return fs::file_status(fs::regular_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISDIR(mode))
+ return fs::file_status(fs::directory_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISLNK(mode))
+ return fs::file_status(fs::symlink_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISBLK(mode))
+ return fs::file_status(fs::block_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISCHR(mode))
+ return fs::file_status(fs::character_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISFIFO(mode))
+ return fs::file_status(fs::fifo_file, static_cast< perms >(mode) & fs::perms_mask);
+ if (S_ISSOCK(mode))
+ return fs::file_status(fs::socket_file, static_cast< perms >(mode) & fs::perms_mask);
+
+ return fs::file_status(fs::type_unknown);
+}
+
+namespace {
+
+//! Flushes buffered data and attributes written to the file to permanent storage
+inline int full_sync(int fd)
+{
+ while (true)
+ {
+#if defined(__APPLE__) && defined(__MACH__) && defined(F_FULLFSYNC)
+ // Mac OS does not flush data to physical storage with fsync()
+ int err = ::fcntl(fd, F_FULLFSYNC);
+#else
+ int err = ::fsync(fd);
+#endif
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ // POSIX says fsync can return EINTR (https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html).
+ // fcntl(F_FULLFSYNC) isn't documented to return EINTR, but it doesn't hurt to check.
+ if (err == EINTR)
+ continue;
+
+ return err;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+//! Flushes buffered data written to the file to permanent storage
+inline int data_sync(int fd)
+{
+#if defined(BOOST_FILESYSTEM_HAS_FDATASYNC) && !(defined(__APPLE__) && defined(__MACH__) && defined(F_FULLFSYNC))
+ while (true)
+ {
+ int err = ::fdatasync(fd);
+ if (BOOST_UNLIKELY(err != 0))
+ {
+ err = errno;
+ // POSIX says fsync can return EINTR (https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html).
+ // It doesn't say so for fdatasync, but it is reasonable to expect it as well.
+ if (err == EINTR)
+ continue;
+
+ return err;
+ }
+
+ break;
+ }
+
+ return 0;
+#else
+ return full_sync(fd);
+#endif
+}
+
+//! Hints the filesystem to opportunistically preallocate storage for a file
+inline int preallocate_storage(int file, uintmax_t size)
+{
+#if defined(BOOST_FILESYSTEM_HAS_FALLOCATE)
+ if (BOOST_LIKELY(size > 0 && size <= static_cast< uintmax_t >((std::numeric_limits< off_t >::max)())))
+ {
+ while (true)
+ {
+ // Note: We intentionally use fallocate rather than posix_fallocate to avoid
+ // invoking glibc emulation that writes zeros to the end of the file.
+ // We want this call to act like a hint to the filesystem and an early
+ // check for the free storage space. We don't want to write zeros only
+ // to later overwrite them with the actual data.
+ int err = fallocate(file, FALLOC_FL_KEEP_SIZE, 0, static_cast< off_t >(size));
+ if (BOOST_UNLIKELY(err != 0))
+ {
+ err = errno;
+
+ // Ignore the error if the operation is not supported by the kernel or filesystem
+ if (err == EOPNOTSUPP || err == ENOSYS)
+ break;
+
+ if (err == EINTR)
+ continue;
+
+ return err;
+ }
+
+ break;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+//! copy_file implementation wrapper that preallocates storage for the target file
+template< typename CopyFileData >
+struct copy_file_data_preallocate
+{
+ //! copy_file implementation wrapper that preallocates storage for the target file before invoking the underlying copy implementation
+ static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
+ {
+ int err = preallocate_storage(outfile, size);
+ if (BOOST_UNLIKELY(err != 0))
+ return err;
+
+ return CopyFileData::impl(infile, outfile, size, blksize);
+ }
+};
+
+// Min and max buffer sizes are selected to minimize the overhead from system calls.
+// The values are picked based on coreutils cp(1) benchmarking data described here:
+// https://github.com/coreutils/coreutils/blob/d1b0257077c0b0f0ee25087efd46270345d1dd1f/src/ioblksize.h#L23-L72
+BOOST_CONSTEXPR_OR_CONST uint_least32_t min_read_write_buf_size = 8u * 1024u;
+BOOST_CONSTEXPR_OR_CONST uint_least32_t max_read_write_buf_size = 256u * 1024u;
+
+//! copy_file read/write loop implementation
+int copy_file_data_read_write_impl(int infile, int outfile, char* buf, std::size_t buf_size)
+{
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_FADVISE)
+ ::posix_fadvise(infile, 0, 0, POSIX_FADV_SEQUENTIAL);
+#endif
+
+ // Don't use file size to limit the amount of data to copy since some filesystems, like procfs or sysfs,
+ // provide files with generated content and indicate that their size is zero or 4096. Just copy as much data
+ // as we can read from the input file.
+ while (true)
+ {
+ ssize_t sz_read = ::read(infile, buf, buf_size);
+ if (sz_read == 0)
+ break;
+ if (BOOST_UNLIKELY(sz_read < 0))
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+ return err;
+ }
+
+ // Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
+ // Marc Rochkind, Addison-Wesley, 2004, page 94
+ for (ssize_t sz_wrote = 0; sz_wrote < sz_read;)
+ {
+ ssize_t sz = ::write(outfile, buf + sz_wrote, static_cast< std::size_t >(sz_read - sz_wrote));
+ if (BOOST_UNLIKELY(sz < 0))
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+ return err;
+ }
+
+ sz_wrote += sz;
+ }
+ }
+
+ return 0;
+}
+
+//! copy_file implementation that uses read/write loop (fallback using a stack buffer)
+int copy_file_data_read_write_stack_buf(int infile, int outfile)
+{
+ char stack_buf[min_read_write_buf_size];
+ return copy_file_data_read_write_impl(infile, outfile, stack_buf, sizeof(stack_buf));
+}
+
+//! copy_file implementation that uses read/write loop
+int copy_file_data_read_write(int infile, int outfile, uintmax_t size, std::size_t blksize)
+{
+ {
+ uintmax_t buf_sz = size;
+ // Prefer the buffer to be larger than the file size so that we don't have
+ // to perform an extra read if the file fits in the buffer exactly.
+ buf_sz += (buf_sz < ~static_cast< uintmax_t >(0u));
+ if (buf_sz < blksize)
+ buf_sz = blksize;
+ if (buf_sz < min_read_write_buf_size)
+ buf_sz = min_read_write_buf_size;
+ if (buf_sz > max_read_write_buf_size)
+ buf_sz = max_read_write_buf_size;
+ const std::size_t buf_size = static_cast< std::size_t >(boost::core::bit_ceil(static_cast< uint_least32_t >(buf_sz)));
+ std::unique_ptr< char[] > buf(new (std::nothrow) char[buf_size]);
+ if (BOOST_LIKELY(!!buf.get()))
+ return copy_file_data_read_write_impl(infile, outfile, buf.get(), buf_size);
+ }
+
+ return copy_file_data_read_write_stack_buf(infile, outfile);
+}
+
+typedef int copy_file_data_t(int infile, int outfile, uintmax_t size, std::size_t blksize);
+
+//! Pointer to the actual implementation of the copy_file_data implementation
+copy_file_data_t* copy_file_data = &copy_file_data_read_write;
+
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+//! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem
+template< typename CopyFileData >
+int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize);
+
+#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+
+struct copy_file_data_sendfile
+{
+ //! copy_file implementation that uses sendfile loop. Requires sendfile to support file descriptors.
+ static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
+ {
+ // sendfile will not send more than this amount of data in one call
+ BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u;
+ uintmax_t offset = 0u;
+ while (offset < size)
+ {
+ uintmax_t size_left = size - offset;
+ std::size_t size_to_copy = max_batch_size;
+ if (size_left < static_cast< uintmax_t >(max_batch_size))
+ size_to_copy = static_cast< std::size_t >(size_left);
+ ssize_t sz = ::sendfile(outfile, infile, nullptr, size_to_copy);
+ if (BOOST_LIKELY(sz > 0))
+ {
+ offset += sz;
+ }
+ else if (sz < 0)
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+
+ if (offset == 0u)
+ {
+ // sendfile may fail with EINVAL if the underlying filesystem does not support it
+ if (err == EINVAL)
+ {
+ fallback_to_read_write:
+ return copy_file_data_read_write(infile, outfile, size, blksize);
+ }
+
+ if (err == ENOSYS)
+ {
+ filesystem::detail::atomic_store_relaxed(copy_file_data, &copy_file_data_read_write);
+ goto fallback_to_read_write;
+ }
+ }
+
+ return err;
+ }
+ else
+ {
+ // EOF: the input file was truncated while copying was in progress
+ break;
+ }
+ }
+
+ return 0;
+ }
+};
+
+#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE)
+
+#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+struct copy_file_data_copy_file_range
+{
+ //! copy_file implementation that uses copy_file_range loop. Requires copy_file_range to support cross-filesystem copying.
+ static int impl(int infile, int outfile, uintmax_t size, std::size_t blksize)
+ {
+ // Although copy_file_range does not document any particular upper limit of one transfer, still use some upper bound to guarantee
+ // that size_t is not overflown in case if off_t is larger and the file size does not fit in size_t.
+ BOOST_CONSTEXPR_OR_CONST std::size_t max_batch_size = 0x7ffff000u;
+ uintmax_t offset = 0u;
+ while (offset < size)
+ {
+ uintmax_t size_left = size - offset;
+ std::size_t size_to_copy = max_batch_size;
+ if (size_left < static_cast< uintmax_t >(max_batch_size))
+ size_to_copy = static_cast< std::size_t >(size_left);
+ // Note: Use syscall directly to avoid depending on libc version. copy_file_range is added in glibc 2.27.
+ // uClibc-ng does not have copy_file_range as of the time of this writing (the latest uClibc-ng release is 1.0.33).
+ loff_t sz = ::syscall(__NR_copy_file_range, infile, (loff_t*)nullptr, outfile, (loff_t*)nullptr, size_to_copy, (unsigned int)0u);
+ if (BOOST_LIKELY(sz > 0))
+ {
+ offset += sz;
+ }
+ else if (sz < 0)
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+
+ if (offset == 0u)
+ {
+ // copy_file_range may fail with EINVAL if the underlying filesystem does not support it.
+ // In some RHEL/CentOS 7.7-7.8 kernel versions, copy_file_range on NFSv4 is also known to return EOPNOTSUPP
+ // if the remote server does not support COPY, despite that it is not a documented error code.
+ // See https://patchwork.kernel.org/project/linux-nfs/patch/20190411183418.4510-1-olga.kornievskaia@gmail.com/
+ // and https://bugzilla.redhat.com/show_bug.cgi?id=1783554.
+ if (err == EINVAL || err == EOPNOTSUPP)
+ {
+#if !defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ fallback_to_read_write:
+#endif
+ return copy_file_data_read_write(infile, outfile, size, blksize);
+ }
+
+ if (err == EXDEV)
+ {
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ fallback_to_sendfile:
+ return copy_file_data_sendfile::impl(infile, outfile, size, blksize);
+#else
+ goto fallback_to_read_write;
+#endif
+ }
+
+ if (err == ENOSYS)
+ {
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ filesystem::detail::atomic_store_relaxed(copy_file_data, &check_fs_type< copy_file_data_preallocate< copy_file_data_sendfile > >);
+ goto fallback_to_sendfile;
+#else
+ filesystem::detail::atomic_store_relaxed(copy_file_data, &copy_file_data_read_write);
+ goto fallback_to_read_write;
+#endif
+ }
+ }
+
+ return err;
+ }
+ else
+ {
+ // EOF: the input file was truncated while copying was in progress
+ break;
+ }
+ }
+
+ return 0;
+ }
+};
+
+#endif // defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+//! copy_file_data wrapper that tests if a read/write loop must be used for a given filesystem
+template< typename CopyFileData >
+int check_fs_type(int infile, int outfile, uintmax_t size, std::size_t blksize)
+{
+ {
+ // Some filesystems have regular files with generated content. Such files have arbitrary size, including zero,
+ // but have actual content. Linux system calls sendfile or copy_file_range will not copy contents of such files,
+ // so we must use a read/write loop to handle them.
+ // https://lore.kernel.org/linux-fsdevel/20210212044405.4120619-1-drinkcat@chromium.org/T/
+ struct statfs sfs;
+ while (true)
+ {
+ int err = ::fstatfs(infile, &sfs);
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ if (err == EINTR)
+ continue;
+
+ goto fallback_to_read_write;
+ }
+
+ break;
+ }
+
+ if (BOOST_UNLIKELY(sfs.f_type == PROC_SUPER_MAGIC ||
+ sfs.f_type == SYSFS_MAGIC ||
+ sfs.f_type == TRACEFS_MAGIC ||
+ sfs.f_type == DEBUGFS_MAGIC))
+ {
+ fallback_to_read_write:
+ return copy_file_data_read_write(infile, outfile, size, blksize);
+ }
+ }
+
+ return CopyFileData::impl(infile, outfile, size, blksize);
+}
+
+#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+
+//! Initializes copy_file_data implementation pointer
+inline void init_copy_file_data_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
+{
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+ copy_file_data_t* cfd = &copy_file_data_read_write;
+
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ // sendfile started accepting file descriptors as the target in Linux 2.6.33
+ if (major_ver > 2u || (major_ver == 2u && (minor_ver > 6u || (minor_ver == 6u && patch_ver >= 33u))))
+ cfd = &check_fs_type< copy_file_data_preallocate< copy_file_data_sendfile > >;
+#endif
+
+#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+ // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3.
+ // copy_file_data_copy_file_range will fallback to copy_file_data_sendfile if copy_file_range returns EXDEV.
+ if (major_ver > 4u || (major_ver == 4u && minor_ver >= 5u))
+ cfd = &check_fs_type< copy_file_data_preallocate< copy_file_data_copy_file_range > >;
+#endif
+
+ filesystem::detail::atomic_store_relaxed(copy_file_data, cfd);
+#endif // defined(BOOST_FILESYSTEM_USE_SENDFILE) || defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
+}
+
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+
+struct syscall_initializer
+{
+ syscall_initializer()
+ {
+ struct ::utsname system_info;
+ if (BOOST_UNLIKELY(::uname(&system_info) < 0))
+ return;
+
+ unsigned int major_ver = 0u, minor_ver = 0u, patch_ver = 0u;
+ int count = std::sscanf(system_info.release, "%u.%u.%u", &major_ver, &minor_ver, &patch_ver);
+ if (BOOST_UNLIKELY(count < 3))
+ return;
+
+ init_statx_impl(major_ver, minor_ver, patch_ver);
+ init_copy_file_data_impl(major_ver, minor_ver, patch_ver);
+ init_fill_random_impl(major_ver, minor_ver, patch_ver);
+ }
+};
+
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+const syscall_initializer syscall_init;
+
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+//! remove() implementation
+inline bool remove_impl
+(
+ path const& p,
+ fs::file_type type,
+ error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ , int basedir_fd = AT_FDCWD
+#endif
+)
+{
+ if (type == fs::file_not_found)
+ return false;
+
+ int res;
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ res = ::unlinkat(basedir_fd, p.c_str(), type == fs::directory_file ? AT_REMOVEDIR : 0);
+#else
+ if (type == fs::directory_file)
+ res = ::rmdir(p.c_str());
+ else
+ res = ::unlink(p.c_str());
+#endif
+
+ if (res != 0)
+ {
+ int err = errno;
+ if (BOOST_UNLIKELY(!not_found_error(err)))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+
+ return true;
+}
+
+//! remove() implementation
+inline bool remove_impl(path const& p, error_code* ec)
+{
+ // Since POSIX remove() is specified to work with either files or directories, in a
+ // perfect world it could just be called. But some important real-world operating
+ // systems (Windows, Mac OS, for example) don't implement the POSIX spec. So
+ // we have to distinguish between files and directories and call corresponding APIs
+ // to remove them.
+
+ error_code local_ec;
+ fs::file_type type = fs::detail::symlink_status_impl(p, &local_ec).type();
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove", p, local_ec));
+
+ *ec = local_ec;
+ return false;
+ }
+
+ return fs::detail::remove_impl(p, type, ec);
+}
+
+//! remove_all() implementation
+uintmax_t remove_all_impl
+(
+ path const& p,
+ error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ , int parentdir_fd = AT_FDCWD
+#endif
+)
+{
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ fs::path filename;
+ const fs::path* remove_path = &p;
+ if (parentdir_fd != AT_FDCWD)
+ {
+ filename = path_algorithms::filename_v4(p);
+ remove_path = &filename;
+ }
+#endif
+
+ error_code dit_create_ec;
+ for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
+ {
+ fs::file_type type;
+ {
+ error_code local_ec;
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ type = fs::detail::symlink_status_impl(*remove_path, &local_ec, parentdir_fd).type();
+#else
+ type = fs::detail::symlink_status_impl(p, &local_ec).type();
+#endif
+
+ if (type == fs::file_not_found)
+ return 0u;
+
+ if (BOOST_UNLIKELY(type == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+ uintmax_t count = 0u;
+ if (type == fs::directory_file) // but not a directory symlink
+ {
+ fs::directory_iterator itr;
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ fs::detail::directory_iterator_params params{ fs::detail::openat_directory(parentdir_fd, *remove_path, directory_options::_detail_no_follow, dit_create_ec) };
+ int dir_fd = -1;
+ if (BOOST_LIKELY(!dit_create_ec))
+ {
+ // Save dir_fd as constructing the iterator will move the fd into the iterator context
+ dir_fd = params.dir_fd.get();
+ fs::detail::directory_iterator_construct(itr, *remove_path, directory_options::_detail_no_follow, &params, &dit_create_ec);
+ }
+#else
+ fs::detail::directory_iterator_construct
+ (
+ itr,
+ p,
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ directory_options::_detail_no_follow,
+#else
+ directory_options::none,
+#endif
+ nullptr,
+ &dit_create_ec
+ );
+#endif
+
+ if (BOOST_UNLIKELY(!!dit_create_ec))
+ {
+ if (dit_create_ec == error_code(ENOTDIR, system_category()))
+ continue;
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ // If open(2) with O_NOFOLLOW fails with ELOOP, this means that either the path contains a loop
+ // of symbolic links, or the last element of the path is a symbolic link. Given that lstat(2) above
+ // did not fail, most likely it is the latter case. I.e. between the lstat above and this open call
+ // the filesystem was modified so that the path no longer refers to a directory file (as opposed to a symlink).
+ if (dit_create_ec == error_code(ELOOP, system_category()))
+ continue;
+#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, dit_create_ec));
+
+ *ec = dit_create_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ count += fs::detail::remove_all_impl
+ (
+ itr->path(),
+ ec
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ , dir_fd
+#endif
+ );
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+ count += fs::detail::remove_impl(*remove_path, type, ec, parentdir_fd);
+#else
+ count += fs::detail::remove_impl(p, type, ec);
+#endif
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ return count;
+ }
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all: path cannot be opened as a directory", p, dit_create_ec));
+
+ *ec = dit_create_ec;
+ return static_cast< uintmax_t >(-1);
+}
+
+#else // defined(BOOST_POSIX_API)
+
+//--------------------------------------------------------------------------------------//
+// //
+// Windows-specific helpers //
+// //
+//--------------------------------------------------------------------------------------//
+
+//! FILE_BASIC_INFO definition from Windows SDK
+struct file_basic_info
+{
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ DWORD FileAttributes;
+};
+
+//! FILE_DISPOSITION_INFO definition from Windows SDK
+struct file_disposition_info
+{
+ BOOLEAN DeleteFile;
+};
+
+//! FILE_DISPOSITION_INFO_EX definition from Windows SDK
+struct file_disposition_info_ex
+{
+ DWORD Flags;
+};
+
+#ifndef FILE_DISPOSITION_FLAG_DELETE
+#define FILE_DISPOSITION_FLAG_DELETE 0x00000001
+#endif
+// Available since Windows 10 1709
+#ifndef FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
+#define FILE_DISPOSITION_FLAG_POSIX_SEMANTICS 0x00000002
+#endif
+// Available since Windows 10 1809
+#ifndef FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE
+#define FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE 0x00000010
+#endif
+
+// REPARSE_DATA_BUFFER related definitions are found in ntifs.h, which is part of the
+// Windows Device Driver Kit. Since that's inconvenient, the definitions are provided
+// here. See http://msdn.microsoft.com/en-us/library/ms791514.aspx
+struct reparse_data_buffer
+{
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union
+ {
+ /*
+ * In SymbolicLink and MountPoint reparse points, there are two names.
+ * SubstituteName is the effective replacement path for the reparse point.
+ * This is what should be used for path traversal.
+ * PrintName is intended for presentation to the user and may omit some
+ * elements of the path or be absent entirely.
+ *
+ * Examples of substitute and print names:
+ * mklink /D ldrive c:\
+ * SubstituteName: "\??\c:\"
+ * PrintName: "c:\"
+ *
+ * mklink /J ldrive c:\
+ * SubstituteName: "\??\C:\"
+ * PrintName: "c:\"
+ *
+ * junction ldrive c:\
+ * SubstituteName: "\??\C:\"
+ * PrintName: ""
+ *
+ * box.com mounted cloud storage
+ * SubstituteName: "\??\Volume{<UUID>}\"
+ * PrintName: ""
+ */
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct
+ {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+};
+
+// Our convenience type for allocating REPARSE_DATA_BUFFER along with sufficient space after it
+union reparse_data_buffer_with_storage
+{
+ reparse_data_buffer rdb;
+ unsigned char storage[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+};
+
+// Windows kernel32.dll functions that may or may not be present
+// must be accessed through pointers
+
+typedef BOOL (WINAPI CreateHardLinkW_t)(
+ /*__in*/ LPCWSTR lpFileName,
+ /*__in*/ LPCWSTR lpExistingFileName,
+ /*__reserved*/ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+
+CreateHardLinkW_t* create_hard_link_api = nullptr;
+
+typedef BOOLEAN (WINAPI CreateSymbolicLinkW_t)(
+ /*__in*/ LPCWSTR lpSymlinkFileName,
+ /*__in*/ LPCWSTR lpTargetFileName,
+ /*__in*/ DWORD dwFlags);
+
+CreateSymbolicLinkW_t* create_symbolic_link_api = nullptr;
+
+//! SetFileInformationByHandle signature. Available since Windows Vista.
+typedef BOOL (WINAPI SetFileInformationByHandle_t)(
+ /*_In_*/ HANDLE hFile,
+ /*_In_*/ file_info_by_handle_class FileInformationClass, // the actual type is FILE_INFO_BY_HANDLE_CLASS enum
+ /*_In_reads_bytes_(dwBufferSize)*/ LPVOID lpFileInformation,
+ /*_In_*/ DWORD dwBufferSize);
+
+SetFileInformationByHandle_t* set_file_information_by_handle_api = nullptr;
+
+} // unnamed namespace
+
+GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api = nullptr;
+
+NtCreateFile_t* nt_create_file_api = nullptr;
+NtQueryDirectoryFile_t* nt_query_directory_file_api = nullptr;
+
+namespace {
+
+//! remove() implementation type
+enum remove_impl_type
+{
+ remove_nt5, //!< Use Windows XP API
+ remove_disp, //!< Use FILE_DISPOSITION_INFO (Windows Vista and later)
+ remove_disp_ex_flag_posix_semantics, //!< Use FILE_DISPOSITION_INFO_EX with FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
+ remove_disp_ex_flag_ignore_readonly //!< Use FILE_DISPOSITION_INFO_EX with FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE
+};
+
+remove_impl_type g_remove_impl_type = remove_nt5;
+
+//! Initializes WinAPI function pointers
+BOOST_FILESYSTEM_INIT_FUNC init_winapi_func_ptrs()
+{
+ boost::winapi::HMODULE_ h = boost::winapi::GetModuleHandleW(L"kernel32.dll");
+ if (BOOST_LIKELY(!!h))
+ {
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = (GetFileInformationByHandleEx_t*)boost::winapi::get_proc_address(h, "GetFileInformationByHandleEx");
+ filesystem::detail::atomic_store_relaxed(get_file_information_by_handle_ex_api, get_file_information_by_handle_ex);
+ SetFileInformationByHandle_t* set_file_information_by_handle = (SetFileInformationByHandle_t*)boost::winapi::get_proc_address(h, "SetFileInformationByHandle");
+ filesystem::detail::atomic_store_relaxed(set_file_information_by_handle_api, set_file_information_by_handle);
+ filesystem::detail::atomic_store_relaxed(create_hard_link_api, (CreateHardLinkW_t*)boost::winapi::get_proc_address(h, "CreateHardLinkW"));
+ filesystem::detail::atomic_store_relaxed(create_symbolic_link_api, (CreateSymbolicLinkW_t*)boost::winapi::get_proc_address(h, "CreateSymbolicLinkW"));
+
+ if (get_file_information_by_handle_ex && set_file_information_by_handle)
+ {
+ // Enable the most advanced implementation based on GetFileInformationByHandleEx/SetFileInformationByHandle.
+ // If certain flags are not supported by the OS, the remove() implementation will downgrade accordingly.
+ filesystem::detail::atomic_store_relaxed(g_remove_impl_type, remove_disp_ex_flag_ignore_readonly);
+ }
+ }
+
+ h = boost::winapi::GetModuleHandleW(L"ntdll.dll");
+ if (BOOST_LIKELY(!!h))
+ {
+ filesystem::detail::atomic_store_relaxed(nt_create_file_api, (NtCreateFile_t*)boost::winapi::get_proc_address(h, "NtCreateFile"));
+ filesystem::detail::atomic_store_relaxed(nt_query_directory_file_api, (NtQueryDirectoryFile_t*)boost::winapi::get_proc_address(h, "NtQueryDirectoryFile"));
+ }
+
+ init_directory_iterator_impl();
+
+ return BOOST_FILESYSTEM_INITRETSUCCESS_V;
+}
+
+#if defined(_MSC_VER)
+
+#if _MSC_VER >= 1400
+
+#pragma section(".CRT$XCL", long, read)
+__declspec(allocate(".CRT$XCL")) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+extern const init_func_ptr_t p_init_winapi_func_ptrs = &init_winapi_func_ptrs;
+
+#else // _MSC_VER >= 1400
+
+#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
+#pragma data_seg(push, old_seg)
+#endif
+#pragma data_seg(".CRT$XCL")
+BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+extern const init_func_ptr_t p_init_winapi_func_ptrs = &init_winapi_func_ptrs;
+#pragma data_seg()
+#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
+#pragma data_seg(pop, old_seg)
+#endif
+
+#endif // _MSC_VER >= 1400
+
+#if defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
+//! Makes sure the global initializer pointers are referenced and not removed by linker
+struct globals_retainer
+{
+ const init_func_ptr_t* volatile m_p_init_winapi_func_ptrs;
+
+ globals_retainer() { m_p_init_winapi_func_ptrs = &p_init_winapi_func_ptrs; }
+};
+BOOST_ATTRIBUTE_UNUSED
+const globals_retainer g_globals_retainer;
+#endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
+
+#else // defined(_MSC_VER)
+
+//! Invokes WinAPI function pointers initialization
+struct winapi_func_ptrs_initializer
+{
+ winapi_func_ptrs_initializer() { init_winapi_func_ptrs(); }
+};
+
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+const winapi_func_ptrs_initializer winapi_func_ptrs_init;
+
+#endif // defined(_MSC_VER)
+
+
+inline std::wstring wgetenv(const wchar_t* name)
+{
+ // use a separate buffer since C++03 basic_string is not required to be contiguous
+ const DWORD size = ::GetEnvironmentVariableW(name, nullptr, 0);
+ if (size > 0)
+ {
+ std::unique_ptr< wchar_t[] > buf(new wchar_t[size]);
+ if (BOOST_LIKELY(::GetEnvironmentVariableW(name, buf.get(), size) > 0))
+ return std::wstring(buf.get());
+ }
+
+ return std::wstring();
+}
+
+inline bool not_found_error(int errval) noexcept
+{
+ return errval == ERROR_FILE_NOT_FOUND || errval == ERROR_PATH_NOT_FOUND || errval == ERROR_INVALID_NAME // "tools/jam/src/:sys:stat.h", "//foo"
+ || errval == ERROR_INVALID_DRIVE // USB card reader with no card inserted
+ || errval == ERROR_NOT_READY // CD/DVD drive with no disc inserted
+ || errval == ERROR_INVALID_PARAMETER // ":sys:stat.h"
+ || errval == ERROR_BAD_PATHNAME // "//no-host" on Win64
+ || errval == ERROR_BAD_NETPATH // "//no-host" on Win32
+ || errval == ERROR_BAD_NET_NAME; // "//no-host/no-share" on Win10 x64
+}
+
+// these constants come from inspecting some Microsoft sample code
+inline DWORD to_time_t(FILETIME const& ft, std::time_t& t)
+{
+ uint64_t ut = (static_cast< uint64_t >(ft.dwHighDateTime) << 32u) | ft.dwLowDateTime;
+ if (BOOST_UNLIKELY(ut > static_cast< uint64_t >((std::numeric_limits< int64_t >::max)())))
+ return ERROR_INVALID_DATA;
+
+ // On Windows, time_t is signed, and negative values are possible since FILETIME epoch is earlier than POSIX epoch
+ int64_t st = static_cast< int64_t >(ut) / 10000000 - 11644473600ll;
+ if (BOOST_UNLIKELY(st < static_cast< int64_t >((std::numeric_limits< std::time_t >::min)()) ||
+ st > static_cast< int64_t >((std::numeric_limits< std::time_t >::max)())))
+ {
+ return ERROR_INVALID_DATA;
+ }
+
+ t = static_cast< std::time_t >(st);
+ return 0u;
+}
+
+inline DWORD to_FILETIME(std::time_t t, FILETIME& ft)
+{
+ // On Windows, time_t is signed, and negative values are possible since FILETIME epoch is earlier than POSIX epoch
+ int64_t st = static_cast< int64_t >(t);
+ if (BOOST_UNLIKELY(st < ((std::numeric_limits< int64_t >::min)() / 10000000 - 11644473600ll) ||
+ st > ((std::numeric_limits< int64_t >::max)() / 10000000 - 11644473600ll)))
+ {
+ return ERROR_INVALID_DATA;
+ }
+
+ st = (st + 11644473600ll) * 10000000;
+ uint64_t ut = static_cast< uint64_t >(st);
+ ft.dwLowDateTime = static_cast< DWORD >(ut);
+ ft.dwHighDateTime = static_cast< DWORD >(ut >> 32u);
+
+ return 0u;
+}
+
+} // unnamed namespace
+
+//! The flag indicates whether OBJ_DONT_REPARSE flag is not supported by the kernel
+static bool g_no_obj_dont_reparse = false;
+
+//! Creates a file handle for a file relative to a previously opened base directory. The file path must be relative and in preferred format.
+boost::winapi::NTSTATUS_ nt_create_file_handle_at
+(
+ unique_handle& out,
+ HANDLE basedir_handle,
+ boost::filesystem::path const& p,
+ ULONG FileAttributes,
+ ACCESS_MASK DesiredAccess,
+ ULONG ShareMode,
+ ULONG CreateDisposition,
+ ULONG CreateOptions
+)
+{
+ NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api);
+ if (BOOST_UNLIKELY(!nt_create_file))
+ return STATUS_NOT_IMPLEMENTED;
+
+ unicode_string obj_name = {};
+ obj_name.Buffer = const_cast< wchar_t* >(p.c_str());
+ obj_name.Length = obj_name.MaximumLength = static_cast< USHORT >(p.size() * sizeof(wchar_t));
+
+ object_attributes obj_attrs = {};
+ obj_attrs.Length = sizeof(obj_attrs);
+ obj_attrs.RootDirectory = basedir_handle;
+ obj_attrs.ObjectName = &obj_name;
+
+ obj_attrs.Attributes = OBJ_CASE_INSENSITIVE;
+ if ((CreateOptions & FILE_OPEN_REPARSE_POINT) != 0u && !filesystem::detail::atomic_load_relaxed(g_no_obj_dont_reparse))
+ obj_attrs.Attributes |= OBJ_DONT_REPARSE;
+
+ io_status_block iosb;
+ HANDLE out_handle = INVALID_HANDLE_VALUE;
+ boost::winapi::NTSTATUS_ status = nt_create_file
+ (
+ &out_handle,
+ DesiredAccess,
+ &obj_attrs,
+ &iosb,
+ nullptr, // AllocationSize
+ FileAttributes,
+ ShareMode,
+ CreateDisposition,
+ CreateOptions,
+ nullptr, // EaBuffer
+ 0u // EaLength
+ );
+
+ if (BOOST_UNLIKELY(status == STATUS_INVALID_PARAMETER && (obj_attrs.Attributes & OBJ_DONT_REPARSE) != 0u))
+ {
+ // OBJ_DONT_REPARSE is supported since Windows 10, retry without it
+ filesystem::detail::atomic_store_relaxed(g_no_obj_dont_reparse, true);
+ obj_attrs.Attributes &= ~static_cast< ULONG >(OBJ_DONT_REPARSE);
+
+ status = nt_create_file
+ (
+ &out_handle,
+ DesiredAccess,
+ &obj_attrs,
+ &iosb,
+ nullptr, // AllocationSize
+ FileAttributes,
+ ShareMode,
+ CreateDisposition,
+ CreateOptions,
+ nullptr, // EaBuffer
+ 0u // EaLength
+ );
+ }
+
+ out.reset(out_handle);
+
+ return status;
+}
+
+ULONG get_reparse_point_tag_ioctl(HANDLE h, path const& p, error_code* ec)
+{
+ std::unique_ptr< reparse_data_buffer_with_storage > buf(new (std::nothrow) reparse_data_buffer_with_storage);
+ if (BOOST_UNLIKELY(!buf.get()))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("Cannot allocate memory to query reparse point", p, make_error_code(system::errc::not_enough_memory)));
+
+ *ec = make_error_code(system::errc::not_enough_memory);
+ return 0u;
+ }
+
+ // Query the reparse data
+ DWORD dwRetLen = 0u;
+ BOOL result = ::DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), sizeof(*buf), &dwRetLen, nullptr);
+ if (BOOST_UNLIKELY(!result))
+ {
+ DWORD err = ::GetLastError();
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("Failed to query reparse point", p, error_code(err, system_category())));
+
+ ec->assign(err, system_category());
+ return 0u;
+ }
+
+ return buf->rdb.ReparseTag;
+}
+
+namespace {
+
+inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* buf, wchar_t** p)
+{
+ return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p));
+}
+
+inline fs::file_status process_status_failure(DWORD errval, path const& p, error_code* ec)
+{
+ if (ec) // always report errval, even though some
+ ec->assign(errval, system_category()); // errval values are not status_errors
+
+ if (not_found_error(errval))
+ {
+ return fs::file_status(fs::file_not_found, fs::no_perms);
+ }
+ else if (errval == ERROR_SHARING_VIOLATION)
+ {
+ return fs::file_status(fs::type_unknown);
+ }
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::status", p, error_code(errval, system_category())));
+
+ return fs::file_status(fs::status_error);
+}
+
+inline fs::file_status process_status_failure(path const& p, error_code* ec)
+{
+ return process_status_failure(::GetLastError(), p, ec);
+}
+
+} // namespace
+
+//! (symlink_)status() by handle implementation
+fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec)
+{
+ fs::file_type ftype;
+ DWORD attrs;
+ ULONG reparse_tag = 0u;
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
+ if (BOOST_LIKELY(get_file_information_by_handle_ex != nullptr))
+ {
+ file_attribute_tag_info info;
+ BOOL res = get_file_information_by_handle_ex(h, file_attribute_tag_info_class, &info, sizeof(info));
+ if (BOOST_UNLIKELY(!res))
+ {
+ // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER.
+ // Presumably, this is because these filesystems don't support reparse points, so ReparseTag
+ // cannot be returned. Also check ERROR_NOT_SUPPORTED for good measure. Fall back to the legacy
+ // code path in this case.
+ DWORD err = ::GetLastError();
+ if (err == ERROR_INVALID_PARAMETER || err == ERROR_NOT_SUPPORTED)
+ goto use_get_file_information_by_handle;
+
+ return process_status_failure(err, p, ec);
+ }
+
+ attrs = info.FileAttributes;
+ reparse_tag = info.ReparseTag;
+ }
+ else
+ {
+ use_get_file_information_by_handle:
+ BY_HANDLE_FILE_INFORMATION info;
+ BOOL res = ::GetFileInformationByHandle(h, &info);
+ if (BOOST_UNLIKELY(!res))
+ return process_status_failure(p, ec);
+
+ attrs = info.dwFileAttributes;
+
+ if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ {
+ reparse_tag = get_reparse_point_tag_ioctl(h, p, ec);
+ if (ec)
+ {
+ if (BOOST_UNLIKELY(!!ec))
+ return fs::file_status(fs::status_error);
+ }
+ }
+ }
+
+ if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
+ {
+ if (reparse_tag == IO_REPARSE_TAG_DEDUP)
+ ftype = fs::regular_file;
+ else if (is_reparse_point_tag_a_symlink(reparse_tag))
+ ftype = fs::symlink_file;
+ else
+ ftype = fs::reparse_file;
+ }
+ else if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
+ {
+ ftype = fs::directory_file;
+ }
+ else
+ {
+ ftype = fs::regular_file;
+ }
+
+ return fs::file_status(ftype, make_permissions(p, attrs));
+}
+
+namespace {
+
+//! symlink_status() implementation
+fs::file_status symlink_status_impl(path const& p, error_code* ec)
+{
+ // Normally, we only need FILE_READ_ATTRIBUTES access mode. But SMBv1 reports incorrect
+ // file attributes in GetFileInformationByHandleEx in this case (e.g. it reports FILE_ATTRIBUTE_NORMAL
+ // for a directory in a SMBv1 share), so we add FILE_READ_EA as a workaround.
+ // https://github.com/boostorg/filesystem/issues/282
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, // lpSecurityAttributes
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
+
+ if (!h)
+ {
+ // For some system files and folders like "System Volume Information" CreateFileW fails
+ // with ERROR_ACCESS_DENIED. GetFileAttributesW succeeds for such files, so try that.
+ // Though this will only help if the file is not a reparse point (symlink or not).
+ DWORD err = ::GetLastError();
+ if (err == ERROR_ACCESS_DENIED)
+ {
+ DWORD attrs = ::GetFileAttributesW(p.c_str());
+ if (attrs != INVALID_FILE_ATTRIBUTES)
+ {
+ if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0u)
+ return fs::file_status((attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file, make_permissions(p, attrs));
+ }
+ else
+ {
+ err = ::GetLastError();
+ }
+ }
+
+ return process_status_failure(err, p, ec);
+ }
+
+ return detail::status_by_handle(h.get(), p, ec);
+}
+
+//! status() implementation
+fs::file_status status_impl(path const& p, error_code* ec)
+{
+ // We should first test if the file is a symlink or a reparse point. Resolving some reparse
+ // points by opening the file may fail, and status() should return file_status(reparse_file) in this case.
+ // Which is what symlink_status() returns.
+ fs::file_status st(detail::symlink_status_impl(p, ec));
+ if (st.type() == symlink_file)
+ {
+ // Resolve the symlink
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA, // see the comment in symlink_status_impl re. access mode
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr, // lpSecurityAttributes
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ if (!h)
+ return process_status_failure(p, ec);
+
+ st = detail::status_by_handle(h.get(), p, ec);
+ }
+
+ return st;
+}
+
+//! remove() implementation for Windows XP and older
+bool remove_nt5_impl(path const& p, DWORD attrs, error_code* ec)
+{
+ const bool is_directory = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ const bool is_read_only = (attrs & FILE_ATTRIBUTE_READONLY) != 0;
+ if (is_read_only)
+ {
+ // RemoveDirectoryW and DeleteFileW do not allow to remove a read-only file, so we have to drop the attribute
+ DWORD new_attrs = attrs & ~FILE_ATTRIBUTE_READONLY;
+ BOOL res = ::SetFileAttributesW(p.c_str(), new_attrs);
+ if (BOOST_UNLIKELY(!res))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+ }
+
+ BOOL res;
+ if (!is_directory)
+ {
+ // DeleteFileW works for file symlinks by removing the symlink, not the target.
+ res = ::DeleteFileW(p.c_str());
+ }
+ else
+ {
+ // RemoveDirectoryW works for symlinks and junctions by removing the symlink, not the target,
+ // even if the target directory is not empty.
+ // Note that unlike opening the directory with FILE_FLAG_DELETE_ON_CLOSE flag, RemoveDirectoryW
+ // will fail if the directory is not empty.
+ res = ::RemoveDirectoryW(p.c_str());
+ }
+
+ if (BOOST_UNLIKELY(!res))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ {
+ if (is_read_only)
+ {
+ // Try to restore the read-only attribute
+ ::SetFileAttributesW(p.c_str(), attrs);
+ }
+
+ emit_error(err, p, ec, "boost::filesystem::remove");
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+//! remove() by handle implementation for Windows Vista and newer
+DWORD remove_nt6_by_handle(HANDLE handle, remove_impl_type impl)
+{
+ GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
+ SetFileInformationByHandle_t* set_file_information_by_handle = filesystem::detail::atomic_load_relaxed(set_file_information_by_handle_api);
+ DWORD err = 0u;
+ switch (impl)
+ {
+ case remove_disp_ex_flag_ignore_readonly:
+ {
+ file_disposition_info_ex info;
+ info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE;
+ BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
+ if (BOOST_LIKELY(!!res))
+ break;
+
+ err = ::GetLastError();
+ if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED))
+ {
+ // Downgrade to the older implementation
+ impl = remove_disp_ex_flag_posix_semantics;
+ filesystem::detail::atomic_store_relaxed(g_remove_impl_type, impl);
+ }
+ else
+ {
+ break;
+ }
+ }
+ BOOST_FALLTHROUGH;
+
+ case remove_disp_ex_flag_posix_semantics:
+ {
+ file_disposition_info_ex info;
+ info.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS;
+ BOOL res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
+ if (BOOST_LIKELY(!!res))
+ {
+ err = 0u;
+ break;
+ }
+
+ err = ::GetLastError();
+ if (err == ERROR_ACCESS_DENIED)
+ {
+ // Check if the file is read-only and reset the attribute
+ file_basic_info basic_info;
+ res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+ if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0))
+ break; // return ERROR_ACCESS_DENIED
+
+ basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+
+ res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+ if (BOOST_UNLIKELY(!res))
+ {
+ err = ::GetLastError();
+ break;
+ }
+
+ // Try to set the flag again
+ res = set_file_information_by_handle(handle, file_disposition_info_ex_class, &info, sizeof(info));
+ if (BOOST_LIKELY(!!res))
+ {
+ err = 0u;
+ break;
+ }
+
+ err = ::GetLastError();
+
+ // Try to restore the read-only flag
+ basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+ set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+
+ break;
+ }
+ else if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED))
+ {
+ // Downgrade to the older implementation
+ impl = remove_disp;
+ filesystem::detail::atomic_store_relaxed(g_remove_impl_type, impl);
+ }
+ else
+ {
+ break;
+ }
+ }
+ BOOST_FALLTHROUGH;
+
+ default:
+ {
+ file_disposition_info info;
+ info.DeleteFile = true;
+ BOOL res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info));
+ if (BOOST_LIKELY(!!res))
+ {
+ err = 0u;
+ break;
+ }
+
+ err = ::GetLastError();
+ if (err == ERROR_ACCESS_DENIED)
+ {
+ // Check if the file is read-only and reset the attribute
+ file_basic_info basic_info;
+ res = get_file_information_by_handle_ex(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+ if (BOOST_UNLIKELY(!res || (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0))
+ break; // return ERROR_ACCESS_DENIED
+
+ basic_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
+
+ res = set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+ if (BOOST_UNLIKELY(!res))
+ {
+ err = ::GetLastError();
+ break;
+ }
+
+ // Try to set the flag again
+ res = set_file_information_by_handle(handle, file_disposition_info_class, &info, sizeof(info));
+ if (BOOST_LIKELY(!!res))
+ {
+ err = 0u;
+ break;
+ }
+
+ err = ::GetLastError();
+
+ // Try to restore the read-only flag
+ basic_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+ set_file_information_by_handle(handle, file_basic_info_class, &basic_info, sizeof(basic_info));
+ }
+
+ break;
+ }
+ }
+
+ return err;
+}
+
+//! remove() implementation for Windows Vista and newer
+inline bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec)
+{
+ unique_handle h(create_file_handle(
+ p,
+ DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
+ DWORD err = 0u;
+ if (BOOST_UNLIKELY(!h))
+ {
+ err = ::GetLastError();
+
+ return_error:
+ if (!not_found_error(err))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+
+ err = fs::detail::remove_nt6_by_handle(h.get(), impl);
+ if (BOOST_UNLIKELY(err != 0u))
+ goto return_error;
+
+ return true;
+}
+
+//! remove() implementation
+inline bool remove_impl(path const& p, error_code* ec)
+{
+ remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type);
+ if (BOOST_LIKELY(impl != remove_nt5))
+ {
+ return fs::detail::remove_nt6_impl(p, impl, ec);
+ }
+ else
+ {
+ const DWORD attrs = ::GetFileAttributesW(p.c_str());
+ if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
+ {
+ DWORD err = ::GetLastError();
+ if (!not_found_error(err))
+ emit_error(err, p, ec, "boost::filesystem::remove");
+
+ return false;
+ }
+
+ return fs::detail::remove_nt5_impl(p, attrs, ec);
+ }
+}
+
+//! remove_all() by handle implementation for Windows Vista and newer
+uintmax_t remove_all_nt6_by_handle(HANDLE h, path const& p, error_code* ec)
+{
+ error_code local_ec;
+ fs::file_status st(fs::detail::status_by_handle(h, p, &local_ec));
+ if (BOOST_UNLIKELY(st.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ uintmax_t count = 0u;
+ if (st.type() == fs::directory_file)
+ {
+ local_ec.clear();
+
+ fs::directory_iterator itr;
+ directory_iterator_params params;
+ params.dir_handle = h;
+ params.close_handle = false; // the caller will close the handle
+ fs::detail::directory_iterator_construct(itr, p, directory_options::_detail_no_follow, &params, &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, local_ec));
+
+ *ec = local_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ NtCreateFile_t* nt_create_file = filesystem::detail::atomic_load_relaxed(nt_create_file_api);
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ fs::path nested_path(itr->path());
+ unique_handle hh;
+ if (BOOST_LIKELY(nt_create_file != nullptr))
+ {
+ // Note: WinAPI methods like CreateFileW implicitly request SYNCHRONIZE access but NtCreateFile doesn't.
+ // Without SYNCHRONIZE access querying file attributes via GetFileInformationByHandleEx fails with ERROR_ACCESS_DENIED.
+ boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
+ (
+ hh,
+ h,
+ path_algorithms::filename_v4(nested_path),
+ 0u, // FileAttributes
+ FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ if (not_found_ntstatus(status))
+ goto next_entry;
+
+ DWORD err = translate_ntstatus(status);
+ emit_error(err, nested_path, ec, "boost::filesystem::remove_all");
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+ else
+ {
+ hh = create_file_handle(
+ nested_path,
+ FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
+
+ if (BOOST_UNLIKELY(!hh))
+ {
+ DWORD err = ::GetLastError();
+ if (not_found_error(err))
+ goto next_entry;
+
+ emit_error(err, nested_path, ec, "boost::filesystem::remove_all");
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+ count += fs::detail::remove_all_nt6_by_handle(hh.get(), nested_path, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ next_entry:
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+ DWORD err = fs::detail::remove_nt6_by_handle(h, fs::detail::atomic_load_relaxed(g_remove_impl_type));
+ if (BOOST_UNLIKELY(err != 0u))
+ {
+ emit_error(err, p, ec, "boost::filesystem::remove_all");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ ++count;
+ return count;
+}
+
+//! remove_all() implementation for Windows XP and older
+uintmax_t remove_all_nt5_impl(path const& p, error_code* ec)
+{
+ error_code dit_create_ec;
+ for (unsigned int attempt = 0u; attempt < remove_all_directory_replaced_retry_count; ++attempt)
+ {
+ const DWORD attrs = ::GetFileAttributesW(p.c_str());
+ if (BOOST_UNLIKELY(attrs == INVALID_FILE_ATTRIBUTES))
+ {
+ DWORD err = ::GetLastError();
+ if (not_found_error(err))
+ return 0u;
+
+ emit_error(err, p, ec, "boost::filesystem::remove_all");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ // Recurse into directories, but not into junctions or directory symlinks
+ const bool recurse = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 && (attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0;
+ uintmax_t count = 0u;
+ if (recurse)
+ {
+ fs::directory_iterator itr;
+ fs::detail::directory_iterator_construct(itr, p, directory_options::_detail_no_follow, nullptr, &dit_create_ec);
+ if (BOOST_UNLIKELY(!!dit_create_ec))
+ {
+ if (dit_create_ec == make_error_condition(system::errc::not_a_directory) ||
+ dit_create_ec == make_error_condition(system::errc::too_many_symbolic_link_levels))
+ {
+ continue;
+ }
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all", p, dit_create_ec));
+
+ *ec = dit_create_ec;
+ return static_cast< uintmax_t >(-1);
+ }
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ count += fs::detail::remove_all_nt5_impl(itr->path(), ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ fs::detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+ }
+ }
+
+ bool removed = fs::detail::remove_nt5_impl(p, attrs, ec);
+ if (ec && *ec)
+ return static_cast< uintmax_t >(-1);
+
+ count += removed;
+ return count;
+ }
+
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::remove_all: path cannot be opened as a directory", p, dit_create_ec));
+
+ *ec = dit_create_ec;
+ return static_cast< uintmax_t >(-1);
+}
+
+//! remove_all() implementation
+inline uintmax_t remove_all_impl(path const& p, error_code* ec)
+{
+ remove_impl_type impl = fs::detail::atomic_load_relaxed(g_remove_impl_type);
+ if (BOOST_LIKELY(impl != remove_nt5))
+ {
+ unique_handle h(create_file_handle(
+ p,
+ FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
+
+ if (BOOST_UNLIKELY(!h))
+ {
+ DWORD err = ::GetLastError();
+ if (not_found_error(err))
+ return 0u;
+
+ emit_error(err, p, ec, "boost::filesystem::remove_all");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ return fs::detail::remove_all_nt6_by_handle(h.get(), p, ec);
+ }
+
+ return fs::detail::remove_all_nt5_impl(p, ec);
+}
+
+inline BOOL resize_file_impl(const wchar_t* p, uintmax_t size)
+{
+ unique_handle h(CreateFileW(p, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+ LARGE_INTEGER sz;
+ sz.QuadPart = size;
+ return !!h && ::SetFilePointerEx(h.get(), sz, 0, FILE_BEGIN) && ::SetEndOfFile(h.get());
+}
+
+//! Converts NT path to a Win32 path
+inline path convert_nt_path_to_win32_path(const wchar_t* nt_path, std::size_t size)
+{
+ // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
+ // https://stackoverflow.com/questions/23041983/path-prefixes-and
+ //
+ // NT paths can be used to identify practically any named objects, devices, files, local and remote shares, etc.
+ // The path starts with a leading backslash and consists of one or more path elements separated with backslashes.
+ // The set of characters allowed in NT path elements is significantly larger than that of Win32 paths - basically,
+ // any character except the backslash is allowed. Path elements are case-insensitive.
+ //
+ // NT paths that start with the "\??\" prefix are used to indicate the current user's session namespace. The prefix
+ // indicates to the NT object manager to lookup the object relative to "\Sessions\0\DosDevices\[Logon Authentication ID]".
+ //
+ // There is also a special "\Global??\" prefix that refers to the system logon. User's session directory shadows
+ // the system logon directory, so that when the referenced object is not found in the user's namespace,
+ // system logon is looked up instead.
+ //
+ // There is a symlink "Global" in the user's session namespace that refers to the global namespace, so "\??\Global"
+ // effectively resolves to "\Global??". This allows Win32 applications to directly refer to the system objects,
+ // even if shadowed by the current user's logon object.
+ //
+ // NT paths can be used to reference not only local filesystems, but also devices and remote shares identifiable via
+ // UNC paths. For this, there is a special "UNC" device (which is a symlink to "\Device\Mup") in the system logon
+ // namespace, so "\??\UNC\host\share" (or "\??\Global\UNC\host\share", or "\Global??\UNC\host\share") is equivalent
+ // to "\\host\share".
+ //
+ // NT paths are not universally accepted by Win32 applications and APIs. For example, Far supports paths starting
+ // with "\??\" and "\??\Global\" but not with "\Global??\". As of Win10 21H1, File Explorer, cmd.exe and PowerShell
+ // don't support any of these. Given this, and that NT paths have a different set of allowed characters from Win32 paths,
+ // we should normally avoid exposing NT paths to users that expect Win32 paths.
+ //
+ // In Boost.Filesystem we only deal with NT paths that come from reparse points, such as symlinks and mount points,
+ // including directory junctions. It was observed that reparse points created by junction.exe and mklink use the "\??\"
+ // prefix for directory junctions and absolute symlink and unqualified relative path for relative symlinks.
+ // Absolute paths are using drive letters for mounted drives (e.g. "\??\C:\directory"), although it is possible
+ // to create a junction to an directory using a different way of identifying the filesystem (e.g.
+ // "\??\Volume{00000000-0000-0000-0000-000000000000}\directory").
+ // mklink does not support creating junctions pointing to a UNC path. junction.exe does create a junction that
+ // uses a seemingly invalid syntax like "\??\\\host\share", i.e. it basically does not expect an UNC path. It is not known
+ // if reparse points that refer to a UNC path are considered valid.
+ // There are reparse points created as mount points for local and remote filsystems (for example, a cloud storage mounted
+ // in the local filesystem). Such mount points have the form of "\??\Volume{00000000-0000-0000-0000-000000000000}\",
+ // "\??\Harddisk0Partition1\" or "\??\HarddiskVolume1\".
+ // Reparse points that refer directly to a global namespace (through "\??\Global\" or "\Global??\" prefixes) or
+ // devices (e.g. "\Device\HarddiskVolume1") have not been observed so far.
+
+ path win32_path;
+ std::size_t pos = 0u;
+ bool global_namespace = false;
+
+ // Check for the "\??\" prefix
+ if (size >= 4u &&
+ nt_path[0] == path::preferred_separator &&
+ nt_path[1] == questionmark &&
+ nt_path[2] == questionmark &&
+ nt_path[3] == path::preferred_separator)
+ {
+ pos = 4u;
+
+ // Check "Global"
+ if ((size - pos) >= 6u &&
+ (nt_path[pos] == L'G' || nt_path[pos] == L'g') &&
+ (nt_path[pos + 1] == L'l' || nt_path[pos + 1] == L'L') &&
+ (nt_path[pos + 2] == L'o' || nt_path[pos + 2] == L'O') &&
+ (nt_path[pos + 3] == L'b' || nt_path[pos + 3] == L'B') &&
+ (nt_path[pos + 4] == L'a' || nt_path[pos + 4] == L'A') &&
+ (nt_path[pos + 5] == L'l' || nt_path[pos + 5] == L'L'))
+ {
+ if ((size - pos) == 6u)
+ {
+ pos += 6u;
+ global_namespace = true;
+ }
+ else if (detail::is_directory_separator(nt_path[pos + 6u]))
+ {
+ pos += 7u;
+ global_namespace = true;
+ }
+ }
+ }
+ // Check for the "\Global??\" prefix
+ else if (size >= 10u &&
+ nt_path[0] == path::preferred_separator &&
+ (nt_path[1] == L'G' || nt_path[1] == L'g') &&
+ (nt_path[2] == L'l' || nt_path[2] == L'L') &&
+ (nt_path[3] == L'o' || nt_path[3] == L'O') &&
+ (nt_path[4] == L'b' || nt_path[4] == L'B') &&
+ (nt_path[5] == L'a' || nt_path[5] == L'A') &&
+ (nt_path[6] == L'l' || nt_path[6] == L'L') &&
+ nt_path[7] == questionmark &&
+ nt_path[8] == questionmark &&
+ nt_path[9] == path::preferred_separator)
+ {
+ pos = 10u;
+ global_namespace = true;
+ }
+
+ if (pos > 0u)
+ {
+ if ((size - pos) >= 2u &&
+ (
+ // Check if the following is a drive letter
+ (
+ detail::is_letter(nt_path[pos]) && nt_path[pos + 1u] == colon &&
+ ((size - pos) == 2u || detail::is_directory_separator(nt_path[pos + 2u]))
+ ) ||
+ // Check for an "incorrect" syntax for UNC path junction points
+ (
+ detail::is_directory_separator(nt_path[pos]) && detail::is_directory_separator(nt_path[pos + 1u]) &&
+ ((size - pos) == 2u || !detail::is_directory_separator(nt_path[pos + 2u]))
+ )
+ ))
+ {
+ // Strip the NT path prefix
+ goto done;
+ }
+
+ static const wchar_t win32_path_prefix[4u] = { path::preferred_separator, path::preferred_separator, questionmark, path::preferred_separator };
+
+ // Check for a UNC path
+ if ((size - pos) >= 4u &&
+ (nt_path[pos] == L'U' || nt_path[pos] == L'u') &&
+ (nt_path[pos + 1] == L'N' || nt_path[pos + 1] == L'n') &&
+ (nt_path[pos + 2] == L'C' || nt_path[pos + 2] == L'c') &&
+ nt_path[pos + 3] == path::preferred_separator)
+ {
+ win32_path.assign(win32_path_prefix, win32_path_prefix + 2);
+ pos += 4u;
+ goto done;
+ }
+
+ // This is some other NT path, possibly a volume mount point. Replace the NT prefix with a Win32 filesystem prefix "\\?\".
+ win32_path.assign(win32_path_prefix, win32_path_prefix + 4);
+ if (global_namespace)
+ {
+ static const wchar_t win32_path_global_prefix[7u] = { L'G', L'l', L'o', L'b', L'a', L'l', path::preferred_separator };
+ win32_path.concat(win32_path_global_prefix, win32_path_global_prefix + 7);
+ }
+ }
+
+done:
+ win32_path.concat(nt_path + pos, nt_path + size);
+ return win32_path;
+}
+
+#endif // defined(BOOST_POSIX_API)
+
+} // unnamed namespace
+
+} // namespace detail
+
+//--------------------------------------------------------------------------------------//
+// //
+// operations functions declared in operations.hpp //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace detail {
+
+BOOST_FILESYSTEM_DECL bool possible_large_file_size_support()
+{
+#ifdef BOOST_POSIX_API
+ typedef struct stat struct_stat;
+ return sizeof(struct_stat().st_size) > 4;
+#else
+ return true;
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+path absolute_v3(path const& p, path const& base, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ if (p.is_absolute())
+ return p;
+
+ // recursively calling absolute is sub-optimal, but is sure and simple
+ path abs_base = base;
+ if (!base.is_absolute())
+ {
+ path cur_path = detail::current_path(ec);
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ if (BOOST_UNLIKELY(!cur_path.is_absolute()))
+ {
+ system::error_code local_ec = system::errc::make_error_code(system::errc::invalid_argument);
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::absolute", p, base, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ abs_base = detail::absolute_v3(base, cur_path, ec);
+ if (ec && *ec)
+ goto return_empty_path;
+ }
+
+ if (p.empty())
+ return abs_base;
+
+ path res;
+ if (p.has_root_name())
+ res = p.root_name();
+ else
+ res = abs_base.root_name();
+
+ if (p.has_root_directory())
+ {
+ res.concat(p.root_directory());
+ }
+ else
+ {
+ res.concat(abs_base.root_directory());
+ path_algorithms::append_v4(res, abs_base.relative_path());
+ }
+
+ path p_relative_path(p.relative_path());
+ if (!p_relative_path.empty())
+ path_algorithms::append_v4(res, p_relative_path);
+
+ return res;
+}
+
+BOOST_FILESYSTEM_DECL
+path absolute_v4(path const& p, path const& base, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ if (p.is_absolute())
+ return p;
+
+ path abs_base = base;
+ if (!base.is_absolute())
+ {
+ path cur_path = detail::current_path(ec);
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ if (BOOST_UNLIKELY(!cur_path.is_absolute()))
+ {
+ system::error_code local_ec = system::errc::make_error_code(system::errc::invalid_argument);
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::absolute", p, base, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ abs_base = detail::absolute_v4(base, cur_path, ec);
+ if (ec && *ec)
+ goto return_empty_path;
+ }
+
+ path res;
+ if (p.has_root_name())
+ res = p.root_name();
+ else
+ res = abs_base.root_name();
+
+ if (p.has_root_directory())
+ {
+ res.concat(p.root_directory());
+ }
+ else
+ {
+ res.concat(abs_base.root_directory());
+ path_algorithms::append_v4(res, abs_base.relative_path());
+ }
+
+ path_algorithms::append_v4(res, p.relative_path());
+
+ return res;
+}
+
+BOOST_FILESYSTEM_DECL
+path canonical_v3(path const& p, path const& base, system::error_code* ec)
+{
+ path source(detail::absolute_v3(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ system::error_code local_ec;
+ file_status st(detail::status_impl(source, &local_ec));
+
+ if (st.type() == fs::file_not_found)
+ {
+ local_ec = system::errc::make_error_code(system::errc::no_such_file_or_directory);
+ goto fail_local_ec;
+ }
+ else if (local_ec)
+ {
+ fail_local_ec:
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::canonical", source, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ path root(source.root_path());
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+ unsigned int symlinks_allowed = symloop_max;
+ path result;
+ while (true)
+ {
+ for (path::iterator itr(source.begin()), end(source.end()); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) == 0)
+ continue;
+ if (path_algorithms::compare_v4(*itr, dot_dot_p) == 0)
+ {
+ if (path_algorithms::compare_v4(result, root) != 0)
+ result.remove_filename_and_trailing_separators();
+ continue;
+ }
+
+ if (itr->size() == 1u && detail::is_directory_separator(itr->native()[0]))
+ {
+ // Convert generic separator returned by the iterator for the root directory to
+ // the preferred separator. This is important on Windows, as in some cases,
+ // like paths for network shares and cloud storage mount points GetFileAttributesW
+ // will return "file not found" if the path contains forward slashes.
+ result += path::preferred_separator;
+ // We don't need to check for a symlink after adding a separator.
+ continue;
+ }
+
+ path_algorithms::append_v4(result, *itr);
+
+ // If we don't have an absolute path yet then don't check symlink status.
+ // This avoids checking "C:" which is "the current directory on drive C"
+ // and hence not what we want to check/resolve here.
+ if (!result.is_absolute())
+ continue;
+
+ st = detail::symlink_status_impl(result, ec);
+ if (ec && *ec)
+ goto return_empty_path;
+
+ if (is_symlink(st))
+ {
+ if (symlinks_allowed == 0)
+ {
+ local_ec = system::errc::make_error_code(system::errc::too_many_symbolic_link_levels);
+ goto fail_local_ec;
+ }
+
+ --symlinks_allowed;
+
+ path link(detail::read_symlink(result, ec));
+ if (ec && *ec)
+ goto return_empty_path;
+ result.remove_filename_and_trailing_separators();
+
+ if (link.is_absolute())
+ {
+ for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) != 0)
+ path_algorithms::append_v4(link, *itr);
+ }
+ source = link;
+ root = source.root_path();
+ }
+ else // link is relative
+ {
+ link.remove_trailing_separator();
+ if (path_algorithms::compare_v4(link, dot_p) == 0)
+ continue;
+
+ path new_source(result);
+ path_algorithms::append_v4(new_source, link);
+ for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) != 0)
+ path_algorithms::append_v4(new_source, *itr);
+ }
+ source = new_source;
+ }
+
+ // symlink causes scan to be restarted
+ goto restart_scan;
+ }
+ }
+
+ break;
+
+ restart_scan:
+ result.clear();
+ }
+
+ BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
+ return result;
+}
+
+BOOST_FILESYSTEM_DECL
+path canonical_v4(path const& p, path const& base, system::error_code* ec)
+{
+ path source(detail::absolute_v4(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ system::error_code local_ec;
+ file_status st(detail::status_impl(source, &local_ec));
+
+ if (st.type() == fs::file_not_found)
+ {
+ local_ec = system::errc::make_error_code(system::errc::no_such_file_or_directory);
+ goto fail_local_ec;
+ }
+ else if (local_ec)
+ {
+ fail_local_ec:
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::canonical", source, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ path root(source.root_path());
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+ unsigned int symlinks_allowed = symloop_max;
+ path result;
+ while (true)
+ {
+ for (path::iterator itr(source.begin()), end(source.end()); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) == 0)
+ continue;
+ if (path_algorithms::compare_v4(*itr, dot_dot_p) == 0)
+ {
+ if (path_algorithms::compare_v4(result, root) != 0)
+ result.remove_filename_and_trailing_separators();
+ continue;
+ }
+
+ if (itr->size() == 1u && detail::is_directory_separator(itr->native()[0]))
+ {
+ // Convert generic separator returned by the iterator for the root directory to
+ // the preferred separator. This is important on Windows, as in some cases,
+ // like paths for network shares and cloud storage mount points GetFileAttributesW
+ // will return "file not found" if the path contains forward slashes.
+ result += path::preferred_separator;
+ // We don't need to check for a symlink after adding a separator.
+ continue;
+ }
+
+ path_algorithms::append_v4(result, *itr);
+
+ // If we don't have an absolute path yet then don't check symlink status.
+ // This avoids checking "C:" which is "the current directory on drive C"
+ // and hence not what we want to check/resolve here.
+ if (!result.is_absolute())
+ continue;
+
+ st = detail::symlink_status_impl(result, ec);
+ if (ec && *ec)
+ goto return_empty_path;
+
+ if (is_symlink(st))
+ {
+ if (symlinks_allowed == 0)
+ {
+ local_ec = system::errc::make_error_code(system::errc::too_many_symbolic_link_levels);
+ goto fail_local_ec;
+ }
+
+ --symlinks_allowed;
+
+ path link(detail::read_symlink(result, ec));
+ if (ec && *ec)
+ goto return_empty_path;
+ result.remove_filename_and_trailing_separators();
+
+ if (link.is_absolute())
+ {
+ for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) != 0)
+ path_algorithms::append_v4(link, *itr);
+ }
+ source = link;
+ root = source.root_path();
+ }
+ else // link is relative
+ {
+ link.remove_trailing_separator();
+ if (path_algorithms::compare_v4(link, dot_p) == 0)
+ continue;
+
+ path new_source(result);
+ path_algorithms::append_v4(new_source, link);
+ for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr))
+ {
+ if (path_algorithms::compare_v4(*itr, dot_p) != 0)
+ path_algorithms::append_v4(new_source, *itr);
+ }
+ source = new_source;
+ }
+
+ // symlink causes scan to be restarted
+ goto restart_scan;
+ }
+ }
+
+ break;
+
+ restart_scan:
+ result.clear();
+ }
+
+ BOOST_ASSERT_MSG(result.is_absolute(), "canonical() implementation error; please report");
+ return result;
+}
+
+BOOST_FILESYSTEM_DECL
+void copy(path const& from, path const& to, copy_options options, system::error_code* ec)
+{
+ BOOST_ASSERT((((options & copy_options::overwrite_existing) != copy_options::none) +
+ ((options & copy_options::skip_existing) != copy_options::none) +
+ ((options & copy_options::update_existing) != copy_options::none)) <= 1);
+
+ BOOST_ASSERT((((options & copy_options::copy_symlinks) != copy_options::none) +
+ ((options & copy_options::skip_symlinks) != copy_options::none)) <= 1);
+
+ BOOST_ASSERT((((options & copy_options::directories_only) != copy_options::none) +
+ ((options & copy_options::create_symlinks) != copy_options::none) +
+ ((options & copy_options::create_hard_links) != copy_options::none)) <= 1);
+
+ if (ec)
+ ec->clear();
+
+ file_status from_stat;
+ if ((options & (copy_options::copy_symlinks | copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
+ {
+ from_stat = detail::symlink_status_impl(from, ec);
+ }
+ else
+ {
+ from_stat = detail::status_impl(from, ec);
+ }
+
+ if (ec && *ec)
+ return;
+
+ if (!exists(from_stat))
+ {
+ emit_error(BOOST_ERROR_FILE_NOT_FOUND, from, to, ec, "boost::filesystem::copy");
+ return;
+ }
+
+ if (is_symlink(from_stat))
+ {
+ if ((options & copy_options::skip_symlinks) != copy_options::none)
+ return;
+
+ if ((options & copy_options::copy_symlinks) == copy_options::none)
+ goto fail;
+
+ detail::copy_symlink(from, to, ec);
+ }
+ else if (is_regular_file(from_stat))
+ {
+ if ((options & copy_options::directories_only) != copy_options::none)
+ return;
+
+ if ((options & copy_options::create_symlinks) != copy_options::none)
+ {
+ const path* pfrom = &from;
+ path relative_from;
+ if (!from.is_absolute())
+ {
+ // Try to generate a relative path from the target location to the original file
+ path cur_dir = detail::current_path(ec);
+ if (ec && *ec)
+ return;
+ path abs_from = detail::absolute_v4(from.parent_path(), cur_dir, ec);
+ if (ec && *ec)
+ return;
+ path abs_to = to.parent_path();
+ if (!abs_to.is_absolute())
+ {
+ abs_to = detail::absolute_v4(abs_to, cur_dir, ec);
+ if (ec && *ec)
+ return;
+ }
+ relative_from = detail::relative(abs_from, abs_to, ec);
+ if (ec && *ec)
+ return;
+ if (path_algorithms::compare_v4(relative_from, dot_path()) != 0)
+ path_algorithms::append_v4(relative_from, path_algorithms::filename_v4(from));
+ else
+ relative_from = path_algorithms::filename_v4(from);
+ pfrom = &relative_from;
+ }
+ detail::create_symlink(*pfrom, to, ec);
+ return;
+ }
+
+ if ((options & copy_options::create_hard_links) != copy_options::none)
+ {
+ detail::create_hard_link(from, to, ec);
+ return;
+ }
+
+ error_code local_ec;
+ file_status to_stat;
+ if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
+ {
+ to_stat = detail::symlink_status_impl(to, &local_ec);
+ }
+ else
+ {
+ to_stat = detail::status_impl(to, &local_ec);
+ }
+
+ // Note: local_ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
+ // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
+ if (to_stat.type() == fs::status_error)
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
+ *ec = local_ec;
+ return;
+ }
+
+ if (is_directory(to_stat))
+ {
+ path target(to);
+ path_algorithms::append_v4(target, path_algorithms::filename_v4(from));
+ detail::copy_file(from, target, options, ec);
+ }
+ else
+ detail::copy_file(from, to, options, ec);
+ }
+ else if (is_directory(from_stat))
+ {
+ error_code local_ec;
+ if ((options & copy_options::create_symlinks) != copy_options::none)
+ {
+ local_ec = make_error_code(system::errc::is_a_directory);
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
+ *ec = local_ec;
+ return;
+ }
+
+ file_status to_stat;
+ if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none)
+ {
+ to_stat = detail::symlink_status_impl(to, &local_ec);
+ }
+ else
+ {
+ to_stat = detail::status_impl(to, &local_ec);
+ }
+
+ // Note: ec may be set by (symlink_)status() even in some non-fatal situations, e.g. when the file does not exist.
+ // OTOH, when it returns status_error, then a real error have happened and it must have set local_ec.
+ if (to_stat.type() == fs::status_error)
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy", from, to, local_ec));
+ *ec = local_ec;
+ return;
+ }
+
+ if (!exists(to_stat))
+ {
+ detail::create_directory(to, &from, ec);
+ if (ec && *ec)
+ return;
+ }
+
+ if ((options & copy_options::recursive) != copy_options::none || options == copy_options::none)
+ {
+ fs::directory_iterator itr;
+ detail::directory_iterator_construct(itr, from, directory_options::none, nullptr, ec);
+ if (ec && *ec)
+ return;
+
+ const fs::directory_iterator end_dit;
+ while (itr != end_dit)
+ {
+ path const& p = itr->path();
+ {
+ path target(to);
+ path_algorithms::append_v4(target, path_algorithms::filename_v4(p));
+ // Set _detail_recursing flag so that we don't recurse more than for one level deeper into the directory if options are copy_options::none
+ detail::copy(p, target, options | copy_options::_detail_recursing, ec);
+ }
+ if (ec && *ec)
+ return;
+
+ detail::directory_iterator_increment(itr, ec);
+ if (ec && *ec)
+ return;
+ }
+ }
+ }
+ else
+ {
+ fail:
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, from, to, ec, "boost::filesystem::copy");
+ }
+}
+
+BOOST_FILESYSTEM_DECL
+bool copy_file(path const& from, path const& to, copy_options options, error_code* ec)
+{
+ BOOST_ASSERT((((options & copy_options::overwrite_existing) != copy_options::none) +
+ ((options & copy_options::skip_existing) != copy_options::none) +
+ ((options & copy_options::update_existing) != copy_options::none)) <= 1);
+
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+ int err = 0;
+
+ // Note: Declare fd wrappers here so that errno is not clobbered by close() that may be called in fd wrapper destructors
+ boost::scope::unique_fd infile, outfile;
+
+ while (true)
+ {
+ infile.reset(::open(from.c_str(), O_RDONLY | O_CLOEXEC));
+ if (BOOST_UNLIKELY(!infile))
+ {
+ err = errno;
+ if (err == EINTR)
+ continue;
+
+ fail:
+ emit_error(err, from, to, ec, "boost::filesystem::copy_file");
+ return false;
+ }
+
+ break;
+ }
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ unsigned int statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO | STATX_SIZE;
+ if ((options & copy_options::update_existing) != copy_options::none)
+ statx_data_mask |= STATX_MTIME;
+
+ struct ::statx from_stat;
+ if (BOOST_UNLIKELY(invoke_statx(infile.get(), "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &from_stat) < 0))
+ {
+ fail_errno:
+ err = errno;
+ goto fail;
+ }
+
+ if (BOOST_UNLIKELY((from_stat.stx_mask & statx_data_mask) != statx_data_mask))
+ {
+ err = ENOSYS;
+ goto fail;
+ }
+#else
+ struct ::stat from_stat;
+ if (BOOST_UNLIKELY(::fstat(infile.get(), &from_stat) != 0))
+ {
+ fail_errno:
+ err = errno;
+ goto fail;
+ }
+#endif
+
+ const mode_t from_mode = get_mode(from_stat);
+ if (BOOST_UNLIKELY(!S_ISREG(from_mode)))
+ {
+ err = ENOSYS;
+ goto fail;
+ }
+
+ mode_t to_mode = from_mode & fs::perms_mask;
+#if !defined(BOOST_FILESYSTEM_USE_WASI)
+ // Enable writing for the newly created files. Having write permission set is important e.g. for NFS,
+ // which checks the file permission on the server, even if the client's file descriptor supports writing.
+ to_mode |= S_IWUSR;
+#endif
+ int oflag = O_WRONLY | O_CLOEXEC;
+
+ if ((options & copy_options::update_existing) != copy_options::none)
+ {
+ // Try opening the existing file without truncation to test the modification time later
+ while (true)
+ {
+ outfile.reset(::open(to.c_str(), oflag, to_mode));
+ if (!outfile)
+ {
+ err = errno;
+ if (err == EINTR)
+ continue;
+
+ if (err == ENOENT)
+ goto create_outfile;
+
+ goto fail;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ create_outfile:
+ oflag |= O_CREAT | O_TRUNC;
+ if (((options & copy_options::overwrite_existing) == copy_options::none ||
+ (options & copy_options::skip_existing) != copy_options::none) &&
+ (options & copy_options::update_existing) == copy_options::none)
+ {
+ oflag |= O_EXCL;
+ }
+
+ while (true)
+ {
+ outfile.reset(::open(to.c_str(), oflag, to_mode));
+ if (!outfile)
+ {
+ err = errno;
+ if (err == EINTR)
+ continue;
+
+ if (err == EEXIST && (options & copy_options::skip_existing) != copy_options::none)
+ return false;
+
+ goto fail;
+ }
+
+ break;
+ }
+ }
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ statx_data_mask = STATX_TYPE | STATX_MODE | STATX_INO;
+ if ((oflag & O_TRUNC) == 0)
+ {
+ // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
+ statx_data_mask |= STATX_MTIME;
+ }
+
+ struct ::statx to_stat;
+ if (BOOST_UNLIKELY(invoke_statx(outfile.get(), "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, statx_data_mask, &to_stat) < 0))
+ goto fail_errno;
+
+ if (BOOST_UNLIKELY((to_stat.stx_mask & statx_data_mask) != statx_data_mask))
+ {
+ err = ENOSYS;
+ goto fail;
+ }
+#else
+ struct ::stat to_stat;
+ if (BOOST_UNLIKELY(::fstat(outfile.get(), &to_stat) != 0))
+ goto fail_errno;
+#endif
+
+ to_mode = get_mode(to_stat);
+ if (BOOST_UNLIKELY(!S_ISREG(to_mode)))
+ {
+ err = ENOSYS;
+ goto fail;
+ }
+
+ if (BOOST_UNLIKELY(detail::equivalent_stat(from_stat, to_stat)))
+ {
+ err = EEXIST;
+ goto fail;
+ }
+
+ if ((oflag & O_TRUNC) == 0)
+ {
+ // O_TRUNC is not set if copy_options::update_existing is set and an existing file was opened.
+ // We need to check the last write times.
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ if (from_stat.stx_mtime.tv_sec < to_stat.stx_mtime.tv_sec || (from_stat.stx_mtime.tv_sec == to_stat.stx_mtime.tv_sec && from_stat.stx_mtime.tv_nsec <= to_stat.stx_mtime.tv_nsec))
+ return false;
+#elif defined(BOOST_FILESYSTEM_STAT_ST_MTIMENSEC)
+ // Modify time is available with nanosecond precision.
+ if (from_stat.st_mtime < to_stat.st_mtime || (from_stat.st_mtime == to_stat.st_mtime && from_stat.BOOST_FILESYSTEM_STAT_ST_MTIMENSEC <= to_stat.BOOST_FILESYSTEM_STAT_ST_MTIMENSEC))
+ return false;
+#else
+ if (from_stat.st_mtime <= to_stat.st_mtime)
+ return false;
+#endif
+
+ if (BOOST_UNLIKELY(::ftruncate(outfile.get(), 0) != 0))
+ goto fail_errno;
+ }
+
+ // Note: Use block size of the target file since it is most important for writing performance.
+ err = filesystem::detail::atomic_load_relaxed(filesystem::detail::copy_file_data)(infile.get(), outfile.get(), get_size(from_stat), get_blksize(to_stat));
+ if (BOOST_UNLIKELY(err != 0))
+ goto fail; // err already contains the error code
+
+#if !defined(BOOST_FILESYSTEM_USE_WASI)
+ // If we created a new file with an explicitly added S_IWUSR permission,
+ // we may need to update its mode bits to match the source file.
+ if ((to_mode & fs::perms_mask) != (from_mode & fs::perms_mask))
+ {
+ if (BOOST_UNLIKELY(::fchmod(outfile.get(), (from_mode & fs::perms_mask)) != 0 &&
+ (options & copy_options::ignore_attribute_errors) == copy_options::none))
+ {
+ goto fail_errno;
+ }
+ }
+#endif
+
+ if ((options & (copy_options::synchronize_data | copy_options::synchronize)) != copy_options::none)
+ {
+ if ((options & copy_options::synchronize) != copy_options::none)
+ err = full_sync(outfile.get());
+ else
+ err = data_sync(outfile.get());
+
+ if (BOOST_UNLIKELY(err != 0))
+ goto fail;
+ }
+
+ // We have to explicitly close the output file descriptor in order to handle a possible error returned from it. The error may indicate
+ // a failure of a prior write operation.
+ err = close_fd(outfile.get());
+ outfile.release();
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ // EINPROGRESS is an allowed error code in future POSIX revisions, according to https://www.austingroupbugs.net/view.php?id=529#c1200.
+ if (err != EINTR && err != EINPROGRESS)
+ goto fail;
+ }
+
+ return true;
+
+#else // defined(BOOST_POSIX_API)
+
+ DWORD copy_flags = 0u;
+ if ((options & copy_options::overwrite_existing) == copy_options::none ||
+ (options & copy_options::skip_existing) != copy_options::none)
+ {
+ copy_flags |= COPY_FILE_FAIL_IF_EXISTS;
+ }
+
+ if ((options & copy_options::update_existing) != copy_options::none)
+ {
+ // Create unique_handle wrappers here so that CloseHandle calls don't clobber error code returned by GetLastError
+ unique_handle hw_from, hw_to;
+
+ // See the comment in last_write_time regarding access rights used here for GetFileTime.
+ hw_from = create_file_handle(
+ from.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS);
+
+ FILETIME lwt_from;
+ if (!hw_from)
+ {
+ fail_last_error:
+ DWORD err = ::GetLastError();
+ emit_error(err, from, to, ec, "boost::filesystem::copy_file");
+ return false;
+ }
+
+ if (!::GetFileTime(hw_from.get(), nullptr, nullptr, &lwt_from))
+ goto fail_last_error;
+
+ hw_to = create_file_handle(
+ to.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS);
+
+ if (!!hw_to)
+ {
+ FILETIME lwt_to;
+ if (!::GetFileTime(hw_to.get(), nullptr, nullptr, &lwt_to))
+ goto fail_last_error;
+
+ ULONGLONG tfrom = (static_cast< ULONGLONG >(lwt_from.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_from.dwLowDateTime);
+ ULONGLONG tto = (static_cast< ULONGLONG >(lwt_to.dwHighDateTime) << 32) | static_cast< ULONGLONG >(lwt_to.dwLowDateTime);
+ if (tfrom <= tto)
+ return false;
+ }
+
+ copy_flags &= ~static_cast< DWORD >(COPY_FILE_FAIL_IF_EXISTS);
+ }
+
+ struct callback_context
+ {
+ DWORD flush_error;
+ };
+
+ struct local
+ {
+ //! Callback that is called to report progress of \c CopyFileExW
+ static DWORD WINAPI on_copy_file_progress(
+ LARGE_INTEGER total_file_size,
+ LARGE_INTEGER total_bytes_transferred,
+ LARGE_INTEGER stream_size,
+ LARGE_INTEGER stream_bytes_transferred,
+ DWORD stream_number,
+ DWORD callback_reason,
+ HANDLE from_handle,
+ HANDLE to_handle,
+ LPVOID ctx)
+ {
+ // For each stream, CopyFileExW will open a separate pair of file handles, so we need to flush each stream separately.
+ if (stream_bytes_transferred.QuadPart == stream_size.QuadPart)
+ {
+ BOOL res = ::FlushFileBuffers(to_handle);
+ if (BOOST_UNLIKELY(!res))
+ {
+ callback_context* context = static_cast< callback_context* >(ctx);
+ if (BOOST_LIKELY(context->flush_error == 0u))
+ context->flush_error = ::GetLastError();
+ }
+ }
+
+ return PROGRESS_CONTINUE;
+ }
+ };
+
+ callback_context cb_context = {};
+ LPPROGRESS_ROUTINE cb = nullptr;
+ LPVOID cb_ctx = nullptr;
+
+ if ((options & (copy_options::synchronize_data | copy_options::synchronize)) != copy_options::none)
+ {
+ cb = &local::on_copy_file_progress;
+ cb_ctx = &cb_context;
+ }
+
+ BOOL cancelled = FALSE;
+ BOOL res = ::CopyFileExW(from.c_str(), to.c_str(), cb, cb_ctx, &cancelled, copy_flags);
+ DWORD err;
+ if (BOOST_UNLIKELY(!res))
+ {
+ err = ::GetLastError();
+ if ((err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS) && (options & copy_options::skip_existing) != copy_options::none)
+ return false;
+
+ copy_failed:
+ emit_error(err, from, to, ec, "boost::filesystem::copy_file");
+ return false;
+ }
+
+ if (BOOST_UNLIKELY(cb_context.flush_error != 0u))
+ {
+ err = cb_context.flush_error;
+ goto copy_failed;
+ }
+
+ return true;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+void copy_symlink(path const& existing_symlink, path const& new_symlink, system::error_code* ec)
+{
+ path p(read_symlink(existing_symlink, ec));
+ if (ec && *ec)
+ return;
+ create_symlink(p, new_symlink, ec);
+}
+
+BOOST_FILESYSTEM_DECL
+bool create_directories(path const& p, system::error_code* ec)
+{
+ if (p.empty())
+ {
+ if (!ec)
+ {
+ BOOST_FILESYSTEM_THROW(filesystem_error(
+ "boost::filesystem::create_directories", p,
+ system::errc::make_error_code(system::errc::invalid_argument)));
+ }
+ ec->assign(system::errc::invalid_argument, system::generic_category());
+ return false;
+ }
+
+ if (ec)
+ ec->clear();
+
+ path::const_iterator e(p.end()), it(e);
+ path parent(p);
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+ error_code local_ec;
+
+ // Find the initial part of the path that exists
+ for (path fname = path_algorithms::filename_v4(parent); parent.has_relative_path(); fname = path_algorithms::filename_v4(parent))
+ {
+ if (!fname.empty() && path_algorithms::compare_v4(fname, dot_p) != 0 && path_algorithms::compare_v4(fname, dot_dot_p) != 0)
+ {
+ file_status existing_status = detail::status_impl(parent, &local_ec);
+
+ if (existing_status.type() == directory_file)
+ {
+ break;
+ }
+ else if (BOOST_UNLIKELY(existing_status.type() == status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directories", p, parent, local_ec));
+ *ec = local_ec;
+ return false;
+ }
+ }
+
+ path_algorithms::decrement_v4(it);
+ parent.remove_filename_and_trailing_separators();
+ }
+
+ // Create missing directories
+ bool created = false;
+ for (; it != e; path_algorithms::increment_v4(it))
+ {
+ path const& fname = *it;
+ path_algorithms::append_v4(parent, fname);
+ if (!fname.empty() && path_algorithms::compare_v4(fname, dot_p) != 0 && path_algorithms::compare_v4(fname, dot_dot_p) != 0)
+ {
+ created = detail::create_directory(parent, nullptr, &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::create_directories", p, parent, local_ec));
+ *ec = local_ec;
+ return false;
+ }
+ }
+ }
+
+ return created;
+}
+
+BOOST_FILESYSTEM_DECL
+bool create_directory(path const& p, const path* existing, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+ mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
+ if (existing)
+ {
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx existing_stat;
+ if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, existing->c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_MODE, &existing_stat) < 0))
+ {
+ emit_error(errno, p, *existing, ec, "boost::filesystem::create_directory");
+ return false;
+ }
+
+ if (BOOST_UNLIKELY((existing_stat.stx_mask & (STATX_TYPE | STATX_MODE)) != (STATX_TYPE | STATX_MODE)))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, *existing, ec, "boost::filesystem::create_directory");
+ return false;
+ }
+#else
+ struct ::stat existing_stat;
+ if (::stat(existing->c_str(), &existing_stat) < 0)
+ {
+ emit_error(errno, p, *existing, ec, "boost::filesystem::create_directory");
+ return false;
+ }
+#endif
+
+ const mode_t existing_mode = get_mode(existing_stat);
+ if (!S_ISDIR(existing_mode))
+ {
+ emit_error(ENOTDIR, p, *existing, ec, "boost::filesystem::create_directory");
+ return false;
+ }
+
+ mode = existing_mode;
+ }
+
+ if (::mkdir(p.c_str(), mode) == 0)
+ return true;
+
+#else // defined(BOOST_POSIX_API)
+
+ BOOL res;
+ if (existing)
+ res = ::CreateDirectoryExW(existing->c_str(), p.c_str(), nullptr);
+ else
+ res = ::CreateDirectoryW(p.c_str(), nullptr);
+
+ if (res)
+ return true;
+
+#endif // defined(BOOST_POSIX_API)
+
+ // attempt to create directory failed
+ err_t errval = BOOST_ERRNO; // save reason for failure
+ error_code dummy;
+
+ if (is_directory(p, dummy))
+ return false;
+
+ // attempt to create directory failed && it doesn't already exist
+ emit_error(errval, p, ec, "boost::filesystem::create_directory");
+ return false;
+}
+
+BOOST_FILESYSTEM_DECL
+void create_directory_symlink(path const& to, path const& from, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+ int err = ::symlink(to.c_str(), from.c_str());
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ emit_error(err, to, from, ec, "boost::filesystem::create_directory_symlink");
+ }
+#else
+ // see if actually supported by Windows runtime dll
+ if (!create_symbolic_link_api)
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_directory_symlink");
+ return;
+ }
+
+ if (!create_symbolic_link_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))
+ {
+ emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_directory_symlink");
+ }
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+void create_hard_link(path const& to, path const& from, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+ int err = ::link(to.c_str(), from.c_str());
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ emit_error(err, to, from, ec, "boost::filesystem::create_hard_link");
+ }
+#else
+ // see if actually supported by Windows runtime dll
+ CreateHardLinkW_t* chl_api = filesystem::detail::atomic_load_relaxed(create_hard_link_api);
+ if (BOOST_UNLIKELY(!chl_api))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_hard_link");
+ return;
+ }
+
+ if (BOOST_UNLIKELY(!chl_api(from.c_str(), to.c_str(), nullptr)))
+ {
+ emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_hard_link");
+ }
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+void create_symlink(path const& to, path const& from, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+ int err = ::symlink(to.c_str(), from.c_str());
+ if (BOOST_UNLIKELY(err < 0))
+ {
+ err = errno;
+ emit_error(err, to, from, ec, "boost::filesystem::create_symlink");
+ }
+#else
+ // see if actually supported by Windows runtime dll
+ CreateSymbolicLinkW_t* csl_api = filesystem::detail::atomic_load_relaxed(create_symbolic_link_api);
+ if (BOOST_UNLIKELY(!csl_api))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, to, from, ec, "boost::filesystem::create_symlink");
+ return;
+ }
+
+ if (BOOST_UNLIKELY(!csl_api(from.c_str(), to.c_str(), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)))
+ {
+ emit_error(BOOST_ERRNO, to, from, ec, "boost::filesystem::create_symlink");
+ }
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+path current_path(error_code* ec)
+{
+#if defined(BOOST_FILESYSTEM_USE_WASI)
+ // Windows CE has no current directory, so everything's relative to the root of the directory tree.
+ // WASI also does not support current path.
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, ec, "boost::filesystem::current_path");
+ return path();
+#elif defined(BOOST_POSIX_API)
+ struct local
+ {
+ static bool getcwd_error(error_code* ec)
+ {
+ const int err = errno;
+ return error((err != ERANGE
+#if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))
+ // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set
+ && err != 0
+#endif
+ ) ? err : 0,
+ ec, "boost::filesystem::current_path");
+ }
+ };
+
+ path cur;
+ char small_buf[small_path_size];
+ const char* p = ::getcwd(small_buf, sizeof(small_buf));
+ if (BOOST_LIKELY(!!p))
+ {
+ cur = p;
+ if (ec)
+ ec->clear();
+ }
+ else if (BOOST_LIKELY(!local::getcwd_error(ec)))
+ {
+ for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
+ {
+ if (BOOST_UNLIKELY(path_max > absolute_path_max))
+ {
+ emit_error(ENAMETOOLONG, ec, "boost::filesystem::current_path");
+ break;
+ }
+
+ std::unique_ptr< char[] > buf(new char[path_max]);
+ p = ::getcwd(buf.get(), path_max);
+ if (BOOST_LIKELY(!!p))
+ {
+ cur = buf.get();
+ if (ec)
+ ec->clear();
+ break;
+ }
+ else if (BOOST_UNLIKELY(local::getcwd_error(ec)))
+ {
+ break;
+ }
+ }
+ }
+
+ return cur;
+#else
+ DWORD sz;
+ if ((sz = ::GetCurrentDirectoryW(0, nullptr)) == 0)
+ sz = 1;
+ std::unique_ptr< path::value_type[] > buf(new path::value_type[sz]);
+ error(::GetCurrentDirectoryW(sz, buf.get()) == 0 ? BOOST_ERRNO : 0, ec, "boost::filesystem::current_path");
+ return path(buf.get());
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+void current_path(path const& p, system::error_code* ec)
+{
+#if defined(BOOST_FILESYSTEM_USE_WASI)
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::current_path");
+#else
+ error(!BOOST_SET_CURRENT_DIRECTORY(p.c_str()) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::current_path");
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+bool equivalent_v3(path const& p1, path const& p2, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+ // p2 is done first, so any error reported is for p1
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx s2;
+ int e2 = invoke_statx(AT_FDCWD, p2.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s2);
+ if (BOOST_LIKELY(e2 == 0))
+ {
+ if (BOOST_UNLIKELY((s2.stx_mask & STATX_INO) != STATX_INO))
+ {
+ fail_unsupported:
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p1, p2, ec, "boost::filesystem::equivalent");
+ return false;
+ }
+ }
+
+ struct ::statx s1;
+ int e1 = invoke_statx(AT_FDCWD, p1.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s1);
+ if (BOOST_LIKELY(e1 == 0))
+ {
+ if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
+ goto fail_unsupported;
+ }
+#else
+ struct ::stat s2;
+ int e2 = ::stat(p2.c_str(), &s2);
+ struct ::stat s1;
+ int e1 = ::stat(p1.c_str(), &s1);
+#endif
+
+ if (BOOST_UNLIKELY(e1 != 0 || e2 != 0))
+ {
+ // if one is invalid and the other isn't then they aren't equivalent,
+ // but if both are invalid then it is an error
+ if (e1 != 0 && e2 != 0)
+ {
+ int err = errno;
+ emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
+ }
+ return false;
+ }
+
+ return equivalent_stat(s1, s2);
+
+#else // Windows
+
+ // Thanks to Jeremy Maitin-Shepard for much help and for permission to
+ // base the equivalent() implementation on portions of his
+ // file-equivalence-win32.cpp experimental code.
+
+ // Note well: Physical location on external media is part of the
+ // equivalence criteria. If there are no open handles, physical location
+ // can change due to defragmentation or other relocations. Thus handles
+ // must be held open until location information for both paths has
+ // been retrieved.
+
+ // p2 is done first, so any error reported is for p1
+ unique_handle h2(create_file_handle(
+ p2.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ unique_handle h1(create_file_handle(
+ p1.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ if (BOOST_UNLIKELY(!h1 || !h2))
+ {
+ // if one is invalid and the other isn't, then they aren't equivalent,
+ // but if both are invalid then it is an error
+ if (!h1 && !h2)
+ error(BOOST_ERRNO, p1, p2, ec, "boost::filesystem::equivalent");
+ return false;
+ }
+
+ // at this point, both handles are known to be valid
+
+ BY_HANDLE_FILE_INFORMATION info1, info2;
+
+ if (error(!::GetFileInformationByHandle(h1.get(), &info1) ? BOOST_ERRNO : 0, p1, p2, ec, "boost::filesystem::equivalent"))
+ return false;
+
+ if (error(!::GetFileInformationByHandle(h2.get(), &info2) ? BOOST_ERRNO : 0, p1, p2, ec, "boost::filesystem::equivalent"))
+ return false;
+
+ // In theory, volume serial numbers are sufficient to distinguish between
+ // devices, but in practice VSN's are sometimes duplicated, so last write
+ // time and file size are also checked.
+ return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber &&
+ info1.nFileIndexHigh == info2.nFileIndexHigh &&
+ info1.nFileIndexLow == info2.nFileIndexLow &&
+ info1.nFileSizeHigh == info2.nFileSizeHigh &&
+ info1.nFileSizeLow == info2.nFileSizeLow &&
+ info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime &&
+ info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime;
+
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+bool equivalent_v4(path const& p1, path const& p2, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx s1;
+ int err = invoke_statx(AT_FDCWD, p1.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s1);
+ if (BOOST_UNLIKELY(err != 0))
+ {
+ fail_errno:
+ err = errno;
+ fail_err:
+ emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
+ return false;
+ }
+
+ if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
+ {
+ fail_unsupported:
+ err = BOOST_ERROR_NOT_SUPPORTED;
+ goto fail_err;
+ }
+
+ struct ::statx s2;
+ err = invoke_statx(AT_FDCWD, p2.c_str(), AT_NO_AUTOMOUNT, STATX_INO, &s2);
+ if (BOOST_UNLIKELY(err != 0))
+ goto fail_errno;
+
+ if (BOOST_UNLIKELY((s1.stx_mask & STATX_INO) != STATX_INO))
+ goto fail_unsupported;
+#else
+ struct ::stat s1;
+ int err = ::stat(p1.c_str(), &s1);
+ if (BOOST_UNLIKELY(err != 0))
+ {
+ fail_errno:
+ err = errno;
+ emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
+ return false;
+ }
+
+ struct ::stat s2;
+ err = ::stat(p2.c_str(), &s2);
+ if (BOOST_UNLIKELY(err != 0))
+ goto fail_errno;
+#endif
+
+ return equivalent_stat(s1, s2);
+
+#else // Windows
+
+ // Thanks to Jeremy Maitin-Shepard for much help and for permission to
+ // base the equivalent() implementation on portions of his
+ // file-equivalence-win32.cpp experimental code.
+
+ // Note well: Physical location on external media is part of the
+ // equivalence criteria. If there are no open handles, physical location
+ // can change due to defragmentation or other relocations. Thus handles
+ // must be held open until location information for both paths has
+ // been retrieved.
+
+ unique_handle h1(create_file_handle(
+ p1.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+ if (BOOST_UNLIKELY(!h1))
+ {
+ fail_errno:
+ err_t err = BOOST_ERRNO;
+ emit_error(err, p1, p2, ec, "boost::filesystem::equivalent");
+ return false;
+ }
+
+ unique_handle h2(create_file_handle(
+ p2.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+ if (BOOST_UNLIKELY(!h2))
+ goto fail_errno;
+
+ BY_HANDLE_FILE_INFORMATION info1;
+ if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h1.get(), &info1)))
+ goto fail_errno;
+
+ BY_HANDLE_FILE_INFORMATION info2;
+ if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h2.get(), &info2)))
+ goto fail_errno;
+
+ // In theory, volume serial numbers are sufficient to distinguish between
+ // devices, but in practice VSN's are sometimes duplicated, so last write
+ // time and file size are also checked.
+ return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber &&
+ info1.nFileIndexHigh == info2.nFileIndexHigh &&
+ info1.nFileIndexLow == info2.nFileIndexLow &&
+ info1.nFileSizeHigh == info2.nFileSizeHigh &&
+ info1.nFileSizeLow == info2.nFileSizeLow &&
+ info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime &&
+ info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime;
+
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+uintmax_t file_size(path const& p, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx path_stat;
+ int err;
+ if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
+ {
+ err = errno;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::file_size");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_SIZE)) != (STATX_TYPE | STATX_SIZE) || !S_ISREG(path_stat.stx_mode)))
+ {
+ err = BOOST_ERROR_NOT_SUPPORTED;
+ goto fail;
+ }
+#else
+ struct ::stat path_stat;
+ int err;
+ if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
+ {
+ err = errno;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::file_size");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ if (BOOST_UNLIKELY(!S_ISREG(path_stat.st_mode)))
+ {
+ err = BOOST_ERROR_NOT_SUPPORTED;
+ goto fail;
+ }
+#endif
+
+ return get_size(path_stat);
+
+#else // defined(BOOST_POSIX_API)
+
+ // assume uintmax_t is 64-bits on all Windows compilers
+
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ DWORD err;
+ if (BOOST_UNLIKELY(!h))
+ {
+ fail_errno:
+ err = BOOST_ERRNO;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::file_size");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ BY_HANDLE_FILE_INFORMATION info;
+ if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h.get(), &info)))
+ goto fail_errno;
+
+ if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0u))
+ {
+ err = ERROR_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ return (static_cast< uintmax_t >(info.nFileSizeHigh) << 32u) | info.nFileSizeLow;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+uintmax_t hard_link_count(path const& p, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx path_stat;
+ if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_NLINK, &path_stat) < 0))
+ {
+ emit_error(errno, p, ec, "boost::filesystem::hard_link_count");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ if (BOOST_UNLIKELY((path_stat.stx_mask & STATX_NLINK) != STATX_NLINK))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::hard_link_count");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ return static_cast< uintmax_t >(path_stat.stx_nlink);
+#else
+ struct ::stat path_stat;
+ if (BOOST_UNLIKELY(::stat(p.c_str(), &path_stat) < 0))
+ {
+ emit_error(errno, p, ec, "boost::filesystem::hard_link_count");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ return static_cast< uintmax_t >(path_stat.st_nlink);
+#endif
+
+#else // defined(BOOST_POSIX_API)
+
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ if (BOOST_UNLIKELY(!h))
+ {
+ fail_errno:
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::hard_link_count");
+ return static_cast< uintmax_t >(-1);
+ }
+
+ // Link count info is only available through GetFileInformationByHandle
+ BY_HANDLE_FILE_INFORMATION info;
+ if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h.get(), &info)))
+ goto fail_errno;
+
+ return static_cast< uintmax_t >(info.nNumberOfLinks);
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+path initial_path(error_code* ec)
+{
+ static path init_path;
+ if (init_path.empty())
+ init_path = current_path(ec);
+ else if (ec)
+ ec->clear();
+ return init_path;
+}
+
+//! Tests if the directory is empty. Implemented in directory.cpp.
+bool is_empty_directory
+(
+#if defined(BOOST_POSIX_API)
+ boost::scope::unique_fd&& fd,
+#else
+ unique_handle&& h,
+#endif
+ path const& p,
+ error_code* ec
+);
+
+BOOST_FILESYSTEM_DECL
+bool is_empty(path const& p, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+ boost::scope::unique_fd file;
+ int err = 0;
+ while (true)
+ {
+ file.reset(::open(p.c_str(), O_RDONLY | O_CLOEXEC));
+ if (BOOST_UNLIKELY(!file))
+ {
+ err = errno;
+ if (err == EINTR)
+ continue;
+
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::is_empty");
+ return false;
+ }
+
+ break;
+ }
+
+#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
+ if (BOOST_UNLIKELY(::fcntl(file.get(), F_SETFD, FD_CLOEXEC) < 0))
+ {
+ err = errno;
+ goto fail;
+ }
+#endif
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx path_stat;
+ if (BOOST_UNLIKELY(invoke_statx(file.get(), "", AT_EMPTY_PATH | AT_NO_AUTOMOUNT, STATX_TYPE | STATX_SIZE, &path_stat) < 0))
+ {
+ err = errno;
+ goto fail;
+ }
+
+ if (BOOST_UNLIKELY((path_stat.stx_mask & (STATX_TYPE | STATX_SIZE)) != (STATX_TYPE | STATX_SIZE)))
+ {
+ err = BOOST_ERROR_NOT_SUPPORTED;
+ goto fail;
+ }
+#else
+ struct ::stat path_stat;
+ if (BOOST_UNLIKELY(::fstat(file.get(), &path_stat) < 0))
+ {
+ err = errno;
+ goto fail;
+ }
+#endif
+
+ const mode_t mode = get_mode(path_stat);
+ if (S_ISDIR(mode))
+ return is_empty_directory(std::move(file), p, ec);
+
+ if (BOOST_UNLIKELY(!S_ISREG(mode)))
+ {
+ err = BOOST_ERROR_NOT_SUPPORTED;
+ goto fail;
+ }
+
+ return get_size(path_stat) == 0u;
+
+#else // defined(BOOST_POSIX_API)
+
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ if (BOOST_UNLIKELY(!h))
+ {
+ fail_errno:
+ const DWORD err = BOOST_ERRNO;
+ emit_error(err, p, ec, "boost::filesystem::is_empty");
+ return false;
+ }
+
+ BY_HANDLE_FILE_INFORMATION info;
+ if (BOOST_UNLIKELY(!::GetFileInformationByHandle(h.get(), &info)))
+ goto fail_errno;
+
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0u)
+ return is_empty_directory(std::move(h), p, ec);
+
+ return (info.nFileSizeHigh | info.nFileSizeLow) == 0u;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+std::time_t creation_time(path const& p, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx stx;
+ if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_BTIME, &stx) < 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ if (BOOST_UNLIKELY((stx.stx_mask & STATX_BTIME) != STATX_BTIME))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::creation_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ return stx.stx_btime.tv_sec;
+#elif defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIME) && defined(BOOST_FILESYSTEM_STAT_ST_BIRTHTIMENSEC)
+ struct ::stat st;
+ if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::creation_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ return st.BOOST_FILESYSTEM_STAT_ST_BIRTHTIME;
+#else
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::creation_time");
+ return (std::numeric_limits< std::time_t >::min)();
+#endif
+
+#else // defined(BOOST_POSIX_API)
+
+ // See the comment in last_write_time regarding access rights used here for GetFileTime.
+ unique_handle hw(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ DWORD err;
+ if (BOOST_UNLIKELY(!hw))
+ {
+ fail_errno:
+ err = BOOST_ERRNO;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::creation_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+
+ FILETIME ct;
+ if (BOOST_UNLIKELY(!::GetFileTime(hw.get(), &ct, nullptr, nullptr)))
+ goto fail_errno;
+
+ std::time_t t;
+ err = to_time_t(ct, t);
+ if (BOOST_UNLIKELY(err != 0u))
+ goto fail;
+
+ return t;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+std::time_t last_write_time(path const& p, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_USE_STATX)
+ struct ::statx stx;
+ if (BOOST_UNLIKELY(invoke_statx(AT_FDCWD, p.c_str(), AT_NO_AUTOMOUNT, STATX_MTIME, &stx) < 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ if (BOOST_UNLIKELY((stx.stx_mask & STATX_MTIME) != STATX_MTIME))
+ {
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::last_write_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ return stx.stx_mtime.tv_sec;
+#else
+ struct ::stat st;
+ if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+ return st.st_mtime;
+#endif
+
+#else // defined(BOOST_POSIX_API)
+
+ // GetFileTime is documented to require GENERIC_READ access right, but this causes problems if the file
+ // is opened by another process without FILE_SHARE_READ. In practice, FILE_READ_ATTRIBUTES works, and
+ // FILE_READ_EA is also added for good measure, in case if it matters for SMBv1.
+ unique_handle hw(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ DWORD err;
+ if (BOOST_UNLIKELY(!hw))
+ {
+ fail_errno:
+ err = BOOST_ERRNO;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::last_write_time");
+ return (std::numeric_limits< std::time_t >::min)();
+ }
+
+ FILETIME lwt;
+ if (BOOST_UNLIKELY(!::GetFileTime(hw.get(), nullptr, nullptr, &lwt)))
+ goto fail_errno;
+
+ std::time_t t;
+ err = to_time_t(lwt, t);
+ if (BOOST_UNLIKELY(err != 0u))
+ goto fail;
+
+ return t;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+BOOST_FILESYSTEM_DECL
+void last_write_time(path const& p, const std::time_t new_time, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+ struct timespec times[2] = {};
+
+ // Keep the last access time unchanged
+ times[0].tv_nsec = UTIME_OMIT;
+
+ times[1].tv_sec = new_time;
+
+ if (BOOST_UNLIKELY(::utimensat(AT_FDCWD, p.c_str(), times, 0) != 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
+ return;
+ }
+
+#else // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+ struct ::stat st;
+ if (BOOST_UNLIKELY(::stat(p.c_str(), &st) < 0))
+ {
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
+ return;
+ }
+
+ ::utimbuf buf;
+ buf.actime = st.st_atime; // utime() updates access time too :-(
+ buf.modtime = new_time;
+ if (BOOST_UNLIKELY(::utime(p.c_str(), &buf) < 0))
+ emit_error(BOOST_ERRNO, p, ec, "boost::filesystem::last_write_time");
+
+#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+#else // defined(BOOST_POSIX_API)
+
+ unique_handle hw(create_file_handle(
+ p.c_str(),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS));
+
+ DWORD err;
+ if (BOOST_UNLIKELY(!hw))
+ {
+ fail_errno:
+ err = BOOST_ERRNO;
+ fail:
+ emit_error(err, p, ec, "boost::filesystem::last_write_time");
+ return;
+ }
+
+ FILETIME lwt;
+ err = to_FILETIME(new_time, lwt);
+ if (BOOST_UNLIKELY(err != 0u))
+ goto fail;
+
+ if (BOOST_UNLIKELY(!::SetFileTime(hw.get(), nullptr, nullptr, &lwt)))
+ goto fail_errno;
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+#ifdef BOOST_POSIX_API
+const perms active_bits(all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit);
+inline mode_t mode_cast(perms prms)
+{
+ return prms & active_bits;
+}
+#endif
+
+BOOST_FILESYSTEM_DECL
+void permissions(path const& p, perms prms, system::error_code* ec)
+{
+ BOOST_ASSERT_MSG(!((prms & add_perms) && (prms & remove_perms)), "add_perms and remove_perms are mutually exclusive");
+
+ if ((prms & add_perms) && (prms & remove_perms)) // precondition failed
+ return;
+
+#if defined(BOOST_FILESYSTEM_USE_WASI)
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::permissions");
+#elif defined(BOOST_POSIX_API)
+ error_code local_ec;
+ file_status current_status((prms & symlink_perms) ? detail::symlink_status_impl(p, &local_ec) : detail::status_impl(p, &local_ec));
+ if (local_ec)
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::permissions", p, local_ec));
+
+ *ec = local_ec;
+ return;
+ }
+
+ if (prms & add_perms)
+ prms |= current_status.permissions();
+ else if (prms & remove_perms)
+ prms = current_status.permissions() & ~prms;
+
+ // OS X <10.10, iOS <8.0 and some other platforms don't support fchmodat().
+ // Solaris (SunPro and gcc) only support fchmodat() on Solaris 11 and higher,
+ // and a runtime check is too much trouble.
+ // Linux does not support permissions on symbolic links and has no plans to
+ // support them in the future. The chmod() code is thus more practical,
+ // rather than always hitting ENOTSUP when sending in AT_SYMLINK_NO_FOLLOW.
+ // - See the 3rd paragraph of
+ // "Symbolic link ownership, permissions, and timestamps" at:
+ // "http://man7.org/linux/man-pages/man7/symlink.7.html"
+ // - See the fchmodat() Linux man page:
+ // "http://man7.org/linux/man-pages/man2/fchmodat.2.html"
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) && \
+ !(defined(__SUNPRO_CC) || defined(__sun) || defined(sun)) && \
+ !(defined(linux) || defined(__linux) || defined(__linux__)) && \
+ !(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101000) && \
+ !(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 80000) && \
+ !(defined(__rtems__)) && \
+ !(defined(__QNX__) && (_NTO_VERSION <= 700))
+ if (::fchmodat(AT_FDCWD, p.c_str(), mode_cast(prms), !(prms & symlink_perms) ? 0 : AT_SYMLINK_NOFOLLOW))
+#else // fallback if fchmodat() not supported
+ if (::chmod(p.c_str(), mode_cast(prms)))
+#endif
+ {
+ const int err = errno;
+ if (!ec)
+ {
+ BOOST_FILESYSTEM_THROW(filesystem_error(
+ "boost::filesystem::permissions", p, error_code(err, system::generic_category())));
+ }
+
+ ec->assign(err, system::generic_category());
+ }
+
+#else // Windows
+
+ // if not going to alter FILE_ATTRIBUTE_READONLY, just return
+ if (!(!((prms & (add_perms | remove_perms))) || (prms & (owner_write | group_write | others_write))))
+ return;
+
+ DWORD attr = ::GetFileAttributesW(p.c_str());
+
+ if (error(attr == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions"))
+ return;
+
+ if (prms & add_perms)
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else if (prms & remove_perms)
+ attr |= FILE_ATTRIBUTE_READONLY;
+ else if (prms & (owner_write | group_write | others_write))
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ attr |= FILE_ATTRIBUTE_READONLY;
+
+ error(::SetFileAttributesW(p.c_str(), attr) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::permissions");
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+path read_symlink(path const& p, system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ path symlink_path;
+
+#ifdef BOOST_POSIX_API
+ const char* const path_str = p.c_str();
+ char small_buf[small_path_size];
+ ssize_t result = ::readlink(path_str, small_buf, sizeof(small_buf));
+ if (BOOST_UNLIKELY(result < 0))
+ {
+ fail:
+ const int err = errno;
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink", p, error_code(err, system_category())));
+
+ ec->assign(err, system_category());
+ }
+ else if (BOOST_LIKELY(static_cast< std::size_t >(result) < sizeof(small_buf)))
+ {
+ symlink_path.assign(small_buf, small_buf + result);
+ }
+ else
+ {
+ for (std::size_t path_max = sizeof(small_buf) * 2u;; path_max *= 2u) // loop 'til buffer large enough
+ {
+ if (BOOST_UNLIKELY(path_max > absolute_path_max))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::read_symlink", p, error_code(ENAMETOOLONG, system_category())));
+
+ ec->assign(ENAMETOOLONG, system_category());
+ break;
+ }
+
+ std::unique_ptr< char[] > buf(new char[path_max]);
+ result = ::readlink(path_str, buf.get(), path_max);
+ if (BOOST_UNLIKELY(result < 0))
+ {
+ goto fail;
+ }
+ else if (BOOST_LIKELY(static_cast< std::size_t >(result) < path_max))
+ {
+ symlink_path.assign(buf.get(), buf.get() + result);
+ break;
+ }
+ }
+ }
+
+#else
+
+ unique_handle h(create_file_handle(
+ p.c_str(),
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT));
+
+ DWORD error;
+ if (BOOST_UNLIKELY(!h))
+ {
+ return_last_error:
+ error = ::GetLastError();
+ emit_error(error, p, ec, "boost::filesystem::read_symlink");
+ return symlink_path;
+ }
+
+ std::unique_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage);
+ DWORD sz = 0u;
+ if (BOOST_UNLIKELY(!::DeviceIoControl(h.get(), FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.get(), sizeof(*buf), &sz, nullptr)))
+ goto return_last_error;
+
+ const wchar_t* buffer;
+ std::size_t offset, len;
+ switch (buf->rdb.ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ buffer = buf->rdb.MountPointReparseBuffer.PathBuffer;
+ offset = buf->rdb.MountPointReparseBuffer.SubstituteNameOffset;
+ len = buf->rdb.MountPointReparseBuffer.SubstituteNameLength;
+ break;
+
+ case IO_REPARSE_TAG_SYMLINK:
+ buffer = buf->rdb.SymbolicLinkReparseBuffer.PathBuffer;
+ offset = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameOffset;
+ len = buf->rdb.SymbolicLinkReparseBuffer.SubstituteNameLength;
+ // Note: iff info.rdb.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE
+ // -> resulting path is relative to the source
+ break;
+
+ default:
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "Unknown ReparseTag in boost::filesystem::read_symlink");
+ return symlink_path;
+ }
+
+ symlink_path = convert_nt_path_to_win32_path(buffer + offset / sizeof(wchar_t), len / sizeof(wchar_t));
+#endif
+
+ return symlink_path;
+}
+
+BOOST_FILESYSTEM_DECL
+path relative(path const& p, path const& base, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ error_code local_ec;
+ path cur_path;
+ if (!p.is_absolute() || !base.is_absolute())
+ {
+ cur_path = detail::current_path(&local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ fail_local_ec:
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::relative", p, base, local_ec));
+
+ *ec = local_ec;
+ return path();
+ }
+ }
+
+ path wc_base(detail::weakly_canonical_v4(base, cur_path, &local_ec));
+ if (BOOST_UNLIKELY(!!local_ec))
+ goto fail_local_ec;
+ path wc_p(detail::weakly_canonical_v4(p, cur_path, &local_ec));
+ if (BOOST_UNLIKELY(!!local_ec))
+ goto fail_local_ec;
+ return wc_p.lexically_relative(wc_base);
+}
+
+BOOST_FILESYSTEM_DECL
+bool remove(path const& p, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ return detail::remove_impl(p, ec);
+}
+
+BOOST_FILESYSTEM_DECL
+uintmax_t remove_all(path const& p, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ return detail::remove_all_impl(p, ec);
+}
+
+BOOST_FILESYSTEM_DECL
+void rename(path const& old_p, path const& new_p, error_code* ec)
+{
+ error(!BOOST_MOVE_FILE(old_p.c_str(), new_p.c_str()) ? BOOST_ERRNO : 0, old_p, new_p, ec, "boost::filesystem::rename");
+}
+
+BOOST_FILESYSTEM_DECL
+void resize_file(path const& p, uintmax_t size, system::error_code* ec)
+{
+#if defined(BOOST_POSIX_API)
+ if (BOOST_UNLIKELY(size > static_cast< uintmax_t >((std::numeric_limits< off_t >::max)())))
+ {
+ emit_error(system::errc::file_too_large, p, ec, "boost::filesystem::resize_file");
+ return;
+ }
+#endif
+ error(!BOOST_RESIZE_FILE(p.c_str(), size) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::resize_file");
+}
+
+BOOST_FILESYSTEM_DECL
+space_info space(path const& p, error_code* ec)
+{
+ space_info info;
+ // Initialize members to -1, as required by C++20 [fs.op.space]/1 in case of error
+ info.capacity = static_cast< uintmax_t >(-1);
+ info.free = static_cast< uintmax_t >(-1);
+ info.available = static_cast< uintmax_t >(-1);
+
+ if (ec)
+ ec->clear();
+
+#if defined(BOOST_FILESYSTEM_USE_WASI)
+
+ emit_error(BOOST_ERROR_NOT_SUPPORTED, p, ec, "boost::filesystem::space");
+
+#elif defined(BOOST_POSIX_API)
+
+ struct BOOST_STATVFS vfs;
+ if (!error(::BOOST_STATVFS(p.c_str(), &vfs) ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::space"))
+ {
+ info.capacity = static_cast< uintmax_t >(vfs.f_blocks) * BOOST_STATVFS_F_FRSIZE;
+ info.free = static_cast< uintmax_t >(vfs.f_bfree) * BOOST_STATVFS_F_FRSIZE;
+ info.available = static_cast< uintmax_t >(vfs.f_bavail) * BOOST_STATVFS_F_FRSIZE;
+ }
+
+#else
+
+ // GetDiskFreeSpaceExW requires a directory path, which is unlike statvfs, which accepts any file.
+ // To work around this, test if the path refers to a directory and use the parent directory if not.
+ error_code local_ec;
+ file_status status = detail::status_impl(p, &local_ec);
+ if (status.type() == fs::status_error || status.type() == fs::file_not_found)
+ {
+ fail_local_ec:
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::space", p, local_ec));
+ *ec = local_ec;
+ return info;
+ }
+
+ path dir_path = p;
+ if (!is_directory(status))
+ {
+ path cur_path = detail::current_path(ec);
+ if (ec && *ec)
+ return info;
+
+ status = detail::symlink_status_impl(p, &local_ec);
+ if (status.type() == fs::status_error)
+ goto fail_local_ec;
+ if (is_symlink(status))
+ {
+ // We need to resolve the symlink so that we report the space for the symlink target
+ dir_path = detail::canonical_v4(p, cur_path, ec);
+ if (ec && *ec)
+ return info;
+ }
+
+ dir_path = dir_path.parent_path();
+ if (dir_path.empty())
+ {
+ // The original path was just a filename, which is a relative path wrt. current directory
+ dir_path = cur_path;
+ }
+ }
+
+ // For UNC names, the path must also include a trailing slash.
+ path::string_type str = dir_path.native();
+ if (str.size() >= 2u && detail::is_directory_separator(str[0]) && detail::is_directory_separator(str[1]) && !detail::is_directory_separator(*(str.end() - 1)))
+ str.push_back(path::preferred_separator);
+
+ ULARGE_INTEGER avail, total, free;
+ if (!error(::GetDiskFreeSpaceExW(str.c_str(), &avail, &total, &free) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::space"))
+ {
+ info.capacity = static_cast< uintmax_t >(total.QuadPart);
+ info.free = static_cast< uintmax_t >(free.QuadPart);
+ info.available = static_cast< uintmax_t >(avail.QuadPart);
+ }
+
+#endif
+
+ return info;
+}
+
+BOOST_FILESYSTEM_DECL
+file_status status(path const& p, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ return detail::status_impl(p, ec);
+}
+
+BOOST_FILESYSTEM_DECL
+file_status symlink_status(path const& p, error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+ return detail::symlink_status_impl(p, ec);
+}
+
+// contributed by Jeff Flinn
+BOOST_FILESYSTEM_DECL
+path temp_directory_path(system::error_code* ec)
+{
+ if (ec)
+ ec->clear();
+
+#ifdef BOOST_POSIX_API
+
+ const char* val = nullptr;
+
+ (val = std::getenv("TMPDIR")) ||
+ (val = std::getenv("TMP")) ||
+ (val = std::getenv("TEMP")) ||
+ (val = std::getenv("TEMPDIR"));
+
+#ifdef __ANDROID__
+ const char* default_tmp = "/data/local/tmp";
+#else
+ const char* default_tmp = "/tmp";
+#endif
+ path p((val != nullptr) ? val : default_tmp);
+
+ if (BOOST_UNLIKELY(p.empty()))
+ {
+ fail_not_dir:
+ error(ENOTDIR, p, ec, "boost::filesystem::temp_directory_path");
+ return p;
+ }
+
+ file_status status = detail::status_impl(p, ec);
+ if (BOOST_UNLIKELY(ec && *ec))
+ return path();
+ if (BOOST_UNLIKELY(!is_directory(status)))
+ goto fail_not_dir;
+
+ return p;
+
+#else // Windows
+
+ static const wchar_t* const env_list[] = { L"TMP", L"TEMP", L"LOCALAPPDATA", L"USERPROFILE" };
+ static const wchar_t temp_dir[] = L"Temp";
+
+ path p;
+ for (unsigned int i = 0; i < sizeof(env_list) / sizeof(*env_list); ++i)
+ {
+ std::wstring env = wgetenv(env_list[i]);
+ if (!env.empty())
+ {
+ p = env;
+ if (i >= 2)
+ path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u));
+ error_code lcl_ec;
+ if (exists(p, lcl_ec) && !lcl_ec && is_directory(p, lcl_ec) && !lcl_ec)
+ break;
+ p.clear();
+ }
+ }
+
+ if (p.empty())
+ {
+ // use a separate buffer since in C++03 a string is not required to be contiguous
+ const UINT size = ::GetWindowsDirectoryW(nullptr, 0);
+ if (BOOST_UNLIKELY(size == 0))
+ {
+ getwindir_error:
+ int errval = ::GetLastError();
+ error(errval, ec, "boost::filesystem::temp_directory_path");
+ return path();
+ }
+
+ std::unique_ptr< wchar_t[] > buf(new wchar_t[size]);
+ if (BOOST_UNLIKELY(::GetWindowsDirectoryW(buf.get(), size) == 0))
+ goto getwindir_error;
+
+ p = buf.get(); // do not depend on initial buf size, see ticket #10388
+ path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u));
+ }
+
+ return p;
+
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+path system_complete(path const& p, system::error_code* ec)
+{
+#ifdef BOOST_POSIX_API
+
+ if (p.empty() || p.is_absolute())
+ return p;
+
+ path res(current_path());
+ path_algorithms::append_v4(res, p);
+ return res;
+
+#else
+
+ if (p.empty())
+ {
+ if (ec)
+ ec->clear();
+ return p;
+ }
+
+ BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128u;
+ wchar_t buf[buf_size];
+ wchar_t* pfn;
+ std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
+
+ if (error(len == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete"))
+ return path();
+
+ if (len < buf_size) // len does not include null termination character
+ return path(&buf[0]);
+
+ std::unique_ptr< wchar_t[] > big_buf(new wchar_t[len]);
+
+ return error(get_full_path_name(p, len, big_buf.get(), &pfn) == 0 ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::system_complete") ? path() : path(big_buf.get());
+
+#endif
+}
+
+BOOST_FILESYSTEM_DECL
+path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec)
+{
+ path source(detail::absolute_v3(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ system::error_code local_ec;
+ const path::iterator source_end(source.end());
+
+#if defined(BOOST_POSIX_API)
+
+ path::iterator itr(source_end);
+ path head(source);
+ for (; !head.empty(); path_algorithms::decrement_v4(itr))
+ {
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() != fs::file_not_found)
+ break;
+
+ head.remove_filename_and_trailing_separators();
+ }
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+
+#else
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+
+ // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization
+ // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would
+ // break canonical, as symlink_status that it calls internally would report an error that the file at the
+ // intermediate path does not exist. To avoid this, scan the initial path in the forward direction.
+ // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW
+ // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and
+ // mounted cloud storages that have forward slashes as separators.
+ // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
+ // such path. Querying the status of a root name such as c: is also not right as this path refers to the current
+ // directory on drive C:, which is not what we want to test for existence anyway.
+ path::iterator itr(source.begin());
+ path head;
+ if (source.has_root_name())
+ {
+ BOOST_ASSERT(itr != source_end);
+ head = *itr;
+ path_algorithms::increment_v4(itr);
+ }
+
+ if (source.has_root_directory())
+ {
+ BOOST_ASSERT(itr != source_end);
+ // Convert generic separator returned by the iterator for the root directory to
+ // the preferred separator.
+ head += path::preferred_separator;
+ path_algorithms::increment_v4(itr);
+ }
+
+ if (!head.empty())
+ {
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() == fs::file_not_found)
+ {
+ // If the root path does not exist then no path element exists
+ itr = source.begin();
+ head.clear();
+ goto skip_head;
+ }
+ }
+
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
+ {
+ path const& source_elem = *itr;
+
+ // Avoid querying status of paths containing dot and dot-dot elements, as this will break
+ // if the root name starts with "\\?\".
+ if (path_algorithms::compare_v4(source_elem, dot_p) == 0)
+ continue;
+
+ if (path_algorithms::compare_v4(source_elem, dot_dot_p) == 0)
+ {
+ if (head.has_relative_path())
+ head.remove_filename_and_trailing_separators();
+
+ continue;
+ }
+
+ path_algorithms::append_v4(head, source_elem);
+
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() == fs::file_not_found)
+ {
+ head.remove_filename_and_trailing_separators();
+ break;
+ }
+ }
+
+skip_head:;
+
+#endif
+
+ path tail;
+ bool tail_has_dots = false;
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
+ {
+ path const& tail_elem = *itr;
+ path_algorithms::append_v4(tail, tail_elem);
+ // for a later optimization, track if any dot or dot-dot elements are present
+ if (!tail_has_dots && (path_algorithms::compare_v4(tail_elem, dot_p) == 0 || path_algorithms::compare_v4(tail_elem, dot_dot_p) == 0))
+ tail_has_dots = true;
+ }
+
+ head = detail::canonical_v3(head, base, &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (BOOST_LIKELY(!tail.empty()))
+ {
+ path_algorithms::append_v4(head, tail);
+
+ // optimization: only normalize if tail had dot or dot-dot element
+ if (tail_has_dots)
+ head = path_algorithms::lexically_normal_v4(head);
+ }
+
+ return head;
+}
+
+BOOST_FILESYSTEM_DECL
+path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec)
+{
+ path source(detail::absolute_v4(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
+ system::error_code local_ec;
+ const path::iterator source_end(source.end());
+
+#if defined(BOOST_POSIX_API)
+
+ path::iterator itr(source_end);
+ path head(source);
+ for (; !head.empty(); path_algorithms::decrement_v4(itr))
+ {
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() != fs::file_not_found)
+ break;
+
+ head.remove_filename_and_trailing_separators();
+ }
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+
+#else
+
+ path const& dot_p = dot_path();
+ path const& dot_dot_p = dot_dot_path();
+
+ // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization
+ // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would
+ // break canonical, as symlink_status that it calls internally would report an error that the file at the
+ // intermediate path does not exist. To avoid this, scan the initial path in the forward direction.
+ // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW
+ // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and
+ // mounted cloud storages that have forward slashes as separators.
+ // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
+ // such path. Querying the status of a root name such as c: is also not right as this path refers to the current
+ // directory on drive C:, which is not what we want to test for existence anyway.
+ path::iterator itr(source.begin());
+ path head;
+ if (source.has_root_name())
+ {
+ BOOST_ASSERT(itr != source_end);
+ head = *itr;
+ path_algorithms::increment_v4(itr);
+ }
+
+ if (source.has_root_directory())
+ {
+ BOOST_ASSERT(itr != source_end);
+ // Convert generic separator returned by the iterator for the root directory to
+ // the preferred separator.
+ head += path::preferred_separator;
+ path_algorithms::increment_v4(itr);
+ }
+
+ if (!head.empty())
+ {
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() == fs::file_not_found)
+ {
+ // If the root path does not exist then no path element exists
+ itr = source.begin();
+ head.clear();
+ goto skip_head;
+ }
+ }
+
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
+ {
+ path const& source_elem = *itr;
+
+ // Avoid querying status of paths containing dot and dot-dot elements, as this will break
+ // if the root name starts with "\\?\".
+ if (path_algorithms::compare_v4(source_elem, dot_p) == 0)
+ continue;
+
+ if (path_algorithms::compare_v4(source_elem, dot_dot_p) == 0)
+ {
+ if (head.has_relative_path())
+ head.remove_filename_and_trailing_separators();
+
+ continue;
+ }
+
+ path_algorithms::append_v4(head, source_elem);
+
+ file_status head_status(detail::status_impl(head, &local_ec));
+ if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (head_status.type() == fs::file_not_found)
+ {
+ head.remove_filename_and_trailing_separators();
+ break;
+ }
+ }
+
+skip_head:;
+
+#endif
+
+ path tail;
+ bool tail_has_dots = false;
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
+ {
+ path const& tail_elem = *itr;
+ path_algorithms::append_v4(tail, tail_elem);
+ // for a later optimization, track if any dot or dot-dot elements are present
+ if (!tail_has_dots && (path_algorithms::compare_v4(tail_elem, dot_p) == 0 || path_algorithms::compare_v4(tail_elem, dot_dot_p) == 0))
+ tail_has_dots = true;
+ }
+
+ head = detail::canonical_v4(head, base, &local_ec);
+ if (BOOST_UNLIKELY(!!local_ec))
+ {
+ if (!ec)
+ BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
+
+ *ec = local_ec;
+ goto return_empty_path;
+ }
+
+ if (BOOST_LIKELY(!tail.empty()))
+ {
+ path_algorithms::append_v4(head, tail);
+
+ // optimization: only normalize if tail had dot or dot-dot element
+ if (tail_has_dots)
+ head = path_algorithms::lexically_normal_v4(head);
+ }
+
+ return head;
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/path.cpp b/contrib/restricted/boost/filesystem/src/path.cpp
new file mode 100644
index 0000000000..82b2c7d520
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/path.cpp
@@ -0,0 +1,1719 @@
+// filesystem path.cpp ------------------------------------------------------------- //
+
+// Copyright Beman Dawes 2008
+// Copyright Andrey Semashev 2021-2024
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+#include "platform_config.hpp"
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/detail/path_traits.hpp> // codecvt_error_category()
+#include <boost/system/error_category.hpp> // for BOOST_SYSTEM_HAS_CONSTEXPR
+#include <boost/assert.hpp>
+#include <algorithm>
+#include <iterator>
+#include <utility>
+#include <string>
+#include <cstddef>
+#include <cstring>
+#include <cstdlib> // std::atexit
+
+#ifdef BOOST_WINDOWS_API
+#include "windows_file_codecvt.hpp"
+#include "windows_tools.hpp"
+#include <windows.h>
+#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
+#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
+#endif
+
+#ifdef BOOST_FILESYSTEM_DEBUG
+#include <iostream>
+#include <iomanip>
+#endif
+
+#include "atomic_tools.hpp"
+#include "private_config.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace fs = boost::filesystem;
+
+using boost::filesystem::path;
+
+//--------------------------------------------------------------------------------------//
+// //
+// class path helpers //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace {
+//------------------------------------------------------------------------------------//
+// miscellaneous class path helpers //
+//------------------------------------------------------------------------------------//
+
+typedef path::value_type value_type;
+typedef path::string_type string_type;
+typedef string_type::size_type size_type;
+
+#ifdef BOOST_WINDOWS_API
+
+const wchar_t dot_path_literal[] = L".";
+const wchar_t dot_dot_path_literal[] = L"..";
+const wchar_t separators[] = L"/\\";
+using boost::filesystem::detail::colon;
+using boost::filesystem::detail::questionmark;
+
+inline bool is_alnum(wchar_t c)
+{
+ return boost::filesystem::detail::is_letter(c) || (c >= L'0' && c <= L'9');
+}
+
+inline bool is_device_name_char(wchar_t c)
+{
+ // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
+ // Device names are:
+ //
+ // - PRN
+ // - AUX
+ // - NUL
+ // - CON
+ // - LPT[1-9]
+ // - COM[1-9]
+ // - CONIN$
+ // - CONOUT$
+ return is_alnum(c) || c == L'$';
+}
+
+//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
+inline size_type find_separator(const wchar_t* p, size_type size) noexcept
+{
+ size_type pos = 0u;
+ for (; pos < size; ++pos)
+ {
+ const wchar_t c = p[pos];
+ if (boost::filesystem::detail::is_directory_separator(c))
+ break;
+ }
+ return pos;
+}
+
+#else // BOOST_WINDOWS_API
+
+const char dot_path_literal[] = ".";
+const char dot_dot_path_literal[] = "..";
+const char separators[] = "/";
+
+//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
+inline size_type find_separator(const char* p, size_type size) noexcept
+{
+ const char* sep = static_cast< const char* >(std::memchr(p, '/', size));
+ size_type pos = size;
+ if (BOOST_LIKELY(!!sep))
+ pos = sep - p;
+ return pos;
+}
+
+#endif // BOOST_WINDOWS_API
+
+// pos is position of the separator
+bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos);
+
+// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found.
+size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos);
+
+// Returns: starting position of root directory or size if not found. Sets root_name_size to length
+// of the root name if the characters before the returned position (if any) are considered a root name.
+size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size);
+
+// Finds position and size of the first element of the path
+void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size);
+
+// Finds position and size of the first element of the path
+inline void first_element(string_type const& src, size_type& element_pos, size_type& element_size)
+{
+ first_element(src, element_pos, element_size, src.size());
+}
+
+} // unnamed namespace
+
+//--------------------------------------------------------------------------------------//
+// //
+// class path implementation //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+// C++14 provides a mismatch algorithm with four iterator arguments(), but earlier
+// standard libraries didn't, so provide this needed functionality.
+inline std::pair< path::iterator, path::iterator > mismatch(path::iterator it1, path::iterator it1end, path::iterator it2, path::iterator it2end)
+{
+ for (; it1 != it1end && it2 != it2end && path_algorithms::compare_v4(*it1, *it2) == 0;)
+ {
+ path_algorithms::increment_v4(it1);
+ path_algorithms::increment_v4(it2);
+ }
+ return std::make_pair(it1, it2);
+}
+
+// normal --------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v3(path const& p)
+{
+ const value_type* const pathname = p.m_pathname.c_str();
+ const size_type pathname_size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size);
+ path normal(pathname, pathname + root_name_size);
+
+#if defined(BOOST_WINDOWS_API)
+ for (size_type i = 0; i < root_name_size; ++i)
+ {
+ if (normal.m_pathname[i] == path::separator)
+ normal.m_pathname[i] = path::preferred_separator;
+ }
+#endif
+
+ size_type root_path_size = root_name_size;
+ if (root_dir_pos < pathname_size)
+ {
+ root_path_size = root_dir_pos + 1;
+ normal.m_pathname.push_back(path::preferred_separator);
+ }
+
+ size_type i = root_path_size;
+
+ // Skip redundant directory separators after the root directory
+ while (i < pathname_size && detail::is_directory_separator(pathname[i]))
+ ++i;
+
+ if (i < pathname_size)
+ {
+ bool last_element_was_dot = false;
+ while (true)
+ {
+ {
+ const size_type start_pos = i;
+
+ // Find next separator
+ i += find_separator(pathname + i, pathname_size - i);
+
+ const size_type size = i - start_pos;
+
+ // Skip dot elements
+ if (size == 1u && pathname[start_pos] == path::dot)
+ {
+ last_element_was_dot = true;
+ goto skip_append;
+ }
+
+ last_element_was_dot = false;
+
+ // Process dot dot elements
+ if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size)
+ {
+ // Don't remove previous dot dot elements
+ const size_type normal_size = normal.m_pathname.size();
+ size_type filename_size = find_filename_size(normal.m_pathname, root_path_size, normal_size);
+ size_type pos = normal_size - filename_size;
+ if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot)
+ {
+ if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1]))
+ --pos;
+ normal.m_pathname.erase(normal.m_pathname.begin() + pos , normal.m_pathname.end());
+ goto skip_append;
+ }
+ }
+
+ // Append the element
+ path_algorithms::append_separator_if_needed(normal);
+ normal.m_pathname.append(pathname + start_pos, size);
+ }
+
+ skip_append:
+ if (i == pathname_size)
+ break;
+
+ // Skip directory separators, including duplicates
+ while (i < pathname_size && detail::is_directory_separator(pathname[i]))
+ ++i;
+
+ if (i == pathname_size)
+ {
+ // If a path ends with a separator, add a trailing dot element
+ goto append_trailing_dot;
+ }
+ }
+
+ if (normal.empty() || last_element_was_dot)
+ {
+ append_trailing_dot:
+ path_algorithms::append_separator_if_needed(normal);
+ normal.m_pathname.push_back(path::dot);
+ }
+ }
+
+ return normal;
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v4(path const& p)
+{
+ const value_type* const pathname = p.m_pathname.c_str();
+ const size_type pathname_size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size);
+ path normal(pathname, pathname + root_name_size);
+
+ size_type root_path_size = root_name_size;
+ if (root_dir_pos < pathname_size)
+ {
+ root_path_size = root_dir_pos + 1;
+ normal.m_pathname.push_back(path::preferred_separator);
+ }
+
+ size_type i = root_path_size;
+
+ // Skip redundant directory separators after the root directory
+ while (i < pathname_size && detail::is_directory_separator(pathname[i]))
+ ++i;
+
+ if (i < pathname_size)
+ {
+ while (true)
+ {
+ bool last_element_was_dot = false;
+ {
+ const size_type start_pos = i;
+
+ // Find next separator
+ i += find_separator(pathname + i, pathname_size - i);
+
+ const size_type size = i - start_pos;
+
+ // Skip dot elements
+ if (size == 1u && pathname[start_pos] == path::dot)
+ {
+ last_element_was_dot = true;
+ goto skip_append;
+ }
+
+ // Process dot dot elements
+ if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size)
+ {
+ // Don't remove previous dot dot elements
+ const size_type normal_size = normal.m_pathname.size();
+ size_type filename_size = find_filename_size(normal.m_pathname, root_path_size, normal_size);
+ size_type pos = normal_size - filename_size;
+ if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot)
+ {
+ if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1]))
+ --pos;
+ normal.m_pathname.erase(normal.m_pathname.begin() + pos, normal.m_pathname.end());
+ goto skip_append;
+ }
+ }
+
+ // Append the element
+ path_algorithms::append_separator_if_needed(normal);
+ normal.m_pathname.append(pathname + start_pos, size);
+ }
+
+ skip_append:
+ if (i == pathname_size)
+ {
+ // If a path ends with a trailing dot after a directory element, add a trailing separator
+ if (last_element_was_dot && !normal.empty() && !normal.filename_is_dot_dot())
+ path_algorithms::append_separator_if_needed(normal);
+
+ break;
+ }
+
+ // Skip directory separators, including duplicates
+ while (i < pathname_size && detail::is_directory_separator(pathname[i]))
+ ++i;
+
+ if (i == pathname_size)
+ {
+ // If a path ends with a separator, add a trailing separator
+ if (!normal.empty() && !normal.filename_is_dot_dot())
+ path_algorithms::append_separator_if_needed(normal);
+ break;
+ }
+ }
+
+ // If the original path was not empty and normalized ended up being empty, make it a dot
+ if (normal.empty())
+ normal.m_pathname.push_back(path::dot);
+ }
+
+ return normal;
+}
+
+// generic_path ---------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v3(path const& p)
+{
+ path tmp;
+ const size_type pathname_size = p.m_pathname.size();
+ tmp.m_pathname.reserve(pathname_size);
+
+ const value_type* const pathname = p.m_pathname.c_str();
+
+ // Don't remove duplicate slashes from the root name
+ size_type root_name_size = 0u;
+ size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size);
+ if (root_name_size > 0u)
+ {
+ tmp.m_pathname.append(pathname, root_name_size);
+#if defined(BOOST_WINDOWS_API)
+ std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/');
+#endif
+ }
+
+ size_type pos = root_name_size;
+ if (root_dir_pos < pathname_size)
+ {
+ tmp.m_pathname.push_back(path::separator);
+ pos = root_dir_pos + 1u;
+ }
+
+ while (pos < pathname_size)
+ {
+ size_type element_size = find_separator(pathname + pos, pathname_size - pos);
+ if (element_size > 0u)
+ {
+ tmp.m_pathname.append(pathname + pos, element_size);
+
+ pos += element_size;
+ if (pos >= pathname_size)
+ break;
+
+ tmp.m_pathname.push_back(path::separator);
+ }
+
+ ++pos;
+ }
+
+ return tmp;
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v4(path const& p)
+{
+ path tmp;
+ const size_type pathname_size = p.m_pathname.size();
+ tmp.m_pathname.reserve(pathname_size);
+
+ const value_type* const pathname = p.m_pathname.c_str();
+
+ // Treat root name specially as it may contain backslashes, duplicate ones too,
+ // in case of UNC paths and Windows-specific prefixes.
+ size_type root_name_size = 0u;
+ size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size);
+ if (root_name_size > 0u)
+ tmp.m_pathname.append(pathname, root_name_size);
+
+ size_type pos = root_name_size;
+ if (root_dir_pos < pathname_size)
+ {
+ tmp.m_pathname.push_back(path::separator);
+ pos = root_dir_pos + 1u;
+ }
+
+ while (pos < pathname_size)
+ {
+ size_type element_size = find_separator(pathname + pos, pathname_size - pos);
+ if (element_size > 0u)
+ {
+ tmp.m_pathname.append(pathname + pos, element_size);
+
+ pos += element_size;
+ if (pos >= pathname_size)
+ break;
+
+ tmp.m_pathname.push_back(path::separator);
+ }
+
+ ++pos;
+ }
+
+ return tmp;
+}
+
+#if defined(BOOST_WINDOWS_API)
+
+// make_preferred -------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v3(path& p)
+{
+ std::replace(p.m_pathname.begin(), p.m_pathname.end(), L'/', L'\\');
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v4(path& p)
+{
+ const size_type pathname_size = p.m_pathname.size();
+ if (pathname_size > 0u)
+ {
+ value_type* const pathname = &p.m_pathname[0];
+
+ // Avoid converting slashes in the root name
+ size_type root_name_size = 0u;
+ find_root_directory_start(pathname, pathname_size, root_name_size);
+
+ std::replace(pathname + root_name_size, pathname + pathname_size, L'/', L'\\');
+ }
+}
+
+#endif // defined(BOOST_WINDOWS_API)
+
+// append --------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL void path_algorithms::append_v3(path& p, const value_type* begin, const value_type* end)
+{
+ if (begin != end)
+ {
+ if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size())))
+ {
+ if (!detail::is_directory_separator(*begin))
+ path_algorithms::append_separator_if_needed(p);
+ p.m_pathname.append(begin, end);
+ }
+ else
+ {
+ // overlapping source
+ string_type rhs(begin, end);
+ path_algorithms::append_v3(p, rhs.data(), rhs.data() + rhs.size());
+ }
+ }
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::append_v4(path& p, const value_type* begin, const value_type* end)
+{
+ if (begin != end)
+ {
+ if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size())))
+ {
+ const size_type that_size = end - begin;
+ size_type that_root_name_size = 0;
+ size_type that_root_dir_pos = find_root_directory_start(begin, that_size, that_root_name_size);
+
+ // if (p.is_absolute())
+ if
+ (
+#if defined(BOOST_WINDOWS_API)
+ that_root_name_size > 0 &&
+#endif
+ that_root_dir_pos < that_size
+ )
+ {
+ return_assign:
+ p.assign(begin, end);
+ return;
+ }
+
+ size_type this_root_name_size = 0;
+ find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), this_root_name_size);
+
+ if
+ (
+ that_root_name_size > 0 &&
+ (that_root_name_size != this_root_name_size || std::memcmp(p.m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 0)
+ )
+ {
+ goto return_assign;
+ }
+
+ if (that_root_dir_pos < that_size)
+ {
+ // Remove root directory (if any) and relative path to replace with those from p
+ p.m_pathname.erase(p.m_pathname.begin() + this_root_name_size, p.m_pathname.end());
+ }
+
+ const value_type* const that_path = begin + that_root_name_size;
+ if (!detail::is_directory_separator(*that_path))
+ path_algorithms::append_separator_if_needed(p);
+ p.m_pathname.append(that_path, end);
+ }
+ else
+ {
+ // overlapping source
+ string_type rhs(begin, end);
+ path_algorithms::append_v4(p, rhs.data(), rhs.data() + rhs.size());
+ }
+ }
+ else if (path_algorithms::has_filename_v4(p))
+ {
+ p.m_pathname.push_back(path::preferred_separator);
+ }
+}
+
+// compare -------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v3
+(
+ path_detail::path_iterator first1, path_detail::path_iterator const& last1,
+ path_detail::path_iterator first2, path_detail::path_iterator const& last2
+)
+{
+ for (; first1 != last1 && first2 != last2;)
+ {
+ if (first1->native() < first2->native())
+ return -1;
+ if (first2->native() < first1->native())
+ return 1;
+ BOOST_ASSERT(first2->native() == first1->native());
+ path_algorithms::increment_v3(first1);
+ path_algorithms::increment_v3(first2);
+ }
+ if (first1 == last1 && first2 == last2)
+ return 0;
+ return first1 == last1 ? -1 : 1;
+}
+
+BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v4
+(
+ path_detail::path_iterator first1, path_detail::path_iterator const& last1,
+ path_detail::path_iterator first2, path_detail::path_iterator const& last2
+)
+{
+ for (; first1 != last1 && first2 != last2;)
+ {
+ if (first1->native() < first2->native())
+ return -1;
+ if (first2->native() < first1->native())
+ return 1;
+ BOOST_ASSERT(first2->native() == first1->native());
+ path_algorithms::increment_v4(first1);
+ path_algorithms::increment_v4(first2);
+ }
+ if (first1 == last1 && first2 == last2)
+ return 0;
+ return first1 == last1 ? -1 : 1;
+}
+
+BOOST_FILESYSTEM_DECL int path_algorithms::compare_v3(path const& left, path const& right)
+{
+ return path_algorithms::lex_compare_v3(left.begin(), left.end(), right.begin(), right.end());
+}
+
+BOOST_FILESYSTEM_DECL int path_algorithms::compare_v4(path const& left, path const& right)
+{
+ return path_algorithms::lex_compare_v4(left.begin(), left.end(), right.begin(), right.end());
+}
+
+// append_separator_if_needed ------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::append_separator_if_needed(path& p)
+{
+ if (!p.m_pathname.empty() &&
+#ifdef BOOST_WINDOWS_API
+ *(p.m_pathname.end() - 1) != colon &&
+#endif
+ !detail::is_directory_separator(*(p.m_pathname.end() - 1)))
+ {
+ string_type::size_type tmp(p.m_pathname.size());
+ p.m_pathname.push_back(path::preferred_separator);
+ return tmp;
+ }
+ return 0;
+}
+
+// erase_redundant_separator -------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL void path_algorithms::erase_redundant_separator(path& p, string_type::size_type sep_pos)
+{
+ if (sep_pos // a separator was added
+ && sep_pos < p.m_pathname.size() // and something was appended
+ && (p.m_pathname[sep_pos + 1] == path::separator // and it was also separator
+#ifdef BOOST_WINDOWS_API
+ || p.m_pathname[sep_pos + 1] == path::preferred_separator // or preferred_separator
+#endif
+ ))
+ {
+ p.m_pathname.erase(p.m_pathname.begin() + sep_pos); // erase the added separator
+ }
+}
+
+// modifiers -----------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p)
+{
+ p.remove_filename_and_trailing_separators();
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v4(path& p)
+{
+ size_type filename_size = path_algorithms::find_filename_v4_size(p);
+ p.m_pathname.erase(p.m_pathname.begin() + (p.m_pathname.size() - filename_size), p.m_pathname.end());
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v3(path& p, path const& new_extension)
+{
+ // erase existing extension, including the dot, if any
+ size_type ext_pos = p.m_pathname.size() - path_algorithms::extension_v3(p).m_pathname.size();
+ p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end());
+
+ if (!new_extension.empty())
+ {
+ // append new_extension, adding the dot if necessary
+ if (new_extension.m_pathname[0] != path::dot)
+ p.m_pathname.push_back(path::dot);
+ p.m_pathname.append(new_extension.m_pathname);
+ }
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v4(path& p, path const& new_extension)
+{
+ // erase existing extension, including the dot, if any
+ size_type ext_pos = p.m_pathname.size() - path_algorithms::find_extension_v4_size(p);
+ p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end());
+
+ if (!new_extension.empty())
+ {
+ // append new_extension, adding the dot if necessary
+ if (new_extension.m_pathname[0] != path::dot)
+ p.m_pathname.push_back(path::dot);
+ p.m_pathname.append(new_extension.m_pathname);
+ }
+}
+
+// decomposition -------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_name_size(path const& p)
+{
+ size_type root_name_size = 0;
+ find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size);
+ return root_name_size;
+}
+
+BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_path_size(path const& p)
+{
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size);
+
+ size_type size = root_name_size;
+ if (root_dir_pos < p.m_pathname.size())
+ size = root_dir_pos + 1;
+
+ return size;
+}
+
+BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_root_directory(path const& p)
+{
+ substring root_dir;
+ size_type root_name_size = 0;
+ root_dir.pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size);
+ root_dir.size = static_cast< std::size_t >(root_dir.pos < p.m_pathname.size());
+ return root_dir;
+}
+
+BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_relative_path(path const& p)
+{
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size);
+
+ // Skip root name, root directory and any duplicate separators
+ size_type size = root_name_size;
+ if (root_dir_pos < p.m_pathname.size())
+ {
+ size = root_dir_pos + 1;
+
+ for (size_type n = p.m_pathname.size(); size < n; ++size)
+ {
+ if (!detail::is_directory_separator(p.m_pathname[size]))
+ break;
+ }
+ }
+
+ substring rel_path;
+ rel_path.pos = size;
+ rel_path.size = p.m_pathname.size() - size;
+
+ return rel_path;
+}
+
+BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_parent_path_size(path const& p)
+{
+ const size_type size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size);
+
+ size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size);
+ size_type end_pos = size - filename_size;
+ while (true)
+ {
+ if (end_pos <= root_name_size)
+ {
+ // Keep the root name as the parent path if there was a filename
+ if (filename_size == 0)
+ end_pos = 0u;
+ break;
+ }
+
+ --end_pos;
+
+ if (!detail::is_directory_separator(p.m_pathname[end_pos]))
+ {
+ ++end_pos;
+ break;
+ }
+
+ if (end_pos == root_dir_pos)
+ {
+ // Keep the trailing root directory if there was a filename
+ end_pos += filename_size > 0;
+ break;
+ }
+ }
+
+ return end_pos;
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::filename_v3(path const& p)
+{
+ const size_type size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size);
+ size_type filename_size, pos;
+ if (root_dir_pos < size && detail::is_directory_separator(p.m_pathname[size - 1]) && is_root_separator(p.m_pathname, root_dir_pos, size - 1))
+ {
+ // Return root directory
+ pos = root_dir_pos;
+ filename_size = 1u;
+ }
+ else if (root_name_size == size)
+ {
+ // Return root name
+ pos = 0u;
+ filename_size = root_name_size;
+ }
+ else
+ {
+ filename_size = find_filename_size(p.m_pathname, root_name_size, size);
+ pos = size - filename_size;
+ if (filename_size == 0u && pos > root_name_size && detail::is_directory_separator(p.m_pathname[pos - 1]) && !is_root_separator(p.m_pathname, root_dir_pos, pos - 1))
+ return detail::dot_path();
+ }
+
+ const value_type* ptr = p.m_pathname.c_str() + pos;
+ return path(ptr, ptr + filename_size);
+}
+
+BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_filename_v4_size(path const& p)
+{
+ const size_type size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ find_root_directory_start(p.m_pathname.c_str(), size, root_name_size);
+ return find_filename_size(p.m_pathname, root_name_size, size);
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::stem_v3(path const& p)
+{
+ path name(path_algorithms::filename_v3(p));
+ if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0)
+ {
+ size_type pos = name.m_pathname.rfind(path::dot);
+ if (pos != string_type::npos)
+ name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end());
+ }
+ return name;
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::stem_v4(path const& p)
+{
+ path name(path_algorithms::filename_v4(p));
+ if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0)
+ {
+ size_type pos = name.m_pathname.rfind(path::dot);
+ if (pos != 0 && pos != string_type::npos)
+ name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end());
+ }
+ return name;
+}
+
+BOOST_FILESYSTEM_DECL path path_algorithms::extension_v3(path const& p)
+{
+ path name(path_algorithms::filename_v3(p));
+ if (path_algorithms::compare_v4(name, detail::dot_path()) == 0 || path_algorithms::compare_v4(name, detail::dot_dot_path()) == 0)
+ return path();
+ size_type pos(name.m_pathname.rfind(path::dot));
+ return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos);
+}
+
+BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_extension_v4_size(path const& p)
+{
+ const size_type size = p.m_pathname.size();
+ size_type root_name_size = 0;
+ find_root_directory_start(p.m_pathname.c_str(), size, root_name_size);
+ size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size);
+ size_type filename_pos = size - filename_size;
+ if
+ (
+ filename_size > 0u &&
+ // Check for "." and ".." filenames
+ !(p.m_pathname[filename_pos] == path::dot &&
+ (filename_size == 1u || (filename_size == 2u && p.m_pathname[filename_pos + 1u] == path::dot)))
+ )
+ {
+ size_type ext_pos = size;
+ while (ext_pos > filename_pos)
+ {
+ --ext_pos;
+ if (p.m_pathname[ext_pos] == path::dot)
+ break;
+ }
+
+ if (ext_pos > filename_pos)
+ return size - ext_pos;
+ }
+
+ return 0u;
+}
+
+} // namespace detail
+
+BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators()
+{
+ size_type end_pos = detail::path_algorithms::find_parent_path_size(*this);
+ m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end());
+ return *this;
+}
+
+BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator()
+{
+ if (!m_pathname.empty() && detail::is_directory_separator(m_pathname[m_pathname.size() - 1]))
+ m_pathname.erase(m_pathname.end() - 1);
+ return *this;
+}
+
+BOOST_FILESYSTEM_DECL path& path::replace_filename(path const& replacement)
+{
+ detail::path_algorithms::remove_filename_v4(*this);
+ detail::path_algorithms::append_v4(*this, replacement.m_pathname.data(), replacement.m_pathname.data() + replacement.m_pathname.size());
+ return *this;
+}
+
+// lexical operations --------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const
+{
+ path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end();
+ std::pair< path::iterator, path::iterator > mm = detail::mismatch(b, e, base_b, base_e);
+ if (mm.first == b && mm.second == base_b)
+ return path();
+ if (mm.first == e && mm.second == base_e)
+ return detail::dot_path();
+
+ std::ptrdiff_t n = 0;
+ for (; mm.second != base_e; detail::path_algorithms::increment_v4(mm.second))
+ {
+ path const& p = *mm.second;
+ if (detail::path_algorithms::compare_v4(p, detail::dot_dot_path()) == 0)
+ --n;
+ else if (!p.empty() && detail::path_algorithms::compare_v4(p, detail::dot_path()) != 0)
+ ++n;
+ }
+ if (n < 0)
+ return path();
+ if (n == 0 && (mm.first == e || mm.first->empty()))
+ return detail::dot_path();
+
+ path tmp;
+ for (; n > 0; --n)
+ detail::path_algorithms::append_v4(tmp, detail::dot_dot_path());
+ for (; mm.first != e; detail::path_algorithms::increment_v4(mm.first))
+ detail::path_algorithms::append_v4(tmp, *mm.first);
+ return tmp;
+}
+
+} // namespace filesystem
+} // namespace boost
+
+//--------------------------------------------------------------------------------------//
+// //
+// class path helpers implementation //
+// //
+//--------------------------------------------------------------------------------------//
+
+namespace {
+
+// is_root_separator ---------------------------------------------------------------//
+
+// pos is position of the separator
+inline bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos)
+{
+ BOOST_ASSERT_MSG(pos < str.size() && fs::detail::is_directory_separator(str[pos]), "precondition violation");
+
+ // root_dir_pos points at the leftmost separator, we need to skip any duplicate separators right of root dir
+ while (pos > root_dir_pos && fs::detail::is_directory_separator(str[pos - 1]))
+ --pos;
+
+ return pos == root_dir_pos;
+}
+
+// find_filename_size --------------------------------------------------------------//
+
+// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found.
+inline size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos)
+{
+ size_type pos = end_pos;
+ while (pos > root_name_size)
+ {
+ --pos;
+
+ if (fs::detail::is_directory_separator(str[pos]))
+ {
+ ++pos; // filename starts past the separator
+ break;
+ }
+ }
+
+ return end_pos - pos;
+}
+
+// find_root_directory_start -------------------------------------------------------//
+
+// Returns: starting position of root directory or size if not found
+size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size)
+{
+ root_name_size = 0;
+ if (size == 0)
+ return 0;
+
+ bool parsing_root_name = false;
+ size_type pos = 0;
+
+ // case "//", possibly followed by more characters
+ if (fs::detail::is_directory_separator(path[0]))
+ {
+ if (size >= 2 && fs::detail::is_directory_separator(path[1]))
+ {
+ if (size == 2)
+ {
+ // The whole path is just a pair of separators
+ root_name_size = 2;
+ return 2;
+ }
+#ifdef BOOST_WINDOWS_API
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+ // cases "\\?\" and "\\.\"
+ else if (size >= 4 && (path[2] == questionmark || path[2] == fs::path::dot) && fs::detail::is_directory_separator(path[3]))
+ {
+ parsing_root_name = true;
+ pos += 4;
+ }
+#endif
+ else if (fs::detail::is_directory_separator(path[2]))
+ {
+ // The path starts with three directory separators, which is interpreted as a root directory followed by redundant separators
+ return 0;
+ }
+ else
+ {
+ // case "//net {/}"
+ parsing_root_name = true;
+ pos += 2;
+ goto find_next_separator;
+ }
+ }
+#ifdef BOOST_WINDOWS_API
+ // https://stackoverflow.com/questions/23041983/path-prefixes-and
+ // case "\??\" (NT path prefix)
+ else if (size >= 4 && path[1] == questionmark && path[2] == questionmark && fs::detail::is_directory_separator(path[3]))
+ {
+ parsing_root_name = true;
+ pos += 4;
+ }
+#endif
+ else
+ {
+ // The path starts with a separator, possibly followed by a non-separator character
+ return 0;
+ }
+ }
+
+#ifdef BOOST_WINDOWS_API
+ // case "c:" or "prn:"
+ // Note: There is ambiguity in a "c:x" path interpretation. It could either mean a file "x" located at the current directory for drive C:,
+ // or an alternative stream "x" of a file "c". Windows API resolve this as the former, and so do we.
+ if ((size - pos) >= 2 && fs::detail::is_letter(path[pos]))
+ {
+ size_type i = pos + 1;
+ for (; i < size; ++i)
+ {
+ if (!is_device_name_char(path[i]))
+ break;
+ }
+
+ if (i < size && path[i] == colon)
+ {
+ pos = i + 1;
+ root_name_size = pos;
+ parsing_root_name = false;
+
+ if (pos < size && fs::detail::is_directory_separator(path[pos]))
+ return pos;
+ }
+ }
+#endif
+
+ if (!parsing_root_name)
+ return size;
+
+find_next_separator:
+ pos += find_separator(path + pos, size - pos);
+ if (parsing_root_name)
+ root_name_size = pos;
+
+ return pos;
+}
+
+//--------------------------------------------------------------------------------------//
+// //
+// class path::iterator implementation //
+// //
+//--------------------------------------------------------------------------------------//
+
+// first_element ----------------------------------------------------------------------//
+
+// sets pos and len of first element, excluding extra separators
+// if src.empty(), sets pos,len, to 0,0.
+void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size)
+{
+ element_pos = 0;
+ element_size = 0;
+ if (src.empty())
+ return;
+
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(src.c_str(), size, root_name_size);
+
+ // First element is the root name, if there is one
+ if (root_name_size > 0)
+ {
+ element_size = root_name_size;
+ return;
+ }
+
+ // Otherwise, the root directory
+ if (root_dir_pos < size)
+ {
+ element_pos = root_dir_pos;
+ element_size = 1u;
+ return;
+ }
+
+ // Otherwise, the first filename or directory name in a relative path
+ size_type end_pos = src.find_first_of(separators);
+ if (end_pos == string_type::npos)
+ end_pos = src.size();
+ element_size = end_pos;
+}
+
+} // unnamed namespace
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+BOOST_FILESYSTEM_DECL void path_algorithms::increment_v3(path_detail::path_iterator& it)
+{
+ const size_type size = it.m_path_ptr->m_pathname.size();
+ BOOST_ASSERT_MSG(it.m_pos < size, "path::iterator increment past end()");
+
+ // increment to position past current element; if current element is implicit dot,
+ // this will cause m_pos to represent the end iterator
+ it.m_pos += it.m_element.m_pathname.size();
+
+ // if the end is reached, we are done
+ if (it.m_pos >= size)
+ {
+ BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified");
+ it.m_element.clear(); // aids debugging
+ return;
+ }
+
+ // process separator (Windows drive spec is only case not a separator)
+ if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
+ {
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
+
+ // detect root directory and set iterator value to the separator if it is
+ if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size)
+ {
+ it.m_element.m_pathname = path::separator; // generic format; see docs
+ return;
+ }
+
+ // skip separators until m_pos points to the start of the next element
+ while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
+ {
+ ++it.m_pos;
+ }
+
+ // detect trailing separator, and treat it as ".", per POSIX spec
+ if (it.m_pos == size &&
+ !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1))
+ {
+ --it.m_pos;
+ it.m_element = detail::dot_path();
+ return;
+ }
+ }
+
+ // get m_element
+ size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos);
+ if (end_pos == string_type::npos)
+ end_pos = size;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos);
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::increment_v4(path_detail::path_iterator& it)
+{
+ const size_type size = it.m_path_ptr->m_pathname.size();
+ BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator increment past end()");
+
+ if (it.m_element.m_pathname.empty() && (it.m_pos + 1) == size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
+ {
+ // The iterator was pointing to the last empty element of the path; set to end.
+ it.m_pos = size;
+ return;
+ }
+
+ // increment to position past current element; if current element is implicit dot,
+ // this will cause m_pos to represent the end iterator
+ it.m_pos += it.m_element.m_pathname.size();
+
+ // if the end is reached, we are done
+ if (it.m_pos >= size)
+ {
+ BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified");
+ it.m_element.clear(); // aids debugging
+ return;
+ }
+
+ // process separator (Windows drive spec is only case not a separator)
+ if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
+ {
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
+
+ // detect root directory and set iterator value to the separator if it is
+ if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size)
+ {
+ it.m_element.m_pathname = path::separator; // generic format; see docs
+ return;
+ }
+
+ // skip separators until m_pos points to the start of the next element
+ while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
+ {
+ ++it.m_pos;
+ }
+
+ // detect trailing separator
+ if (it.m_pos == size &&
+ !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1))
+ {
+ --it.m_pos;
+ it.m_element.m_pathname.clear();
+ return;
+ }
+ }
+
+ // get m_element
+ size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos);
+ if (end_pos == string_type::npos)
+ end_pos = size;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos);
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v3(path_detail::path_iterator& it)
+{
+ const size_type size = it.m_path_ptr->m_pathname.size();
+ BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()");
+ BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified");
+
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
+
+ if (root_dir_pos < size && it.m_pos == root_dir_pos)
+ {
+ // Was pointing at root directory, decrement to root name
+ set_to_root_name:
+ it.m_pos = 0u;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p, p + root_name_size);
+ return;
+ }
+
+ // if at end and there was a trailing non-root '/', return "."
+ if (it.m_pos == size &&
+ size > 1 &&
+ detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) &&
+ !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1))
+ {
+ --it.m_pos;
+ it.m_element = detail::dot_path();
+ return;
+ }
+
+ // skip separators unless root directory
+ size_type end_pos = it.m_pos;
+ while (end_pos > root_name_size)
+ {
+ --end_pos;
+
+ if (end_pos == root_dir_pos)
+ {
+ // Decremented to the root directory
+ it.m_pos = end_pos;
+ it.m_element.m_pathname = path::separator; // generic format; see docs
+ return;
+ }
+
+ if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos]))
+ {
+ ++end_pos;
+ break;
+ }
+ }
+
+ if (end_pos <= root_name_size)
+ goto set_to_root_name;
+
+ size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos);
+ it.m_pos = end_pos - filename_size;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos);
+}
+
+BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v4(path_detail::path_iterator& it)
+{
+ const size_type size = it.m_path_ptr->m_pathname.size();
+ BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()");
+ BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified");
+
+ size_type root_name_size = 0;
+ size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
+
+ if (root_dir_pos < size && it.m_pos == root_dir_pos)
+ {
+ // Was pointing at root directory, decrement to root name
+ set_to_root_name:
+ it.m_pos = 0u;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p, p + root_name_size);
+ return;
+ }
+
+ // if at end and there was a trailing '/', return ""
+ if (it.m_pos == size &&
+ size > 1 &&
+ detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) &&
+ !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1))
+ {
+ --it.m_pos;
+ it.m_element.m_pathname.clear();
+ return;
+ }
+
+ // skip separators unless root directory
+ size_type end_pos = it.m_pos;
+ while (end_pos > root_name_size)
+ {
+ --end_pos;
+
+ if (end_pos == root_dir_pos)
+ {
+ // Decremented to the root directory
+ it.m_pos = end_pos;
+ it.m_element.m_pathname = path::separator; // generic format; see docs
+ return;
+ }
+
+ if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos]))
+ {
+ ++end_pos;
+ break;
+ }
+ }
+
+ if (end_pos <= root_name_size)
+ goto set_to_root_name;
+
+ size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos);
+ it.m_pos = end_pos - filename_size;
+ const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
+ it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos);
+}
+
+} // namespace detail
+
+// path iterators ------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL path::iterator path::begin() const
+{
+ iterator itr;
+ itr.m_path_ptr = this;
+
+ size_type element_size;
+ first_element(m_pathname, itr.m_pos, element_size);
+
+ if (element_size > 0)
+ {
+ itr.m_element = m_pathname.substr(itr.m_pos, element_size);
+#ifdef BOOST_WINDOWS_API
+ if (itr.m_element.m_pathname.size() == 1u && itr.m_element.m_pathname[0] == path::preferred_separator)
+ itr.m_element.m_pathname[0] = path::separator;
+#endif
+ }
+
+ return itr;
+}
+
+BOOST_FILESYSTEM_DECL path::iterator path::end() const
+{
+ iterator itr;
+ itr.m_path_ptr = this;
+ itr.m_pos = m_pathname.size();
+ return itr;
+}
+
+} // namespace filesystem
+} // namespace boost
+
+namespace {
+
+//------------------------------------------------------------------------------------//
+// locale helpers //
+//------------------------------------------------------------------------------------//
+
+// Prior versions of these locale and codecvt implementations tried to take advantage
+// of static initialization where possible, kept a local copy of the current codecvt
+// facet (to avoid codecvt() having to call use_facet()), and was not multi-threading
+// safe (again for efficiency).
+//
+// This was error prone, and required different implementation techniques depending
+// on the compiler and also whether static or dynamic linking was used. Furthermore,
+// users could not easily provide their multi-threading safe wrappers because the
+// path interface requires the implementation itself to call codecvt() to obtain the
+// default facet, and the initialization of the static within path_locale() could race.
+//
+// The code below is portable to all platforms, is much simpler, and hopefully will be
+// much more robust. Timing tests (on Windows, using a Visual C++ release build)
+// indicated the current code is roughly 9% slower than the previous code, and that
+// seems a small price to pay for better code that is easier to use.
+
+std::locale default_locale()
+{
+#if defined(BOOST_WINDOWS_API)
+ std::locale global_loc = std::locale();
+ return std::locale(global_loc, new boost::filesystem::detail::windows_file_codecvt());
+#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
+ // "All BSD system functions expect their string parameters to be in UTF-8 encoding
+ // and nothing else." See
+ // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
+ //
+ // "The kernel will reject any filename that is not a valid UTF-8 string, and it will
+ // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
+ // The right way to deal with it would be to always convert the filename to UTF-8
+ // before trying to open/create a file." See
+ // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
+ //
+ // "How a file name looks at the API level depends on the API. Current Carbon APIs
+ // handle file names as an array of UTF-16 characters; POSIX ones handle them as an
+ // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
+ // depends on the disk format; HFS+ uses UTF-16, but that's not important in most
+ // cases." See
+ // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
+ //
+ // Many thanks to Peter Dimov for digging out the above references!
+
+ std::locale global_loc = std::locale();
+ return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet());
+#else // Other POSIX
+ // ISO C calls std::locale("") "the locale-specific native environment", and this
+ // locale is the default for many POSIX-based operating systems such as Linux.
+ return std::locale("");
+#endif
+}
+
+std::locale* g_path_locale = nullptr;
+
+void schedule_path_locale_cleanup() noexcept;
+
+// std::locale("") construction, needed on non-Apple POSIX systems, can throw
+// (if environmental variables LC_MESSAGES or LANG are wrong, for example), so
+// get_path_locale() provides lazy initialization to ensure that any
+// exceptions occur after main() starts and so can be caught. Furthermore,
+// g_path_locale is only initialized if path::codecvt() or path::imbue() are themselves
+// actually called, ensuring that an exception will only be thrown if std::locale("")
+// is really needed.
+inline std::locale& get_path_locale()
+{
+#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ atomic_ns::atomic_ref< std::locale* > a(g_path_locale);
+ std::locale* p = a.load(atomic_ns::memory_order_acquire);
+ if (BOOST_UNLIKELY(!p))
+ {
+ std::locale* new_p = new std::locale(default_locale());
+ if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire))
+ {
+ p = new_p;
+ schedule_path_locale_cleanup();
+ }
+ else
+ {
+ delete new_p;
+ }
+ }
+ return *p;
+#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ std::locale* p = g_path_locale;
+ if (BOOST_UNLIKELY(!p))
+ {
+ g_path_locale = p = new std::locale(default_locale());
+ schedule_path_locale_cleanup();
+ }
+ return *p;
+#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+}
+
+inline std::locale* replace_path_locale(std::locale const& loc)
+{
+ std::locale* new_p = new std::locale(loc);
+#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ std::locale* p = atomic_ns::atomic_ref< std::locale* >(g_path_locale).exchange(new_p, atomic_ns::memory_order_acq_rel);
+#else
+ std::locale* p = g_path_locale;
+ g_path_locale = new_p;
+#endif
+ if (!p)
+ schedule_path_locale_cleanup();
+ return p;
+}
+
+#if defined(_MSC_VER)
+
+const boost::filesystem::path* g_dot_path = nullptr;
+const boost::filesystem::path* g_dot_dot_path = nullptr;
+
+inline void schedule_path_locale_cleanup() noexcept
+{
+}
+
+inline boost::filesystem::path const& get_dot_path()
+{
+#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_path);
+ const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire);
+ if (BOOST_UNLIKELY(!p))
+ {
+ const boost::filesystem::path* new_p = new boost::filesystem::path(dot_path_literal);
+ if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire))
+ p = new_p;
+ else
+ delete new_p;
+ }
+ return *p;
+#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ const boost::filesystem::path* p = g_dot_path;
+ if (BOOST_UNLIKELY(!p))
+ g_dot_path = p = new boost::filesystem::path(dot_path_literal);
+ return *p;
+#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+}
+
+inline boost::filesystem::path const& get_dot_dot_path()
+{
+#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_dot_path);
+ const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire);
+ if (BOOST_UNLIKELY(!p))
+ {
+ const boost::filesystem::path* new_p = new boost::filesystem::path(dot_dot_path_literal);
+ if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire))
+ p = new_p;
+ else
+ delete new_p;
+ }
+ return *p;
+#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+ const boost::filesystem::path* p = g_dot_dot_path;
+ if (BOOST_UNLIKELY(!p))
+ g_dot_dot_path = p = new boost::filesystem::path(dot_dot_path_literal);
+ return *p;
+#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
+}
+
+void __cdecl destroy_path_globals()
+{
+ delete g_dot_dot_path;
+ g_dot_dot_path = nullptr;
+ delete g_dot_path;
+ g_dot_path = nullptr;
+ delete g_path_locale;
+ g_path_locale = nullptr;
+}
+
+BOOST_FILESYSTEM_INIT_FUNC init_path_globals()
+{
+#if !defined(BOOST_SYSTEM_HAS_CONSTEXPR)
+ // codecvt_error_category needs to be called early to dynamic-initialize the error category instance
+ boost::filesystem::codecvt_error_category();
+#endif
+ std::atexit(&destroy_path_globals);
+ return BOOST_FILESYSTEM_INITRETSUCCESS_V;
+}
+
+#if _MSC_VER >= 1400
+
+#pragma section(".CRT$XCM", long, read)
+__declspec(allocate(".CRT$XCM")) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+extern const init_func_ptr_t p_init_path_globals = &init_path_globals;
+
+#else // _MSC_VER >= 1400
+
+#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
+#pragma data_seg(push, old_seg)
+#endif
+#pragma data_seg(".CRT$XCM")
+BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+extern const init_func_ptr_t p_init_path_globals = &init_path_globals;
+#pragma data_seg()
+#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
+#pragma data_seg(pop, old_seg)
+#endif
+
+#endif // _MSC_VER >= 1400
+
+#if defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
+//! Makes sure the global initializer pointers are referenced and not removed by linker
+struct globals_retainer
+{
+ const init_func_ptr_t* volatile m_p_init_path_globals;
+
+ globals_retainer() { m_p_init_path_globals = &p_init_path_globals; }
+};
+BOOST_ATTRIBUTE_UNUSED
+static const globals_retainer g_globals_retainer;
+#endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
+
+#else // defined(_MSC_VER)
+
+struct path_locale_deleter
+{
+ ~path_locale_deleter()
+ {
+ delete g_path_locale;
+ g_path_locale = nullptr;
+ }
+};
+
+#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
+
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED
+const path_locale_deleter g_path_locale_deleter = {};
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY)
+const boost::filesystem::path g_dot_path(dot_path_literal);
+BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY)
+const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal);
+
+inline void schedule_path_locale_cleanup() noexcept
+{
+}
+
+inline boost::filesystem::path const& get_dot_path()
+{
+ return g_dot_path;
+}
+
+inline boost::filesystem::path const& get_dot_dot_path()
+{
+ return g_dot_dot_path;
+}
+
+#else // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
+
+inline void schedule_path_locale_cleanup() noexcept
+{
+ BOOST_ATTRIBUTE_UNUSED static const path_locale_deleter g_path_locale_deleter;
+}
+
+inline boost::filesystem::path const& get_dot_path()
+{
+ static const boost::filesystem::path g_dot_path(dot_path_literal);
+ return g_dot_path;
+}
+
+inline boost::filesystem::path const& get_dot_dot_path()
+{
+ static const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal);
+ return g_dot_dot_path;
+}
+
+#endif // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
+
+#endif // defined(_MSC_VER)
+
+} // unnamed namespace
+
+//--------------------------------------------------------------------------------------//
+// path::codecvt() and path::imbue() implementation //
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+
+BOOST_FILESYSTEM_DECL path::codecvt_type const& path::codecvt()
+{
+#ifdef BOOST_FILESYSTEM_DEBUG
+ std::cout << "***** path::codecvt() called" << std::endl;
+#endif
+ return std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(get_path_locale());
+}
+
+BOOST_FILESYSTEM_DECL std::locale path::imbue(std::locale const& loc)
+{
+#ifdef BOOST_FILESYSTEM_DEBUG
+ std::cout << "***** path::imbue() called" << std::endl;
+#endif
+ std::locale* p = replace_path_locale(loc);
+ if (BOOST_LIKELY(p != nullptr))
+ {
+ // Note: copying/moving std::locale does not throw
+ std::locale temp(std::move(*p));
+ delete p;
+ return temp;
+ }
+
+ return default_locale();
+}
+
+namespace detail {
+
+BOOST_FILESYSTEM_DECL path const& dot_path()
+{
+ return get_dot_path();
+}
+
+BOOST_FILESYSTEM_DECL path const& dot_dot_path()
+{
+ return get_dot_dot_path();
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/path_traits.cpp b/contrib/restricted/boost/filesystem/src/path_traits.cpp
new file mode 100644
index 0000000000..9a68f25872
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/path_traits.cpp
@@ -0,0 +1,187 @@
+// filesystem path_traits.cpp --------------------------------------------------------//
+
+// Copyright Beman Dawes 2008, 2009
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/detail/path_traits.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/assert.hpp>
+#include <memory>
+#include <string>
+#include <locale> // for codecvt_base::result
+#include <cwchar> // for mbstate_t
+#include <cstddef>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace pt = boost::filesystem::detail::path_traits;
+namespace fs = boost::filesystem;
+namespace bs = boost::system;
+
+//--------------------------------------------------------------------------------------//
+// configuration //
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_CODECVT_BUF_SIZE
+#define BOOST_FILESYSTEM_CODECVT_BUF_SIZE 256
+#endif
+
+namespace {
+
+BOOST_CONSTEXPR_OR_CONST std::size_t default_codecvt_buf_size = BOOST_FILESYSTEM_CODECVT_BUF_SIZE;
+
+//--------------------------------------------------------------------------------------//
+// //
+// The public convert() functions do buffer management, and then forward to the //
+// convert_aux() functions for the actual call to the codecvt facet. //
+// //
+//--------------------------------------------------------------------------------------//
+
+//--------------------------------------------------------------------------------------//
+// convert_aux const char* to wstring //
+//--------------------------------------------------------------------------------------//
+
+void convert_aux(const char* from, const char* from_end, wchar_t* to, wchar_t* to_end, std::wstring& target, pt::codecvt_type const& cvt)
+{
+ //std::cout << std::hex
+ // << " from=" << std::size_t(from)
+ // << " from_end=" << std::size_t(from_end)
+ // << " to=" << std::size_t(to)
+ // << " to_end=" << std::size_t(to_end)
+ // << std::endl;
+
+ std::mbstate_t state = std::mbstate_t(); // perhaps unneeded, but cuts bug reports
+ const char* from_next;
+ wchar_t* to_next;
+
+ std::codecvt_base::result res;
+
+ if ((res = cvt.in(state, from, from_end, from_next, to, to_end, to_next)) != std::codecvt_base::ok)
+ {
+ //std::cout << " result is " << static_cast<int>(res) << std::endl;
+ BOOST_FILESYSTEM_THROW(bs::system_error(res, fs::codecvt_error_category(), "boost::filesystem::path codecvt to wstring"));
+ }
+ target.append(to, to_next);
+}
+
+//--------------------------------------------------------------------------------------//
+// convert_aux const wchar_t* to string //
+//--------------------------------------------------------------------------------------//
+
+void convert_aux(const wchar_t* from, const wchar_t* from_end, char* to, char* to_end, std::string& target, pt::codecvt_type const& cvt)
+{
+ //std::cout << std::hex
+ // << " from=" << std::size_t(from)
+ // << " from_end=" << std::size_t(from_end)
+ // << " to=" << std::size_t(to)
+ // << " to_end=" << std::size_t(to_end)
+ // << std::endl;
+
+ std::mbstate_t state = std::mbstate_t(); // perhaps unneeded, but cuts bug reports
+ const wchar_t* from_next;
+ char* to_next;
+
+ std::codecvt_base::result res;
+
+ if ((res = cvt.out(state, from, from_end, from_next, to, to_end, to_next)) != std::codecvt_base::ok)
+ {
+ //std::cout << " result is " << static_cast<int>(res) << std::endl;
+ BOOST_FILESYSTEM_THROW(bs::system_error(res, fs::codecvt_error_category(), "boost::filesystem::path codecvt to string"));
+ }
+ target.append(to, to_next);
+}
+
+} // unnamed namespace
+
+//--------------------------------------------------------------------------------------//
+// path_traits //
+//--------------------------------------------------------------------------------------//
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+namespace path_traits {
+
+//--------------------------------------------------------------------------------------//
+// convert const char* to wstring //
+//--------------------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL
+void convert(const char* from, const char* from_end, std::wstring& to, const codecvt_type* cvt)
+{
+ if (from == from_end)
+ return;
+
+ BOOST_ASSERT(from != nullptr);
+ BOOST_ASSERT(from_end != nullptr);
+
+ if (!cvt)
+ cvt = &fs::path::codecvt();
+
+ std::size_t buf_size = (from_end - from) * 3; // perhaps too large, but that's OK
+
+ // dynamically allocate a buffer only if source is unusually large
+ if (buf_size > default_codecvt_buf_size)
+ {
+ std::unique_ptr< wchar_t[] > buf(new wchar_t[buf_size]);
+ convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, *cvt);
+ }
+ else
+ {
+ wchar_t buf[default_codecvt_buf_size];
+ convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, *cvt);
+ }
+}
+
+//--------------------------------------------------------------------------------------//
+// convert const wchar_t* to string //
+//--------------------------------------------------------------------------------------//
+
+BOOST_FILESYSTEM_DECL
+void convert(const wchar_t* from, const wchar_t* from_end, std::string& to, const codecvt_type* cvt)
+{
+ if (from == from_end)
+ return;
+
+ BOOST_ASSERT(from != nullptr);
+ BOOST_ASSERT(from_end != nullptr);
+
+ if (!cvt)
+ cvt = &fs::path::codecvt();
+
+ // The codecvt length functions may not be implemented, and I don't really
+ // understand them either. Thus this code is just a guess; if it turns
+ // out the buffer is too small then an error will be reported and the code
+ // will have to be fixed.
+ std::size_t buf_size = (from_end - from) * 4; // perhaps too large, but that's OK
+ buf_size += 4; // encodings like shift-JIS need some prefix space
+
+ // dynamically allocate a buffer only if source is unusually large
+ if (buf_size > default_codecvt_buf_size)
+ {
+ std::unique_ptr< char[] > buf(new char[buf_size]);
+ convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, *cvt);
+ }
+ else
+ {
+ char buf[default_codecvt_buf_size];
+ convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, *cvt);
+ }
+}
+
+} // namespace path_traits
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/platform_config.hpp b/contrib/restricted/boost/filesystem/src/platform_config.hpp
new file mode 100644
index 0000000000..941338b0a7
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/platform_config.hpp
@@ -0,0 +1,83 @@
+// platform_config.hpp --------------------------------------------------------------------//
+
+// Copyright 2020 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+#ifndef BOOST_FILESYSTEM_PLATFORM_CONFIG_HPP_
+#define BOOST_FILESYSTEM_PLATFORM_CONFIG_HPP_
+
+// define 64-bit offset macros BEFORE including boost/config.hpp (see ticket #5355)
+#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ < 24
+// Android fully supports 64-bit file offsets only for API 24 and above.
+//
+// Trying to define _FILE_OFFSET_BITS=64 for APIs below 24
+// leads to compilation failure for one or another reason,
+// depending on target Android API level, Android NDK version,
+// used STL, order of include paths and more.
+// For more information, please see:
+// - https://github.com/boostorg/filesystem/issues/65
+// - https://github.com/boostorg/filesystem/pull/69
+//
+// Android NDK developers consider it the expected behavior.
+// See their official position here:
+// - https://github.com/android-ndk/ndk/issues/501#issuecomment-326447479
+// - https://android.googlesource.com/platform/bionic/+/a34817457feee026e8702a1d2dffe9e92b51d7d1/docs/32-bit-abi.md#32_bit-abi-bugs
+//
+// Thus we do not define _FILE_OFFSET_BITS in such case.
+#else
+// Defining _FILE_OFFSET_BITS=64 should kick in 64-bit off_t's
+// (and thus st_size) on 32-bit systems that provide the Large File
+// Support (LFS) interface, such as Linux, Solaris, and IRIX.
+//
+// At the time of this comment writing (March 2018), on most systems
+// _FILE_OFFSET_BITS=64 definition is harmless:
+// either the definition is supported and enables 64-bit off_t,
+// or the definition is not supported and is ignored, in which case
+// off_t does not change its default size for the target system
+// (which may be 32-bit or 64-bit already).
+// Thus it makes sense to have _FILE_OFFSET_BITS=64 defined by default,
+// instead of listing every system that supports the definition.
+// Those few systems, on which _FILE_OFFSET_BITS=64 is harmful,
+// for example this definition causes compilation failure on those systems,
+// should be exempt from defining _FILE_OFFSET_BITS by adding
+// an appropriate #elif block above with the appropriate comment.
+//
+// _FILE_OFFSET_BITS must be defined before any headers are included
+// to ensure that the definition is available to all included headers.
+// That is required at least on Solaris, and possibly on other
+// systems as well.
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#if defined(__APPLE__) || defined(__MACH__)
+// Enable newer ABI on Mac OS 10.5 and later, which is needed for struct stat to have birthtime members
+#define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+#ifndef _POSIX_PTHREAD_SEMANTICS
+#define _POSIX_PTHREAD_SEMANTICS // Sun readdir_r() needs this
+#endif
+
+#if !defined(_INCLUDE_STDCSOURCE_199901) && (defined(hpux) || defined(_hpux) || defined(__hpux))
+// For HP-UX, request that WCHAR_MAX and WCHAR_MIN be defined as macros,
+// not casts. See ticket 5048
+#define _INCLUDE_STDCSOURCE_199901
+#endif
+
+#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || \
+ defined(__CYGWIN__)
+// Define target Windows version macros before including any other headers
+#include <boost/winapi/config.hpp>
+#endif
+
+#ifndef BOOST_SYSTEM_NO_DEPRECATED
+#define BOOST_SYSTEM_NO_DEPRECATED
+#endif
+
+#include <boost/filesystem/config.hpp>
+
+#endif // BOOST_FILESYSTEM_PLATFORM_CONFIG_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/portability.cpp b/contrib/restricted/boost/filesystem/src/portability.cpp
new file mode 100644
index 0000000000..8e0e2e5d98
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/portability.cpp
@@ -0,0 +1,83 @@
+// portability.cpp -------------------------------------------------------------------//
+
+// Copyright 2002-2005 Beman Dawes
+// Use, modification, and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
+// at http://www.boost.org/LICENSE_1_0.txt)
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include <cstring> // SGI MIPSpro compilers need this
+#include <string>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+
+namespace {
+
+BOOST_CONSTEXPR_OR_CONST char windows_invalid_chars[] =
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+ "<>:\"/\\|";
+
+BOOST_CONSTEXPR_OR_CONST char posix_valid_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-";
+
+} // unnamed namespace
+
+// name_check functions ----------------------------------------------//
+
+#ifdef BOOST_WINDOWS
+BOOST_FILESYSTEM_DECL bool native(std::string const& name)
+{
+ return windows_name(name);
+}
+#else
+BOOST_FILESYSTEM_DECL bool native(std::string const& name)
+{
+ return !name.empty() && name[0] != ' ' && name.find('/') == std::string::npos;
+}
+#endif
+
+BOOST_FILESYSTEM_DECL bool portable_posix_name(std::string const& name)
+{
+ return !name.empty() && name.find_first_not_of(posix_valid_chars, 0, sizeof(posix_valid_chars) - 1) == std::string::npos;
+}
+
+BOOST_FILESYSTEM_DECL bool windows_name(std::string const& name)
+{
+ // note that the terminating '\0' is part of the string - thus the size below
+ // is sizeof(windows_invalid_chars) rather than sizeof(windows_invalid_chars)-1.
+ return !name.empty() && name[0] != ' ' && name.find_first_of(windows_invalid_chars, 0, sizeof(windows_invalid_chars)) == std::string::npos
+ && *(name.end() - 1) != ' ' && (*(name.end() - 1) != '.' || name.size() == 1 || name == "..");
+}
+
+BOOST_FILESYSTEM_DECL bool portable_name(std::string const& name)
+{
+ return !name.empty() && (name == "." || name == ".." || (windows_name(name) && portable_posix_name(name) && name[0] != '.' && name[0] != '-'));
+}
+
+BOOST_FILESYSTEM_DECL bool portable_directory_name(std::string const& name)
+{
+ return name == "." || name == ".." || (portable_name(name) && name.find('.') == std::string::npos);
+}
+
+BOOST_FILESYSTEM_DECL bool portable_file_name(std::string const& name)
+{
+ std::string::size_type pos;
+ return portable_name(name) && name != "." && name != ".." && ((pos = name.find('.')) == std::string::npos || (name.find('.', pos + 1) == std::string::npos && (pos + 5) > name.size()));
+}
+
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/posix_tools.hpp b/contrib/restricted/boost/filesystem/src/posix_tools.hpp
new file mode 100644
index 0000000000..09af4d5889
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/posix_tools.hpp
@@ -0,0 +1,78 @@
+// posix_tools.hpp -------------------------------------------------------------------//
+
+// Copyright 2021-2024 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_POSIX_TOOLS_HPP_
+#define BOOST_FILESYSTEM_SRC_POSIX_TOOLS_HPP_
+
+#include "platform_config.hpp"
+#include <boost/filesystem/config.hpp>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <boost/scope/unique_fd.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/file_status.hpp>
+#include <boost/filesystem/directory.hpp>
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+//! Platform-specific parameters for directory iterator construction
+struct directory_iterator_params
+{
+#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
+ //! File descriptor of the directory to iterate over. If not a negative value, the directory path is only used to generate paths returned by the iterator.
+ boost::scope::unique_fd dir_fd;
+#endif
+};
+
+//! status() implementation
+file_status status_impl
+(
+ path const& p,
+ system::error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
+ , int basedir_fd = AT_FDCWD
+#endif
+);
+
+//! symlink_status() implementation
+file_status symlink_status_impl
+(
+ path const& p,
+ system::error_code* ec
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) || defined(BOOST_FILESYSTEM_USE_STATX)
+ , int basedir_fd = AT_FDCWD
+#endif
+);
+
+#if defined(BOOST_POSIX_API)
+
+//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
+boost::scope::unique_fd open_directory(path const& p, directory_options opts, system::error_code& ec);
+
+#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
+boost::scope::unique_fd openat_directory(int basedir_fd, path const& p, directory_options opts, system::error_code& ec);
+#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
+
+#endif // defined(BOOST_POSIX_API)
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_SRC_POSIX_TOOLS_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/private_config.hpp b/contrib/restricted/boost/filesystem/src/private_config.hpp
new file mode 100644
index 0000000000..c58d245ee1
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/private_config.hpp
@@ -0,0 +1,74 @@
+// private_config.hpp ----------------------------------------------------------------//
+
+// Copyright 2021 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_PRIVATE_CONFIG_HPP_
+#define BOOST_FILESYSTEM_SRC_PRIVATE_CONFIG_HPP_
+
+#include <boost/filesystem/config.hpp>
+
+#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
+#define BOOST_FILESYSTEM_INIT_PRIORITY(n) __attribute__ ((init_priority(n)))
+#else
+#define BOOST_FILESYSTEM_INIT_PRIORITY(n)
+#endif
+
+// According to https://gcc.gnu.org/bugzilla//show_bug.cgi?id=65115,
+// the default C++ object initializers priority is 65535. We would like to
+// initialize function pointers earlier than that (with lower priority values),
+// before the other global objects initializers are run. Other than this,
+// these priority values are arbitrary.
+#define BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY 32767
+
+// Path globals initialization priority
+#define BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY 32768
+
+#if defined(__has_feature) && defined(__has_attribute)
+#if __has_feature(memory_sanitizer) && __has_attribute(no_sanitize)
+#define BOOST_FILESYSTEM_NO_SANITIZE_MEMORY __attribute__ ((no_sanitize("memory")))
+#endif
+#endif // defined(__has_feature) && defined(__has_attribute)
+
+#ifndef BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
+#define BOOST_FILESYSTEM_NO_SANITIZE_MEMORY
+#endif
+
+#if defined(_MSC_VER)
+#if _MSC_VER < 1300 || _MSC_VER > 1900 // 1300 == VC++ 7.0, 1900 == VC++ 14.0
+typedef void (__cdecl* init_func_ptr_t)();
+#define BOOST_FILESYSTEM_INITRETSUCCESS_V
+#define BOOST_FILESYSTEM_INIT_FUNC void __cdecl
+#else
+typedef int (__cdecl* init_func_ptr_t)();
+#define BOOST_FILESYSTEM_INITRETSUCCESS_V 0
+#define BOOST_FILESYSTEM_INIT_FUNC int __cdecl
+#endif
+#else // defined(_MSC_VER)
+typedef void (*init_func_ptr_t)();
+#define BOOST_FILESYSTEM_INITRETSUCCESS_V
+#define BOOST_FILESYSTEM_INIT_FUNC void
+#endif // defined(_MSC_VER)
+
+#if defined(__has_attribute)
+#if __has_attribute(__used__)
+#define BOOST_FILESYSTEM_ATTRIBUTE_RETAIN __attribute__ ((__used__))
+#endif
+#endif
+
+#if !defined(BOOST_FILESYSTEM_ATTRIBUTE_RETAIN) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 402
+#define BOOST_FILESYSTEM_ATTRIBUTE_RETAIN __attribute__ ((__used__))
+#endif
+
+#if !defined(BOOST_FILESYSTEM_ATTRIBUTE_RETAIN)
+#define BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN
+#define BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
+#endif
+
+#endif // BOOST_FILESYSTEM_SRC_PRIVATE_CONFIG_HPP_
diff --git a/contrib/restricted/boost/filesystem/src/unique_path.cpp b/contrib/restricted/boost/filesystem/src/unique_path.cpp
new file mode 100644
index 0000000000..e98677fa70
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/unique_path.cpp
@@ -0,0 +1,351 @@
+// filesystem unique_path.cpp --------------------------------------------------------//
+
+// Copyright Beman Dawes 2010
+// Copyright Andrey Semashev 2020, 2024
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <boost/predef/library/c/cloudabi.h>
+#include <boost/predef/os/bsd/open.h>
+#include <boost/predef/os/bsd/free.h>
+
+#ifdef BOOST_POSIX_API
+
+#include <cerrno>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#if !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
+#if BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(2, 1, 0) || \
+ BOOST_OS_BSD_FREE >= BOOST_VERSION_NUMBER(8, 0, 0) || \
+ BOOST_LIB_C_CLOUDABI
+#include <stdlib.h>
+#define BOOST_FILESYSTEM_HAS_ARC4RANDOM
+#endif
+#endif // !defined(BOOST_FILESYSTEM_DISABLE_ARC4RANDOM)
+
+#if !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
+#if (defined(__linux__) || defined(__linux) || defined(linux)) && \
+ (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
+#include <sys/syscall.h>
+#if defined(SYS_getrandom)
+#define BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL
+#endif // defined(SYS_getrandom)
+#if defined(__has_include)
+#if __has_include(<sys/random.h>)
+#define BOOST_FILESYSTEM_HAS_GETRANDOM
+#endif
+#elif defined(__GLIBC__)
+#if __GLIBC_PREREQ(2, 25)
+#define BOOST_FILESYSTEM_HAS_GETRANDOM
+#endif
+#endif // BOOST_FILESYSTEM_HAS_GETRANDOM definition
+#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
+#include <sys/random.h>
+#endif
+#endif // (defined(__linux__) || defined(__linux) || defined(linux)) && (!defined(__ANDROID__) || __ANDROID_API__ >= 28)
+#endif // !defined(BOOST_FILESYSTEM_DISABLE_GETRANDOM)
+
+#include <boost/scope/unique_fd.hpp>
+#include "posix_tools.hpp"
+
+#else // BOOST_WINDOWS_API
+
+// We use auto-linking below to help users of static builds of Boost.Filesystem to link to whatever Windows SDK library we selected.
+// The dependency information is currently not exposed in CMake config files generated by Boost.Build (https://github.com/boostorg/boost_install/issues/18),
+// which makes it non-trivial for users to discover the libraries they need. This feature is deprecated and may be removed in the future,
+// when the situation with CMake config files improves.
+// Note that the library build system is the principal source of linking the library, which must work regardless of auto-linking.
+#include <boost/predef/platform.h>
+#include <boost/winapi/basic_types.hpp>
+
+#if defined(BOOST_FILESYSTEM_HAS_BCRYPT) // defined on the command line by the project
+#include <boost/winapi/error_codes.hpp>
+#include <boost/winapi/bcrypt.hpp>
+#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
+#pragma comment(lib, "bcrypt.lib")
+#endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
+#else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+#include <boost/winapi/crypt.hpp>
+#include <boost/winapi/get_last_error.hpp>
+#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
+#if !defined(_WIN32_WCE)
+#pragma comment(lib, "advapi32.lib")
+#else
+#pragma comment(lib, "coredll.lib")
+#endif
+#endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) && defined(_MSC_VER)
+#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+
+#endif // BOOST_POSIX_API
+
+#include <cstddef>
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/operations.hpp>
+#include "private_config.hpp"
+#include "atomic_tools.hpp"
+#include "error_handling.hpp"
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+#if defined(BOOST_POSIX_API)
+// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#endif // defined(BOOST_POSIX_API)
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+namespace {
+
+#if defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
+
+//! Fills buffer with cryptographically random data obtained from /dev/(u)random
+int fill_random_dev_random(void* buf, std::size_t len)
+{
+ boost::scope::unique_fd file;
+ while (true)
+ {
+ file.reset(::open("/dev/urandom", O_RDONLY | O_CLOEXEC));
+ if (!file)
+ {
+ if (errno == EINTR)
+ continue;
+ }
+
+ break;
+ }
+
+ if (!file)
+ {
+ while (true)
+ {
+ file.reset(::open("/dev/random", O_RDONLY | O_CLOEXEC));
+ if (!file)
+ {
+ const int err = errno;
+ if (err == EINTR)
+ continue;
+ return err;
+ }
+
+ break;
+ }
+ }
+
+ std::size_t bytes_read = 0u;
+ while (bytes_read < len)
+ {
+ ssize_t n = ::read(file.get(), buf, len - bytes_read);
+ if (BOOST_UNLIKELY(n < 0))
+ {
+ const int err = errno;
+ if (err == EINTR)
+ continue;
+ return err;
+ }
+
+ bytes_read += n;
+ buf = static_cast< char* >(buf) + n;
+ }
+
+ return 0;
+}
+
+#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
+
+typedef int fill_random_t(void* buf, std::size_t len);
+
+//! Pointer to the implementation of fill_random.
+fill_random_t* fill_random = &fill_random_dev_random;
+
+//! Fills buffer with cryptographically random data obtained from getrandom()
+int fill_random_getrandom(void* buf, std::size_t len)
+{
+ std::size_t bytes_read = 0u;
+ while (bytes_read < len)
+ {
+#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM)
+ ssize_t n = ::getrandom(buf, len - bytes_read, 0u);
+#else
+ ssize_t n = ::syscall(SYS_getrandom, buf, len - bytes_read, 0u);
+#endif
+ if (BOOST_UNLIKELY(n < 0))
+ {
+ const int err = errno;
+ if (err == EINTR)
+ continue;
+
+ if (err == ENOSYS && bytes_read == 0u)
+ {
+ filesystem::detail::atomic_store_relaxed(fill_random, &fill_random_dev_random);
+ return fill_random_dev_random(buf, len);
+ }
+
+ return err;
+ }
+
+ bytes_read += n;
+ buf = static_cast< char* >(buf) + n;
+ }
+
+ return 0;
+}
+
+#endif // defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
+
+#endif // defined(BOOST_POSIX_API) && !defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
+
+void system_crypt_random(void* buf, std::size_t len, boost::system::error_code* ec)
+{
+#if defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL)
+
+ int err = filesystem::detail::atomic_load_relaxed(fill_random)(buf, len);
+ if (BOOST_UNLIKELY(err != 0))
+ emit_error(err, ec, "boost::filesystem::unique_path");
+
+#elif defined(BOOST_FILESYSTEM_HAS_ARC4RANDOM)
+
+ arc4random_buf(buf, len);
+
+#else
+
+ int err = fill_random_dev_random(buf, len);
+ if (BOOST_UNLIKELY(err != 0))
+ emit_error(err, ec, "boost::filesystem::unique_path");
+
+#endif
+
+#else // defined(BOOST_POSIX_API)
+
+#if defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+
+ boost::winapi::BCRYPT_ALG_HANDLE_ handle;
+ boost::winapi::NTSTATUS_ status = boost::winapi::BCryptOpenAlgorithmProvider(&handle, boost::winapi::BCRYPT_RNG_ALGORITHM_, nullptr, 0);
+ if (BOOST_UNLIKELY(status != 0))
+ {
+ fail:
+ emit_error(translate_ntstatus(status), ec, "boost::filesystem::unique_path");
+ return;
+ }
+
+ status = boost::winapi::BCryptGenRandom(handle, static_cast< boost::winapi::PUCHAR_ >(buf), static_cast< boost::winapi::ULONG_ >(len), 0);
+
+ boost::winapi::BCryptCloseAlgorithmProvider(handle, 0);
+
+ if (BOOST_UNLIKELY(status != 0))
+ goto fail;
+
+#else // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+
+ boost::winapi::HCRYPTPROV_ handle;
+ boost::winapi::DWORD_ err = 0u;
+ if (BOOST_UNLIKELY(!boost::winapi::CryptAcquireContextW(&handle, nullptr, nullptr, boost::winapi::PROV_RSA_FULL_, boost::winapi::CRYPT_VERIFYCONTEXT_ | boost::winapi::CRYPT_SILENT_)))
+ {
+ err = boost::winapi::GetLastError();
+
+ fail:
+ emit_error(err, ec, "boost::filesystem::unique_path");
+ return;
+ }
+
+ boost::winapi::BOOL_ gen_ok = boost::winapi::CryptGenRandom(handle, static_cast< boost::winapi::DWORD_ >(len), static_cast< boost::winapi::BYTE_* >(buf));
+
+ if (BOOST_UNLIKELY(!gen_ok))
+ err = boost::winapi::GetLastError();
+
+ boost::winapi::CryptReleaseContext(handle, 0);
+
+ if (BOOST_UNLIKELY(!gen_ok))
+ goto fail;
+
+#endif // defined(BOOST_FILESYSTEM_HAS_BCRYPT)
+
+#endif // defined(BOOST_POSIX_API)
+}
+
+#ifdef BOOST_WINDOWS_API
+BOOST_CONSTEXPR_OR_CONST wchar_t hex[] = L"0123456789abcdef";
+BOOST_CONSTEXPR_OR_CONST wchar_t percent = L'%';
+#else
+BOOST_CONSTEXPR_OR_CONST char hex[] = "0123456789abcdef";
+BOOST_CONSTEXPR_OR_CONST char percent = '%';
+#endif
+
+} // unnamed namespace
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+
+//! Initializes fill_random implementation pointer
+void init_fill_random_impl(unsigned int major_ver, unsigned int minor_ver, unsigned int patch_ver)
+{
+#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) && \
+ (defined(BOOST_FILESYSTEM_HAS_GETRANDOM) || defined(BOOST_FILESYSTEM_HAS_GETRANDOM_SYSCALL))
+ fill_random_t* fr = &fill_random_dev_random;
+
+ if (major_ver > 3u || (major_ver == 3u && minor_ver >= 17u))
+ fr = &fill_random_getrandom;
+
+ filesystem::detail::atomic_store_relaxed(fill_random, fr);
+#endif
+}
+
+#endif // defined(linux) || defined(__linux) || defined(__linux__)
+
+BOOST_FILESYSTEM_DECL
+path unique_path(path const& model, system::error_code* ec)
+{
+ // This function used wstring for fear of misidentifying
+ // a part of a multibyte character as a percent sign.
+ // However, double byte encodings only have 80-FF as lead
+ // bytes and 40-7F as trailing bytes, whereas % is 25.
+ // So, use string on POSIX and avoid conversions.
+
+ path::string_type s(model.native());
+
+ char ran[16] = {}; // init to avoid clang static analyzer message
+ // see ticket #8954
+ BOOST_CONSTEXPR_OR_CONST unsigned int max_nibbles = 2u * sizeof(ran); // 4-bits per nibble
+
+ unsigned int nibbles_used = max_nibbles;
+ for (path::string_type::size_type i = 0, n = s.size(); i < n; ++i)
+ {
+ if (s[i] == percent) // digit request
+ {
+ if (nibbles_used == max_nibbles)
+ {
+ system_crypt_random(ran, sizeof(ran), ec);
+ if (ec && *ec)
+ return path();
+ nibbles_used = 0;
+ }
+ unsigned int c = ran[nibbles_used / 2u];
+ c >>= 4u * (nibbles_used++ & 1u); // if odd, shift right 1 nibble
+ s[i] = hex[c & 0xf]; // convert to hex digit and replace
+ }
+ }
+
+ if (ec)
+ ec->clear();
+
+ return s;
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp b/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp
new file mode 100644
index 0000000000..7e5939bb71
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/utf8_codecvt_facet.cpp
@@ -0,0 +1,29 @@
+// Copyright Vladimir Prus 2004.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt
+// or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#include "platform_config.hpp"
+
+#include <boost/filesystem/config.hpp>
+
+#include <boost/filesystem/detail/header.hpp>
+
+#define BOOST_UTF8_BEGIN_NAMESPACE \
+ namespace boost { \
+ namespace filesystem { \
+ namespace detail {
+
+#define BOOST_UTF8_END_NAMESPACE \
+ } \
+ } \
+ }
+#define BOOST_UTF8_DECL BOOST_FILESYSTEM_DECL
+
+#include <boost/detail/utf8_codecvt_facet.ipp>
+
+#undef BOOST_UTF8_BEGIN_NAMESPACE
+#undef BOOST_UTF8_END_NAMESPACE
+#undef BOOST_UTF8_DECL
+
+#include <boost/filesystem/detail/footer.hpp>
diff --git a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp
new file mode 100644
index 0000000000..f86ba5111b
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.cpp
@@ -0,0 +1,72 @@
+// filesystem windows_file_codecvt.cpp -----------------------------------------//
+
+// Copyright Beman Dawes 2009
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#include "platform_config.hpp"
+
+#include <cwchar> // for mbstate_t
+
+#ifdef BOOST_WINDOWS_API
+
+#include "windows_file_codecvt.hpp"
+
+#include <windows.h>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+std::codecvt_base::result windows_file_codecvt::do_in(
+ std::mbstate_t&,
+ const char* from, const char* from_end, const char*& from_next,
+ wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const
+{
+ UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+
+ int count;
+ if ((count = ::MultiByteToWideChar(codepage, MB_PRECOMPOSED, from, static_cast< int >(from_end - from), to, static_cast< int >(to_end - to))) == 0)
+ {
+ return error; // conversion failed
+ }
+
+ from_next = from_end;
+ to_next = to + count;
+ *to_next = L'\0';
+ return ok;
+}
+
+std::codecvt_base::result windows_file_codecvt::do_out(
+ std::mbstate_t&,
+ const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next,
+ char* to, char* to_end, char*& to_next) const
+{
+ UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+
+ int count;
+ if ((count = ::WideCharToMultiByte(codepage, WC_NO_BEST_FIT_CHARS, from, static_cast< int >(from_end - from), to, static_cast< int >(to_end - to), 0, 0)) == 0)
+ {
+ return error; // conversion failed
+ }
+
+ from_next = from_end;
+ to_next = to + count;
+ *to_next = '\0';
+ return ok;
+}
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_WINDOWS_API
diff --git a/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp
new file mode 100644
index 0000000000..e0f0ae6c3a
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/windows_file_codecvt.hpp
@@ -0,0 +1,72 @@
+// filesystem windows_file_codecvt.hpp -----------------------------------------------//
+
+// Copyright Beman Dawes 2009
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// Library home page: http://www.boost.org/libs/filesystem
+
+#ifndef BOOST_FILESYSTEM_WINDOWS_FILE_CODECVT_HPP
+#define BOOST_FILESYSTEM_WINDOWS_FILE_CODECVT_HPP
+
+#include <boost/filesystem/config.hpp>
+
+#ifdef BOOST_WINDOWS_API
+
+#include <boost/config/workaround.hpp>
+#include <cstddef>
+#include <cwchar> // std::mbstate_t
+#include <locale>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+//------------------------------------------------------------------------------------//
+// //
+// class windows_file_codecvt //
+// //
+// Warning: partial implementation; even do_in and do_out only partially meet the //
+// standard library specifications as the "to" buffer must hold the entire result. //
+// //
+//------------------------------------------------------------------------------------//
+
+class BOOST_SYMBOL_VISIBLE windows_file_codecvt final :
+ public std::codecvt< wchar_t, char, std::mbstate_t >
+{
+public:
+ explicit windows_file_codecvt(std::size_t refs = 0) :
+ std::codecvt< wchar_t, char, std::mbstate_t >(refs)
+ {
+ }
+
+protected:
+ bool do_always_noconv() const noexcept override { return false; }
+
+ // seems safest to assume variable number of characters since we don't
+ // actually know what codepage is active
+ int do_encoding() const noexcept override { return 0; }
+ std::codecvt_base::result do_in(std::mbstate_t& state, const char* from, const char* from_end, const char*& from_next, wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const override;
+ std::codecvt_base::result do_out(std::mbstate_t& state, const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, char* to, char* to_end, char*& to_next) const override;
+ std::codecvt_base::result do_unshift(std::mbstate_t&, char* /*from*/, char* /*to*/, char*& /*next*/) const override { return ok; }
+ int do_length(std::mbstate_t&, const char* /*from*/, const char* /*from_end*/, std::size_t /*max*/) const
+#if BOOST_WORKAROUND(__IBMCPP__, BOOST_TESTED_AT(600))
+ throw()
+#endif
+ override
+ { return 0; }
+ int do_max_length() const noexcept override { return 0; }
+};
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_WINDOWS_API
+
+#endif // BOOST_FILESYSTEM_WINDOWS_FILE_CODECVT_HPP
diff --git a/contrib/restricted/boost/filesystem/src/windows_tools.hpp b/contrib/restricted/boost/filesystem/src/windows_tools.hpp
new file mode 100644
index 0000000000..c16b61fb48
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/src/windows_tools.hpp
@@ -0,0 +1,310 @@
+// windows_tools.hpp -----------------------------------------------------------------//
+
+// Copyright 2001 Dietmar Kuehl
+// Copyright 2002-2009, 2014 Beman Dawes
+// Copyright 2021-2022 Andrey Semashev
+
+// Distributed under the Boost Software License, Version 1.0.
+// See http://www.boost.org/LICENSE_1_0.txt
+
+// See library home page at http://www.boost.org/libs/filesystem
+
+//--------------------------------------------------------------------------------------//
+
+#ifndef BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_
+#define BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_
+
+#include <boost/filesystem/config.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/file_status.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/scope/unique_resource.hpp>
+#include <boost/winapi/basic_types.hpp> // NTSTATUS_
+
+#include <windows.h>
+
+#include <boost/filesystem/detail/header.hpp> // must be the last #include
+
+#ifndef IO_REPARSE_TAG_DEDUP
+#define IO_REPARSE_TAG_DEDUP (0x80000013L)
+#endif
+
+#ifndef IO_REPARSE_TAG_MOUNT_POINT
+#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
+#endif
+
+#ifndef IO_REPARSE_TAG_SYMLINK
+#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+
+namespace boost {
+namespace filesystem {
+namespace detail {
+
+//! Deleter for HANDLEs
+struct handle_deleter
+{
+ using result_type = void;
+
+ result_type operator() (HANDLE h) const noexcept
+ {
+ ::CloseHandle(h);
+ }
+};
+
+//! Resource traits for HANDLEs
+struct handle_resource_traits
+{
+ static HANDLE make_default() noexcept
+ {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ static bool is_allocated(HANDLE h) noexcept
+ {
+ return h != INVALID_HANDLE_VALUE && h != nullptr;
+ }
+};
+
+//! Unique HANDLE wrapper
+using unique_handle = boost::scope::unique_resource< HANDLE, handle_deleter, handle_resource_traits >;
+
+BOOST_INLINE_VARIABLE BOOST_CONSTEXPR_OR_CONST wchar_t colon = L':';
+BOOST_INLINE_VARIABLE BOOST_CONSTEXPR_OR_CONST wchar_t questionmark = L'?';
+
+inline bool is_letter(wchar_t c)
+{
+ return (c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z');
+}
+
+inline bool equal_extension(wchar_t const* p, wchar_t const (&x1)[5], wchar_t const (&x2)[5])
+{
+ return (p[0] == x1[0] || p[0] == x2[0]) &&
+ (p[1] == x1[1] || p[1] == x2[1]) &&
+ (p[2] == x1[2] || p[2] == x2[2]) &&
+ (p[3] == x1[3] || p[3] == x2[3]) &&
+ p[4] == 0;
+}
+
+inline boost::filesystem::perms make_permissions(boost::filesystem::path const& p, DWORD attr)
+{
+ boost::filesystem::perms prms = boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read;
+ if ((attr & FILE_ATTRIBUTE_READONLY) == 0u)
+ prms |= boost::filesystem::owner_write | boost::filesystem::group_write | boost::filesystem::others_write;
+ boost::filesystem::path ext = detail::path_algorithms::extension_v4(p);
+ wchar_t const* q = ext.c_str();
+ if (equal_extension(q, L".exe", L".EXE") || equal_extension(q, L".com", L".COM") || equal_extension(q, L".bat", L".BAT") || equal_extension(q, L".cmd", L".CMD"))
+ prms |= boost::filesystem::owner_exe | boost::filesystem::group_exe | boost::filesystem::others_exe;
+ return prms;
+}
+
+ULONG get_reparse_point_tag_ioctl(HANDLE h, boost::filesystem::path const& p, boost::system::error_code* ec);
+
+inline bool is_reparse_point_tag_a_symlink(ULONG reparse_point_tag)
+{
+ return reparse_point_tag == IO_REPARSE_TAG_SYMLINK
+ // Issue 9016 asked that NTFS directory junctions be recognized as directories.
+ // That is equivalent to recognizing them as symlinks, and then the normal symlink
+ // mechanism will take care of recognizing them as directories.
+ //
+ // Directory junctions are very similar to symlinks, but have some performance
+ // and other advantages over symlinks. They can be created from the command line
+ // with "mklink /J junction-name target-path".
+ //
+ // Note that mounted filesystems also have the same repartse point tag, which makes
+ // them look like directory symlinks in terms of Boost.Filesystem. read_symlink()
+ // may return a volume path or NT path for such symlinks.
+ || reparse_point_tag == IO_REPARSE_TAG_MOUNT_POINT; // aka "directory junction" or "junction"
+}
+
+//! Platform-specific parameters for directory iterator construction
+struct directory_iterator_params
+{
+ //! Handle of the directory to iterate over. If not \c INVALID_HANDLE_VALUE, the directory path is only used to generate paths returned by the iterator.
+ HANDLE dir_handle;
+ /*!
+ * If \c dir_handle is not \c INVALID_HANDLE_VALUE, specifies whether the directory iterator should close the handle upon destruction.
+ * If \c false, the handle must remain valid for the lifetime of the iterator.
+ */
+ bool close_handle;
+};
+
+//! IO_STATUS_BLOCK definition from Windows SDK.
+struct io_status_block
+{
+ union
+ {
+ boost::winapi::NTSTATUS_ Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+};
+
+//! UNICODE_STRING definition from Windows SDK
+struct unicode_string
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+};
+
+//! OBJECT_ATTRIBUTES definition from Windows SDK
+struct object_attributes
+{
+ ULONG Length;
+ HANDLE RootDirectory;
+ unicode_string* ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+};
+
+#ifndef OBJ_CASE_INSENSITIVE
+#define OBJ_CASE_INSENSITIVE 0x00000040
+#endif
+#ifndef OBJ_DONT_REPARSE
+#define OBJ_DONT_REPARSE 0x00001000
+#endif
+
+#ifndef FILE_SUPERSEDE
+#define FILE_SUPERSEDE 0x00000000
+#endif
+#ifndef FILE_OPEN
+#define FILE_OPEN 0x00000001
+#endif
+#ifndef FILE_CREATE
+#define FILE_CREATE 0x00000002
+#endif
+#ifndef FILE_OPEN_IF
+#define FILE_OPEN_IF 0x00000003
+#endif
+#ifndef FILE_OVERWRITE
+#define FILE_OVERWRITE 0x00000004
+#endif
+#ifndef FILE_OVERWRITE_IF
+#define FILE_OVERWRITE_IF 0x00000005
+#endif
+
+#ifndef FILE_DIRECTORY_FILE
+#define FILE_DIRECTORY_FILE 0x00000001
+#endif
+#ifndef FILE_SYNCHRONOUS_IO_NONALERT
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+#endif
+#ifndef FILE_OPEN_FOR_BACKUP_INTENT
+#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
+#endif
+#ifndef FILE_OPEN_REPARSE_POINT
+#define FILE_OPEN_REPARSE_POINT 0x00200000
+#endif
+
+//! NtCreateFile signature. Available since Windows 2000 (probably).
+typedef boost::winapi::NTSTATUS_ (NTAPI NtCreateFile_t)(
+ /*out*/ PHANDLE FileHandle,
+ /*in*/ ACCESS_MASK DesiredAccess,
+ /*in*/ object_attributes* ObjectAttributes,
+ /*out*/ io_status_block* IoStatusBlock,
+ /*in, optional*/ PLARGE_INTEGER AllocationSize,
+ /*in*/ ULONG FileAttributes,
+ /*in*/ ULONG ShareAccess,
+ /*in*/ ULONG CreateDisposition,
+ /*in*/ ULONG CreateOptions,
+ /*in, optional*/ PVOID EaBuffer,
+ /*in*/ ULONG EaLength);
+
+extern NtCreateFile_t* nt_create_file_api;
+
+//! PIO_APC_ROUTINE definition from Windows SDK
+typedef VOID (NTAPI* pio_apc_routine) (PVOID ApcContext, io_status_block* IoStatusBlock, ULONG Reserved);
+
+//! FILE_INFORMATION_CLASS enum entries
+enum file_information_class
+{
+ file_directory_information_class = 1
+};
+
+//! NtQueryDirectoryFile signature. Available since Windows NT 4.0 (probably).
+typedef boost::winapi::NTSTATUS_ (NTAPI NtQueryDirectoryFile_t)(
+ /*in*/ HANDLE FileHandle,
+ /*in, optional*/ HANDLE Event,
+ /*in, optional*/ pio_apc_routine ApcRoutine,
+ /*in, optional*/ PVOID ApcContext,
+ /*out*/ io_status_block* IoStatusBlock,
+ /*out*/ PVOID FileInformation,
+ /*in*/ ULONG Length,
+ /*in*/ file_information_class FileInformationClass,
+ /*in*/ BOOLEAN ReturnSingleEntry,
+ /*in, optional*/ unicode_string* FileName,
+ /*in*/ BOOLEAN RestartScan);
+
+extern NtQueryDirectoryFile_t* nt_query_directory_file_api;
+
+//! FILE_INFO_BY_HANDLE_CLASS enum entries
+enum file_info_by_handle_class
+{
+ file_basic_info_class = 0,
+ file_disposition_info_class = 4,
+ file_attribute_tag_info_class = 9,
+ file_id_both_directory_info_class = 10,
+ file_id_both_directory_restart_info_class = 11,
+ file_full_directory_info_class = 14,
+ file_full_directory_restart_info_class = 15,
+ file_id_extd_directory_info_class = 19,
+ file_id_extd_directory_restart_info_class = 20,
+ file_disposition_info_ex_class = 21
+};
+
+//! FILE_ATTRIBUTE_TAG_INFO definition from Windows SDK
+struct file_attribute_tag_info
+{
+ DWORD FileAttributes;
+ DWORD ReparseTag;
+};
+
+//! GetFileInformationByHandleEx signature. Available since Windows Vista.
+typedef BOOL (WINAPI GetFileInformationByHandleEx_t)(
+ /*__in*/ HANDLE hFile,
+ /*__in*/ file_info_by_handle_class FileInformationClass, // the actual type is FILE_INFO_BY_HANDLE_CLASS enum
+ /*__out_bcount(dwBufferSize)*/ LPVOID lpFileInformation,
+ /*__in*/ DWORD dwBufferSize);
+
+extern GetFileInformationByHandleEx_t* get_file_information_by_handle_ex_api;
+
+//! Creates a file handle
+inline unique_handle create_file_handle
+(
+ boost::filesystem::path const& p,
+ DWORD dwDesiredAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile = nullptr
+)
+{
+ return unique_handle(::CreateFileW(p.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile));
+}
+
+//! Creates a file handle for a file relative to a previously opened base directory. The file path must be relative and in preferred format.
+boost::winapi::NTSTATUS_ nt_create_file_handle_at
+(
+ unique_handle& out,
+ HANDLE basedir_handle,
+ boost::filesystem::path const& p,
+ ULONG FileAttributes,
+ ACCESS_MASK DesiredAccess,
+ ULONG ShareMode,
+ ULONG CreateDisposition,
+ ULONG CreateOptions
+);
+
+//! Returns status of the file identified by an open handle. The path \a p is used to report errors and infer file permissions.
+filesystem::file_status status_by_handle(HANDLE h, path const& p, system::error_code* ec);
+
+} // namespace detail
+} // namespace filesystem
+} // namespace boost
+
+#include <boost/filesystem/detail/footer.hpp>
+
+#endif // BOOST_FILESYSTEM_SRC_WINDOWS_TOOLS_HPP_
diff --git a/contrib/restricted/boost/filesystem/ya.make b/contrib/restricted/boost/filesystem/ya.make
new file mode 100644
index 0000000000..f468ba4980
--- /dev/null
+++ b/contrib/restricted/boost/filesystem/ya.make
@@ -0,0 +1,68 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(BSL-1.0)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(1.86.0)
+
+ORIGINAL_SOURCE(https://github.com/boostorg/filesystem/archive/boost-1.86.0.tar.gz)
+
+PEERDIR(
+ contrib/restricted/boost/assert
+ contrib/restricted/boost/atomic
+ contrib/restricted/boost/config
+ contrib/restricted/boost/container_hash
+ contrib/restricted/boost/core
+ contrib/restricted/boost/detail
+ contrib/restricted/boost/io
+ contrib/restricted/boost/iterator
+ contrib/restricted/boost/predef
+ contrib/restricted/boost/scope
+ contrib/restricted/boost/smart_ptr
+ contrib/restricted/boost/system
+ contrib/restricted/boost/type_traits
+ contrib/restricted/boost/winapi
+)
+
+ADDINCL(
+ GLOBAL contrib/restricted/boost/filesystem/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ -DBOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF
+)
+
+IF (OS_LINUX)
+ CFLAGS(
+ GLOBAL -DBOOST_FILESYSTEM_HAS_POSIX_AT_APIS
+ )
+ENDIF()
+
+IF (DYNAMIC_BOOST)
+ CFLAGS(
+ GLOBAL -DBOOST_FILESYSTEM_DYN_LINK
+ -DBOOST_FILESYSTEM_SOURCE
+ )
+ENDIF()
+
+SRCS(
+ src/codecvt_error_category.cpp
+ src/directory.cpp
+ src/exception.cpp
+ src/operations.cpp
+ src/path.cpp
+ src/path_traits.cpp
+ src/portability.cpp
+ src/unique_path.cpp
+ src/utf8_codecvt_facet.cpp
+ src/windows_file_codecvt.cpp
+)
+
+END()
diff --git a/contrib/restricted/boost/scope/.yandex_meta/devtools.copyrights.report b/contrib/restricted/boost/scope/.yandex_meta/devtools.copyrights.report
new file mode 100644
index 0000000000..25c80d389e
--- /dev/null
+++ b/contrib/restricted/boost/scope/.yandex_meta/devtools.copyrights.report
@@ -0,0 +1,91 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits
+# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files
+# $ # user comments
+# $
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP COPYRIGHT_SERVICE_LABEL 3244b00e8ab273a03f19b20a64c26a96
+BELONGS ya.make
+ License text:
+ * Copyright (c) 2023 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/scope/detail/config.hpp [6:6]
+ include/boost/scope/detail/footer.hpp [6:6]
+ include/boost/scope/detail/header.hpp [6:6]
+ include/boost/scope/detail/is_nonnull_default_constructible.hpp [6:6]
+ include/boost/scope/detail/is_not_like.hpp [6:6]
+ include/boost/scope/detail/type_traits/conjunction.hpp [6:6]
+ include/boost/scope/detail/type_traits/disjunction.hpp [6:6]
+ include/boost/scope/detail/type_traits/is_final.hpp [6:6]
+ include/boost/scope/detail/type_traits/is_invocable.hpp [6:6]
+ include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp [6:6]
+ include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp [6:6]
+ include/boost/scope/detail/type_traits/is_swappable.hpp [6:6]
+ include/boost/scope/detail/type_traits/negation.hpp [6:6]
+ include/boost/scope/error_code_checker.hpp [6:6]
+ include/boost/scope/exception_checker.hpp [6:6]
+ include/boost/scope/fd_deleter.hpp [6:6]
+ include/boost/scope/fd_resource_traits.hpp [6:6]
+ include/boost/scope/scope_exit.hpp [6:6]
+ include/boost/scope/unique_fd.hpp [6:6]
+
+KEEP COPYRIGHT_SERVICE_LABEL 3f6ec3343e75c2c0c0e1e8956ffabc65
+BELONGS ya.make
+ License text:
+ * Copyright (c) 2022-2024 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/scope/defer.hpp [6:6]
+ include/boost/scope/unique_resource.hpp [6:6]
+
+KEEP COPYRIGHT_SERVICE_LABEL 45666b61e7d8667b41556a93c064e0e8
+BELONGS ya.make
+ License text:
+ * Copyright (c) 2022 Andrey Semashev
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/boost/scope/detail/compact_storage.hpp [6:6]
+ include/boost/scope/detail/move_or_copy_assign_ref.hpp [6:6]
+ include/boost/scope/detail/move_or_copy_construct_ref.hpp [6:6]
+ include/boost/scope/scope_fail.hpp [6:6]
+ include/boost/scope/scope_success.hpp [6:6]
+ include/boost/scope/unique_resource_fwd.hpp [6:6]
diff --git a/contrib/restricted/boost/scope/.yandex_meta/devtools.licenses.report b/contrib/restricted/boost/scope/.yandex_meta/devtools.licenses.report
new file mode 100644
index 0000000000..6fe13bd0ea
--- /dev/null
+++ b/contrib/restricted/boost/scope/.yandex_meta/devtools.licenses.report
@@ -0,0 +1,108 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits
+# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files
+# $ # user comments
+# $
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP BSL-1.0 2077ca9d01c7e6d6029ec1763233c5b0
+BELONGS ya.make
+ License text:
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/boost/scope/defer.hpp [2:4]
+ include/boost/scope/detail/compact_storage.hpp [2:4]
+ include/boost/scope/detail/config.hpp [2:4]
+ include/boost/scope/detail/footer.hpp [2:4]
+ include/boost/scope/detail/header.hpp [2:4]
+ include/boost/scope/detail/is_nonnull_default_constructible.hpp [2:4]
+ include/boost/scope/detail/is_not_like.hpp [2:4]
+ include/boost/scope/detail/move_or_copy_assign_ref.hpp [2:4]
+ include/boost/scope/detail/move_or_copy_construct_ref.hpp [2:4]
+ include/boost/scope/detail/type_traits/conjunction.hpp [2:4]
+ include/boost/scope/detail/type_traits/disjunction.hpp [2:4]
+ include/boost/scope/detail/type_traits/is_final.hpp [2:4]
+ include/boost/scope/detail/type_traits/is_invocable.hpp [2:4]
+ include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp [2:4]
+ include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp [2:4]
+ include/boost/scope/detail/type_traits/is_swappable.hpp [2:4]
+ include/boost/scope/detail/type_traits/negation.hpp [2:4]
+ include/boost/scope/error_code_checker.hpp [2:4]
+ include/boost/scope/exception_checker.hpp [2:4]
+ include/boost/scope/fd_deleter.hpp [2:4]
+ include/boost/scope/fd_resource_traits.hpp [2:4]
+ include/boost/scope/scope_exit.hpp [2:4]
+ include/boost/scope/scope_fail.hpp [2:4]
+ include/boost/scope/scope_success.hpp [2:4]
+ include/boost/scope/unique_fd.hpp [2:4]
+ include/boost/scope/unique_resource.hpp [2:4]
+ include/boost/scope/unique_resource_fwd.hpp [2:4]
+
+KEEP BSL-1.0 2c7a3fa82e66676005cd4ee2608fd7d2
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : TEXT
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ LICENSE [1:23]
+
+KEEP BSL-1.0 49af97cadb10453f2b05003f793e4adc
+BELONGS ya.make
+ License text:
+ Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 94.44
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ README.md [28:28]
+
+KEEP BSL-1.0 a5006bb276a0e8fcc0c080cd5a14814e
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 55.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ README.md [17:17]
diff --git a/contrib/restricted/boost/scope/.yandex_meta/licenses.list.txt b/contrib/restricted/boost/scope/.yandex_meta/licenses.list.txt
new file mode 100644
index 0000000000..6790a8ddab
--- /dev/null
+++ b/contrib/restricted/boost/scope/.yandex_meta/licenses.list.txt
@@ -0,0 +1,48 @@
+====================BSL-1.0====================
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+* Submit your patches as [pull requests](https://github.com/boostorg/scope/compare) against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+
+====================BSL-1.0====================
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+====================BSL-1.0====================
+Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+====================COPYRIGHT====================
+ * Copyright (c) 2022 Andrey Semashev
+
+
+====================COPYRIGHT====================
+ * Copyright (c) 2022-2024 Andrey Semashev
+
+
+====================COPYRIGHT====================
+ * Copyright (c) 2023 Andrey Semashev
diff --git a/contrib/restricted/boost/scope/LICENSE b/contrib/restricted/boost/scope/LICENSE
new file mode 100644
index 0000000000..36b7cd93cd
--- /dev/null
+++ b/contrib/restricted/boost/scope/LICENSE
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/contrib/restricted/boost/scope/README.md b/contrib/restricted/boost/scope/README.md
new file mode 100644
index 0000000000..21bc1f403c
--- /dev/null
+++ b/contrib/restricted/boost/scope/README.md
@@ -0,0 +1,28 @@
+# Boost.Scope
+
+Boost.Scope provides a number of scope guard utilities and a `unique_resource` wrapper, similar to those described in
+[C++ Extensions for Library Fundamentals, Version 3](https://github.com/cplusplus/fundamentals-ts/releases/tag/n4908),
+Section 3.3 Scope guard support \[scopeguard\]. The implementation also provides a few non-standard extensions.
+
+### Directories
+
+* **doc** - QuickBook documentation sources
+* **include** - Interface headers of Boost.Scope
+* **test** - Boost.Scope unit tests
+
+### More information
+
+* Read the [documentation](https://www.boost.org/libs/scope/).
+* [Report bugs](https://github.com/boostorg/scope/issues/new). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
+* Submit your patches as [pull requests](https://github.com/boostorg/scope/compare) against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
+
+### Build status
+
+Branch | GitHub Actions| Test Matrix | Dependencies |
+:-------------: | --------------| ----------- | ------------ |
+[`master`](https://github.com/boostorg/scope/tree/master) | [![GitHub Actions](https://github.com/boostorg/scope/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/boostorg/scope/actions?query=branch%3Amaster) | [![Tests](https://img.shields.io/badge/matrix-master-brightgreen.svg)](https://www.boost.org/development/tests/master/developer/scope.html) | [![Dependencies](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/scope.html)
+[`develop`](https://github.com/boostorg/scope/tree/develop) | [![GitHub Actions](https://github.com/boostorg/scope/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/boostorg/scope/actions?query=branch%3Adevelop) | [![Tests](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](https://www.boost.org/development/tests/develop/developer/scope.html) | [![Dependencies](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/scope.html)
+
+### License
+
+Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt).
diff --git a/contrib/restricted/boost/scope/include/boost/scope/defer.hpp b/contrib/restricted/boost/scope/include/boost/scope/defer.hpp
new file mode 100644
index 0000000000..988f976536
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/defer.hpp
@@ -0,0 +1,180 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022-2024 Andrey Semashev
+ */
+/*!
+ * \file scope/defer.hpp
+ *
+ * This header contains definition of \c defer_guard template.
+ */
+
+#ifndef BOOST_SCOPE_DEFER_HPP_INCLUDED_
+#define BOOST_SCOPE_DEFER_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/is_not_like.hpp>
+#include <boost/scope/detail/move_or_copy_construct_ref.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+template< typename Func >
+class defer_guard;
+
+namespace detail {
+
+// Workaround for clang < 5.0 which can't pass defer_guard as a template template parameter from within defer_guard definition
+template< typename T >
+using is_not_like_defer_guard = detail::is_not_like< T, defer_guard >;
+
+} // namespace detail
+
+/*!
+ * \brief Defer guard that invokes a function upon leaving the scope.
+ *
+ * The scope guard wraps a function object callable with no arguments
+ * that can be one of:
+ *
+ * \li A user-defined class with a public `operator()`.
+ * \li An lvalue reference to such class.
+ * \li An lvalue reference or pointer to function taking no arguments.
+ *
+ * The defer guard unconditionally invokes the wrapped function object
+ * on destruction.
+ */
+template< typename Func >
+class defer_guard
+{
+//! \cond
+private:
+ struct data
+ {
+ Func m_func;
+
+ template< typename F, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type >
+ explicit data(F&& func, std::true_type) noexcept :
+ m_func(static_cast< F&& >(func))
+ {
+ }
+
+ template< typename F, typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type >
+ explicit data(F&& func, std::false_type) try :
+ m_func(static_cast< F&& >(func))
+ {
+ }
+ catch (...)
+ {
+ func();
+ }
+ };
+
+ data m_data;
+
+//! \endcond
+public:
+ /*!
+ * \brief Constructs a defer guard with a given callable function object.
+ *
+ * **Requires:** \c Func is constructible from \a func.
+ *
+ * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
+ * `std::forward< F >(func)`, otherwise constructs from `func`.
+ *
+ * If \c Func construction throws, invokes \a func before returning with the exception.
+ *
+ * **Throws:** Nothing, unless construction of the function object throws.
+ *
+ * \param func The callable function object to invoke on destruction.
+ */
+ template<
+ typename F
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type
+ >,
+ detail::is_not_like_defer_guard< F >
+ >::value >::type
+ //! \endcond
+ >
+ defer_guard(F&& func)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
+ typename std::is_nothrow_constructible< Func, typename detail::move_or_copy_construct_ref< F, Func >::type >::type()
+ )
+ {
+ }
+
+ defer_guard(defer_guard const&) = delete;
+ defer_guard& operator= (defer_guard const&) = delete;
+
+ /*!
+ * \brief Invokes the wrapped callable function object and destroys the callable.
+ *
+ * **Throws:** Nothing, unless invoking the callable throws.
+ */
+ ~defer_guard() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< Func& >::value))
+ {
+ m_data.m_func();
+ }
+};
+
+#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+template< typename Func >
+defer_guard(Func) -> defer_guard< Func >;
+#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+
+} // namespace scope
+
+//! \cond
+#if defined(BOOST_MSVC)
+#define BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG __COUNTER__
+#else
+#define BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG __LINE__
+#endif
+//! \endcond
+
+/*!
+ * \brief The macro creates a uniquely named defer guard.
+ *
+ * The macro should be followed by a function object that should be called
+ * on leaving the current scope. Usage example:
+ *
+ * ```
+ * BOOST_SCOPE_DEFER []
+ * {
+ * std::cout << "Hello world!" << std::endl;
+ * };
+ * ```
+ *
+ * \note Using this macro requires C++17.
+ */
+#define BOOST_SCOPE_DEFER \
+ boost::scope::defer_guard BOOST_JOIN(_boost_defer_guard_, BOOST_SCOPE_DETAIL_UNIQUE_VAR_TAG) =
+
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DEFER_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/compact_storage.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/compact_storage.hpp
new file mode 100644
index 0000000000..b619e3f7d3
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/compact_storage.hpp
@@ -0,0 +1,102 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/compact_storage.hpp
+ *
+ * This header contains utility helpers for implementing compact storage
+ * for class members. In particular, it allows to leverage empty base optimization (EBO).
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_COMPACT_STORAGE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_COMPACT_STORAGE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/type_traits/is_final.hpp>
+#include <boost/scope/detail/type_traits/negation.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+//! The class allows to place data members in the tail padding of type \a T if the user's class derives from it
+template<
+ typename T,
+ typename Tag = void,
+ bool = detail::conjunction< std::is_class< T >, detail::negation< detail::is_final< T > > >::value
+>
+class compact_storage :
+ private T
+{
+public:
+ template< typename... Args >
+ constexpr compact_storage(Args&&... args) noexcept(std::is_nothrow_constructible< T, Args... >::value) :
+ T(static_cast< Args&& >(args)...)
+ {
+ }
+
+ compact_storage(compact_storage&&) = default;
+ compact_storage& operator= (compact_storage&&) = default;
+
+ compact_storage(compact_storage const&) = default;
+ compact_storage& operator= (compact_storage const&) = default;
+
+ T& get() noexcept
+ {
+ return *static_cast< T* >(this);
+ }
+
+ T const& get() const noexcept
+ {
+ return *static_cast< const T* >(this);
+ }
+};
+
+template< typename T, typename Tag >
+class compact_storage< T, Tag, false >
+{
+private:
+ T m_data;
+
+public:
+ template< typename... Args >
+ constexpr compact_storage(Args&&... args) noexcept(std::is_nothrow_constructible< T, Args... >::value) :
+ m_data(static_cast< Args&& >(args)...)
+ {
+ }
+
+ compact_storage(compact_storage&&) = default;
+ compact_storage& operator= (compact_storage&&) = default;
+
+ compact_storage(compact_storage const&) = default;
+ compact_storage& operator= (compact_storage const&) = default;
+
+ T& get() noexcept
+ {
+ return m_data;
+ }
+
+ T const& get() const noexcept
+ {
+ return m_data;
+ }
+};
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_COMPACT_STORAGE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/config.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/config.hpp
new file mode 100644
index 0000000000..26821ea344
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/config.hpp
@@ -0,0 +1,50 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/config.hpp
+ *
+ * This header contains Boost.Scope common configuration.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_CONFIG_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_CONFIG_HPP_INCLUDED_
+
+#include <boost/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if !(defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510l) && !defined(_NOEXCEPT_TYPES_SUPPORTED)
+#define BOOST_SCOPE_NO_CXX17_NOEXCEPT_FUNCTION_TYPES
+#endif
+
+#if !defined(BOOST_SCOPE_DETAIL_DOC_ALT)
+#if !defined(BOOST_SCOPE_DOXYGEN)
+#define BOOST_SCOPE_DETAIL_DOC_ALT(alt, ...) __VA_ARGS__
+#else
+#define BOOST_SCOPE_DETAIL_DOC_ALT(alt, ...) alt
+#endif
+#endif
+
+#if !defined(BOOST_SCOPE_DETAIL_DOC_HIDDEN)
+#define BOOST_SCOPE_DETAIL_DOC_HIDDEN(...) BOOST_SCOPE_DETAIL_DOC_ALT(..., __VA_ARGS__)
+#endif
+
+#if !defined(BOOST_SCOPE_DETAIL_DOC)
+#if !defined(BOOST_SCOPE_DOXYGEN)
+#define BOOST_SCOPE_DETAIL_DOC(...)
+#else
+#define BOOST_SCOPE_DETAIL_DOC(...) __VA_ARGS__
+#endif
+#endif
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_CONFIG_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/footer.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/footer.hpp
new file mode 100644
index 0000000000..2d9b12e6ba
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/footer.hpp
@@ -0,0 +1,22 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+
+#if !defined(BOOST_SCOPE_ENABLE_WARNINGS)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+#pragma warning(pop)
+
+#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \
+ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__)
+
+#pragma GCC diagnostic pop
+
+#endif
+
+#endif // !defined(BOOST_SCOPE_ENABLE_WARNINGS)
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/header.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/header.hpp
new file mode 100644
index 0000000000..fc99f16cf1
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/header.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+
+#if !defined(BOOST_SCOPE_ENABLE_WARNINGS)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+
+#pragma warning(push, 3)
+// unreferenced formal parameter
+#pragma warning(disable: 4100)
+// conditional expression is constant
+#pragma warning(disable: 4127)
+// function marked as __forceinline not inlined
+#pragma warning(disable: 4714)
+// decorated name length exceeded, name was truncated
+#pragma warning(disable: 4503)
+// qualifier applied to function type has no meaning; ignored
+#pragma warning(disable: 4180)
+// qualifier applied to reference type; ignored
+#pragma warning(disable: 4181)
+// unreachable code
+#pragma warning(disable: 4702)
+// destructor never returns, potential memory leak
+#pragma warning(disable: 4722)
+
+#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \
+ && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__)
+
+// Note: clang-cl goes here as well, as it seems to support gcc-style warning control pragmas.
+
+#pragma GCC diagnostic push
+// unused parameter 'arg'
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+// unused function 'foo'
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#if defined(__clang__)
+// template argument uses unnamed type
+#pragma clang diagnostic ignored "-Wunnamed-type-template-args"
+#endif // defined(__clang__)
+
+#endif
+
+#endif // !defined(BOOST_SCOPE_ENABLE_WARNINGS)
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/is_nonnull_default_constructible.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/is_nonnull_default_constructible.hpp
new file mode 100644
index 0000000000..e6e86474f4
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/is_nonnull_default_constructible.hpp
@@ -0,0 +1,66 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/is_nonnull_default_constructible.hpp
+ *
+ * This header contains definition of \c is_nonnull_default_constructible
+ * and \c is_nothrow_nonnull_default_constructible type traits. The type
+ * traits are useful for preventing default-construction of pointers to
+ * functions where a default-constructed function object is expected.
+ * Without it, default- or value-constructing a pointer to function would
+ * produce a function object that is not callable.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+//! The type trait checks if \c T is not a pointer and is default-constructible
+template< typename T >
+struct is_nonnull_default_constructible :
+ public std::is_default_constructible< T >
+{
+};
+
+template< typename T >
+struct is_nonnull_default_constructible< T* > :
+ public std::false_type
+{
+};
+
+//! The type trait checks if \c T is not a pointer and is nothrow-default-constructible
+template< typename T >
+struct is_nothrow_nonnull_default_constructible :
+ public std::is_nothrow_default_constructible< T >
+{
+};
+
+template< typename T >
+struct is_nothrow_nonnull_default_constructible< T* > :
+ public std::false_type
+{
+};
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_IS_NONNULL_DEFAULT_CONSTRUCTIBLE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/is_not_like.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/is_not_like.hpp
new file mode 100644
index 0000000000..3f07fa8023
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/is_not_like.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/is_not_like.hpp
+ *
+ * This header contains definition of \c is_not_like type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_IS_NOT_LIKE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_IS_NOT_LIKE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+//! The type trait checks if \c T is not a possibly cv-reference-qualified specialization of \c Template
+template< typename T, template< typename... > class Template >
+struct is_not_like : public std::true_type { };
+template< typename T, template< typename... > class Template >
+struct is_not_like< T&, Template > : public is_not_like< T, Template > { };
+template< template< typename... > class Template, typename... Ts >
+struct is_not_like< Template< Ts... >, Template > : public std::false_type { };
+template< template< typename... > class Template, typename... Ts >
+struct is_not_like< const Template< Ts... >, Template > : public std::false_type { };
+template< template< typename... > class Template, typename... Ts >
+struct is_not_like< volatile Template< Ts... >, Template > : public std::false_type { };
+template< template< typename... > class Template, typename... Ts >
+struct is_not_like< const volatile Template< Ts... >, Template > : public std::false_type { };
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_IS_NOT_LIKE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_assign_ref.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_assign_ref.hpp
new file mode 100644
index 0000000000..0e270f8c1b
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_assign_ref.hpp
@@ -0,0 +1,52 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/move_or_copy_assign_ref.hpp
+ *
+ * This header contains definition of \c move_or_copy_assign_ref type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_MOVE_OR_COPY_ASSIGN_REF_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_MOVE_OR_COPY_ASSIGN_REF_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+//! The type trait produces an rvalue reference to \a From if \a To has a non-throwing assignment from a \a From rvalue and an lvalue reference otherwise.
+template< typename From, typename To = From >
+struct move_or_copy_assign_ref
+{
+ using type = typename std::conditional<
+ std::is_nothrow_assignable< To, From >::value,
+ From&&,
+ From const&
+ >::type;
+};
+
+template< typename From, typename To >
+struct move_or_copy_assign_ref< From&, To >
+{
+ using type = From&;
+};
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_MOVE_OR_COPY_ASSIGN_REF_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_construct_ref.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_construct_ref.hpp
new file mode 100644
index 0000000000..b3d3796868
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/move_or_copy_construct_ref.hpp
@@ -0,0 +1,52 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/move_or_copy_construct_ref.hpp
+ *
+ * This header contains definition of \c move_or_copy_construct_ref type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_MOVE_OR_COPY_CONSTRUCT_REF_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_MOVE_OR_COPY_CONSTRUCT_REF_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+//! The type trait produces an rvalue reference to \a From if \a To has a non-throwing constructor from a \a From rvalue and an lvalue reference otherwise.
+template< typename From, typename To = From >
+struct move_or_copy_construct_ref
+{
+ using type = typename std::conditional<
+ std::is_nothrow_constructible< To, From >::value,
+ From&&,
+ From const&
+ >::type;
+};
+
+template< typename From, typename To >
+struct move_or_copy_construct_ref< From&, To >
+{
+ using type = From&;
+};
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_DETAIL_MOVE_OR_COPY_CONSTRUCT_REF_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/conjunction.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/conjunction.hpp
new file mode 100644
index 0000000000..497f5721d2
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/conjunction.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/conjunction.hpp
+ *
+ * This header contains definition of \c conjunction type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::conjunction;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/conjunction.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::conjunction;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_CONJUNCTION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/disjunction.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/disjunction.hpp
new file mode 100644
index 0000000000..af1532dede
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/disjunction.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/disjunction.hpp
+ *
+ * This header contains definition of \c disjunction type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::disjunction;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/disjunction.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::disjunction;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_DISJUNCTION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_final.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_final.hpp
new file mode 100644
index 0000000000..57a2bf255b
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_final.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/is_final.hpp
+ *
+ * This header contains definition of \c is_final type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_FINAL_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_FINAL_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_is_final) && (__cpp_lib_is_final >= 201402l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (BOOST_CXX_VERSION >= 201402l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::is_final;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/is_final.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::is_final;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_FINAL_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_invocable.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_invocable.hpp
new file mode 100644
index 0000000000..987bf1b10b
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_invocable.hpp
@@ -0,0 +1,63 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/is_invocable.hpp
+ *
+ * This header contains definition of \c is_invocable type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_INVOCABLE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_INVOCABLE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::is_invocable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+// A simplified implementation that does not support member function pointers
+template< typename Func, typename... Args >
+struct is_invocable_impl
+{
+ template< typename F = Func, typename = decltype(std::declval< F >()(std::declval< Args >()...)) >
+ static std::true_type _check_invocable(int);
+ static std::false_type _check_invocable(...);
+
+ using type = decltype(is_invocable_impl::_check_invocable(0));
+};
+
+template< typename Func, typename... Args >
+struct is_invocable : public is_invocable_impl< Func, Args... >::type { };
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_INVOCABLE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp
new file mode 100644
index 0000000000..5510a5fb0a
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_invocable.hpp
@@ -0,0 +1,69 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/is_nothrow_invocable.hpp
+ *
+ * This header contains definition of \c is_nothrow_invocable type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_INVOCABLE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_INVOCABLE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_is_invocable) && (__cpp_lib_is_invocable >= 201703l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::is_nothrow_invocable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/scope/detail/type_traits/is_invocable.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+template< bool, typename Func, typename... Args >
+struct is_nothrow_invocable_impl
+{
+ using type = std::false_type;
+};
+
+template< typename Func, typename... Args >
+struct is_nothrow_invocable_impl< true, Func, Args... >
+{
+ using type = std::integral_constant< bool, noexcept(std::declval< Func >()(std::declval< Args >()...)) >;
+};
+
+template< typename Func, typename... Args >
+struct is_nothrow_invocable :
+ public is_nothrow_invocable_impl< detail::is_invocable< Func, Args... >::value, Func, Args... >::type
+{
+};
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_INVOCABLE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp
new file mode 100644
index 0000000000..08e3171655
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_nothrow_swappable.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/is_nothrow_swappable.hpp
+ *
+ * This header contains definition of \c is_nothrow_swappable type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_SWAPPABLE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_SWAPPABLE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_is_swappable) && (__cpp_lib_is_swappable >= 201603l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190024210) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::is_nothrow_swappable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/is_nothrow_swappable.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::is_nothrow_swappable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_NOTHROW_SWAPPABLE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_swappable.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_swappable.hpp
new file mode 100644
index 0000000000..c84a147913
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/is_swappable.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/is_swappable.hpp
+ *
+ * This header contains definition of \c is_swappable type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_SWAPPABLE_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_SWAPPABLE_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_is_swappable) && (__cpp_lib_is_swappable >= 201603l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190024210) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::is_swappable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/is_swappable.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::is_swappable;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_IS_SWAPPABLE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/negation.hpp b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/negation.hpp
new file mode 100644
index 0000000000..f78f52f22c
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/detail/type_traits/negation.hpp
@@ -0,0 +1,53 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/detail/type_traits/negation.hpp
+ *
+ * This header contains definition of \c negation type trait.
+ */
+
+#ifndef BOOST_SCOPE_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
+#define BOOST_SCOPE_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#if (defined(__cpp_lib_logical_traits) && (__cpp_lib_logical_traits >= 201510l)) || \
+ (defined(BOOST_MSSTL_VERSION) && (BOOST_MSSTL_VERSION >= 140) && (_MSC_FULL_VER >= 190023918) && (BOOST_CXX_VERSION >= 201703l))
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using std::negation;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#else
+
+#include <boost/type_traits/negation.hpp>
+
+namespace boost {
+namespace scope {
+namespace detail {
+
+using boost::negation;
+
+} // namespace detail
+} // namespace scope
+} // namespace boost
+
+#endif
+
+#endif // BOOST_SCOPE_DETAIL_TYPE_TRAITS_NEGATION_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/error_code_checker.hpp b/contrib/restricted/boost/scope/include/boost/scope/error_code_checker.hpp
new file mode 100644
index 0000000000..1481e14f52
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/error_code_checker.hpp
@@ -0,0 +1,103 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/error_code_checker.hpp
+ *
+ * This header contains definition of \c error_code_checker type.
+ */
+
+#ifndef BOOST_SCOPE_ERROR_CODE_CHECKER_HPP_INCLUDED_
+#define BOOST_SCOPE_ERROR_CODE_CHECKER_HPP_INCLUDED_
+
+#include <boost/core/addressof.hpp>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+/*!
+ * \brief A predicate for checking whether an error code indicates error.
+ *
+ * The predicate captures a reference to an external error code object, which it
+ * tests for an error indication when called. The error code object must remain
+ * valid for the whole lifetime duration of the predicate.
+ *
+ * For an error code object `ec`, an expression `!ec` must be valid, never throw exceptions,
+ * and return a value contextually convertible to `bool`. If the returned value converts
+ * to `false`, then this is taken as an error indication, and the predicate returns `true`.
+ * Otherwise, the predicate returns `false`.
+ *
+ * A few examples of error code types:
+ *
+ * \li `std::error_code` or `boost::system::error_code`,
+ * \li `std::expected`, `boost::outcome_v2::basic_outcome` or `boost::outcome_v2::basic_result`,
+ * \li `int`, where the value of 0 indicates no error,
+ * \li `bool`, where the value of `false` indicates no error,
+ * \li `T*`, where a null pointer indicates no error.
+ *
+ * \tparam ErrorCode Error code type.
+ */
+template< typename ErrorCode >
+class error_code_checker
+{
+public:
+ //! Predicate result type
+ using result_type = bool;
+
+private:
+ ErrorCode* m_error_code;
+
+public:
+ /*!
+ * \brief Constructs the predicate.
+ *
+ * Upon construction, the predicate saves a reference to the external error code object.
+ * The referenced object must remain valid for the whole lifetime duration of the predicate.
+ *
+ * **Throws:** Nothing.
+ */
+ explicit error_code_checker(ErrorCode& ec) noexcept :
+ m_error_code(boost::addressof(ec))
+ {
+ }
+
+ /*!
+ * \brief Checks if the error code indicates error.
+ *
+ * **Throws:** Nothing.
+ *
+ * \returns As if `!!ec`, where `ec` is the error code object passed to the predicate constructor.
+ */
+ result_type operator()() const noexcept
+ {
+ return !!(*m_error_code);
+ }
+};
+
+/*!
+ * \brief Creates a predicate for checking whether an exception is being thrown
+ *
+ * **Throws:** Nothing.
+ */
+template< typename ErrorCode >
+inline error_code_checker< ErrorCode > check_error_code(ErrorCode& ec) noexcept
+{
+ return error_code_checker< ErrorCode >(ec);
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_ERROR_CODE_CHECKER_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/exception_checker.hpp b/contrib/restricted/boost/scope/include/boost/scope/exception_checker.hpp
new file mode 100644
index 0000000000..a46b4801b7
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/exception_checker.hpp
@@ -0,0 +1,106 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/exception_checker.hpp
+ *
+ * This header contains definition of \c exception_checker type.
+ */
+
+#ifndef BOOST_SCOPE_EXCEPTION_CHECKER_HPP_INCLUDED_
+#define BOOST_SCOPE_EXCEPTION_CHECKER_HPP_INCLUDED_
+
+#include <boost/assert.hpp>
+#include <boost/scope/detail/config.hpp>
+#include <boost/core/uncaught_exceptions.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+/*!
+ * \brief A predicate for checking whether an exception is being thrown.
+ *
+ * On construction, the predicate captures the current number of uncaught exceptions,
+ * which it then compares with the number of uncaught exceptions at the point when it
+ * is called. If the number increased then a new exception is detected and the predicate
+ * returns \c true.
+ *
+ * \note This predicate is designed for a specific use case with scope guards created on
+ * the stack. It is incompatible with C++20 coroutines and similar facilities (e.g.
+ * fibers and userspace context switching), where the thread of execution may be
+ * suspended after the predicate captures the number of uncaught exceptions and
+ * then resumed in a different context, where the number of uncaught exceptions
+ * has changed. Similarly, it is incompatible with usage patterns where the predicate
+ * is cached after construction and is invoked after the thread has left the scope
+ * where the predicate was constructed (e.g. when the predicate is stored as a class
+ * data member or a namespace-scope variable).
+ */
+class exception_checker
+{
+public:
+ //! Predicate result type
+ using result_type = bool;
+
+private:
+ unsigned int m_uncaught_count;
+
+public:
+ /*!
+ * \brief Constructs the predicate.
+ *
+ * Upon construction, the predicate saves the current number of uncaught exceptions.
+ * This information will be used when calling the predicate to detect if a new
+ * exception is being thrown.
+ *
+ * **Throws:** Nothing.
+ */
+ exception_checker() noexcept :
+ m_uncaught_count(boost::core::uncaught_exceptions())
+ {
+ }
+
+ /*!
+ * \brief Checks if an exception is being thrown.
+ *
+ * **Throws:** Nothing.
+ *
+ * \returns \c true if the number of uncaught exceptions at the point of call is
+ * greater than that at the point of construction of the predicate,
+ * otherwise \c false.
+ */
+ result_type operator()() const noexcept
+ {
+ const unsigned int uncaught_count = boost::core::uncaught_exceptions();
+ // If this assertion fails, the predicate is likely being used in an unsupported
+ // way, where it is called in a different scope or thread context from where
+ // it was constructed.
+ BOOST_ASSERT((uncaught_count - m_uncaught_count) <= 1u);
+ return uncaught_count > m_uncaught_count;
+ }
+};
+
+/*!
+ * \brief Creates a predicate for checking whether an exception is being thrown
+ *
+ * **Throws:** Nothing.
+ */
+inline exception_checker check_exception() noexcept
+{
+ return exception_checker();
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_EXCEPTION_CHECKER_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/fd_deleter.hpp b/contrib/restricted/boost/scope/include/boost/scope/fd_deleter.hpp
new file mode 100644
index 0000000000..92f3ee21f5
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/fd_deleter.hpp
@@ -0,0 +1,82 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/fd_deleter.hpp
+ *
+ * This header contains definition of a deleter function object for
+ * POSIX-like file descriptors for use with \c unique_resource.
+ */
+
+#ifndef BOOST_SCOPE_FD_DELETER_HPP_INCLUDED_
+#define BOOST_SCOPE_FD_DELETER_HPP_INCLUDED_
+
+#include <boost/scope/detail/config.hpp>
+
+#if !defined(BOOST_WINDOWS)
+#include <unistd.h>
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+#include <cerrno>
+#endif
+#else // !defined(BOOST_WINDOWS)
+#include <io.h>
+#endif // !defined(BOOST_WINDOWS)
+
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+//! POSIX-like file descriptor deleter
+struct fd_deleter
+{
+ using result_type = void;
+
+ //! Closes the file descriptor
+ result_type operator() (int fd) const noexcept
+ {
+#if !defined(BOOST_WINDOWS)
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+ // Some systems don't close the file descriptor in case if the thread is interrupted by a signal and close(2) returns EINTR.
+ // Other (most) systems do close the file descriptor even when when close(2) returns EINTR, and attempting to close it
+ // again could close a different file descriptor that was opened by a different thread.
+ //
+ // Future POSIX standards will likely fix this by introducing posix_close (see https://www.austingroupbugs.net/view.php?id=529)
+ // and prohibiting returning EINTR from close(2), but we still have to support older systems where this new behavior is not available and close(2)
+ // behaves differently between systems.
+ int res;
+ while (true)
+ {
+ res = ::close(fd);
+ if (BOOST_UNLIKELY(res < 0))
+ {
+ int err = errno;
+ if (err == EINTR)
+ continue;
+ }
+
+ break;
+ }
+#else
+ ::close(fd);
+#endif
+#else // !defined(BOOST_WINDOWS)
+ ::_close(fd);
+#endif // !defined(BOOST_WINDOWS)
+ }
+};
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_FD_DELETER_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/fd_resource_traits.hpp b/contrib/restricted/boost/scope/include/boost/scope/fd_resource_traits.hpp
new file mode 100644
index 0000000000..76177979ca
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/fd_resource_traits.hpp
@@ -0,0 +1,49 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/fd_resource_traits.hpp
+ *
+ * This header contains definition of \c unique_resource traits
+ * for compatibility with POSIX-like file descriptors.
+ */
+
+#ifndef BOOST_SCOPE_FD_RESOURCE_TRAITS_HPP_INCLUDED_
+#define BOOST_SCOPE_FD_RESOURCE_TRAITS_HPP_INCLUDED_
+
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+//! POSIX-like file descriptor resource traits
+struct fd_resource_traits
+{
+ //! Creates a default fd value
+ static int make_default() noexcept
+ {
+ return -1;
+ }
+
+ //! Tests if the fd is allocated (valid)
+ static bool is_allocated(int fd) noexcept
+ {
+ return fd >= 0;
+ }
+};
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_FD_RESOURCE_TRAITS_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/scope_exit.hpp b/contrib/restricted/boost/scope/include/boost/scope/scope_exit.hpp
new file mode 100644
index 0000000000..0f03ff4c47
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/scope_exit.hpp
@@ -0,0 +1,560 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/scope_exit.hpp
+ *
+ * This header contains definition of \c scope_exit template.
+ */
+
+#ifndef BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
+#define BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/is_not_like.hpp>
+#include <boost/scope/detail/compact_storage.hpp>
+#include <boost/scope/detail/move_or_copy_construct_ref.hpp>
+#include <boost/scope/detail/is_nonnull_default_constructible.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/type_traits/is_invocable.hpp>
+#include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+template< typename Func, typename Cond >
+class scope_exit;
+
+namespace detail {
+
+// Workaround for clang < 5.0 which can't pass scope_exit as a template template parameter from within scope_exit definition
+template< typename T >
+using is_not_like_scope_exit = detail::is_not_like< T, scope_exit >;
+
+//! The scope guard used to invoke the condition and action functions in case of exception during scope guard construction
+template< typename Func, typename Cond >
+class init_guard
+{
+private:
+ Func& m_func;
+ Cond& m_cond;
+ bool m_active;
+
+public:
+ init_guard(Func& func, Cond& cond, bool active) noexcept :
+ m_func(func),
+ m_cond(cond),
+ m_active(active)
+ {
+ }
+
+ init_guard(init_guard const&) = delete;
+ init_guard& operator= (init_guard const&) = delete;
+
+ ~init_guard()
+ noexcept(detail::conjunction<
+ detail::is_nothrow_invocable< Func& >,
+ detail::is_nothrow_invocable< Cond& >
+ >::value)
+ {
+ if (m_active && m_cond())
+ m_func();
+ }
+
+ Func&& get_func() noexcept
+ {
+ return static_cast< Func&& >(m_func);
+ }
+
+ Cond&& get_cond() noexcept
+ {
+ return static_cast< Cond&& >(m_cond);
+ }
+
+ void deactivate() noexcept
+ {
+ m_active = false;
+ }
+};
+
+} // namespace detail
+
+/*!
+ * \brief A predicate that always returns \c true.
+ *
+ * This predicate can be used as the default condition function object for
+ * \c scope_exit and similar scope guards.
+ */
+class always_true
+{
+public:
+ //! Predicate result type
+ using result_type = bool;
+
+ /*!
+ * **Throws:** Nothing.
+ *
+ * \returns \c true.
+ */
+ result_type operator()() const noexcept
+ {
+ return true;
+ }
+};
+
+/*!
+ * \brief Scope exit guard that conditionally invokes a function upon leaving the scope.
+ *
+ * The scope guard wraps two function objects: the scope guard action and
+ * a condition for invoking the action. Both function objects must be
+ * callable with no arguments and can be one of:
+ *
+ * \li A user-defined class with a public `operator()`.
+ * \li An lvalue reference to such class.
+ * \li An lvalue reference or pointer to function taking no arguments.
+ *
+ * The condition function object `operator()` must return a value
+ * contextually convertible to \c true, if the action function object
+ * is allowed to be executed, and \c false otherwise. Additionally,
+ * the condition function object `operator()` must not throw, as
+ * otherwise the action function object may not be called.
+ *
+ * The condition function object is optional, and if not specified in
+ * template parameters, the scope guard will operate as if the condition
+ * always returns \c true.
+ *
+ * The scope guard can be in either active or inactive state. By default,
+ * the constructed scope guard is active. When active, and condition
+ * function object returns \c true, the scope guard invokes the wrapped
+ * action function object on destruction. Otherwise, the scope guard
+ * does not call the wrapped action function object.
+ *
+ * The scope guard can be made inactive by moving-from the scope guard
+ * or calling `set_active(false)`. An inactive scope guard can be made
+ * active by calling `set_active(true)`. If a moved-from scope guard
+ * is active on destruction, the behavior is undefined.
+ *
+ * \tparam Func Scope guard action function object type.
+ * \tparam Cond Scope guard condition function object type.
+ */
+template< typename Func, typename Cond = always_true >
+class scope_exit
+{
+//! \cond
+private:
+ struct func_holder :
+ public detail::compact_storage< Func >
+ {
+ using func_base = detail::compact_storage< Func >;
+
+ template<
+ typename F,
+ typename C,
+ typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type
+ >
+ explicit func_holder(F&& func, C&& cond, bool active, std::true_type) noexcept :
+ func_base(static_cast< F&& >(func))
+ {
+ }
+
+ template<
+ typename F,
+ typename C,
+ typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type
+ >
+ explicit func_holder(F&& func, C&& cond, bool active, std::false_type) :
+ func_holder(detail::init_guard< F, C >(func, cond, active))
+ {
+ }
+
+ private:
+ template< typename F, typename C >
+ explicit func_holder(detail::init_guard< F, C >&& init) :
+ func_base(init.get_func())
+ {
+ init.deactivate();
+ }
+ };
+
+ struct cond_holder :
+ public detail::compact_storage< Cond >
+ {
+ using cond_base = detail::compact_storage< Cond >;
+
+ template<
+ typename C,
+ typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type
+ >
+ explicit cond_holder(C&& cond, Func& func, bool active, std::true_type) noexcept :
+ cond_base(static_cast< C&& >(cond))
+ {
+ }
+
+ template<
+ typename C,
+ typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type
+ >
+ explicit cond_holder(C&& cond, Func& func, bool active, std::false_type) :
+ cond_holder(detail::init_guard< Func&, C >(func, cond, active))
+ {
+ }
+
+ private:
+ template< typename C >
+ explicit cond_holder(detail::init_guard< Func&, C >&& init) :
+ cond_base(init.get_cond())
+ {
+ init.deactivate();
+ }
+ };
+
+ struct data :
+ public func_holder,
+ public cond_holder
+ {
+ bool m_active;
+
+ template<
+ typename F,
+ typename C,
+ typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< func_holder, F, C, bool, typename std::is_nothrow_constructible< Func, F >::type >,
+ std::is_constructible< cond_holder, C, Func&, bool, typename std::is_nothrow_constructible< Cond, C >::type >
+ >::value >::type
+ >
+ explicit data(F&& func, C&& cond, bool active)
+ noexcept(detail::conjunction< std::is_nothrow_constructible< Func, F >, std::is_nothrow_constructible< Cond, C > >::value) :
+ func_holder(static_cast< F&& >(func), static_cast< C&& >(cond), active, typename std::is_nothrow_constructible< Func, F >::type()),
+ cond_holder(static_cast< C&& >(cond), func_holder::get(), active, typename std::is_nothrow_constructible< Cond, C >::type()),
+ m_active(active)
+ {
+ }
+
+ Func& get_func() noexcept
+ {
+ return func_holder::get();
+ }
+
+ Func const& get_func() const noexcept
+ {
+ return func_holder::get();
+ }
+
+ Cond& get_cond() noexcept
+ {
+ return cond_holder::get();
+ }
+
+ Cond const& get_cond() const noexcept
+ {
+ return cond_holder::get();
+ }
+
+ bool deactivate() noexcept
+ {
+ bool active = m_active;
+ m_active = false;
+ return active;
+ }
+ };
+
+ data m_data;
+
+//! \endcond
+public:
+ /*!
+ * \brief Constructs a scope guard with a given callable action function object.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible
+ * and is not a pointer to function.
+ *
+ * \note The requirement for \c Cond default constructor to be non-throwing is to allow for
+ * the condition function object to be called in case if constructing either function
+ * object throws.
+ *
+ * **Effects:** Constructs the scope guard as if by calling
+ * `scope_exit(std::forward< F >(func), Cond(), active)`.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ detail::is_nothrow_nonnull_default_constructible< Cond >,
+ std::is_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename detail::move_or_copy_construct_ref< Cond >::type,
+ bool
+ >,
+ detail::is_not_like_scope_exit< F >
+ >::value >::type
+ //! \endcond
+ >
+ explicit scope_exit(F&& func, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename detail::move_or_copy_construct_ref< Cond >::type,
+ bool
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
+ static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(Cond()),
+ active
+ )
+ {
+ }
+
+ /*!
+ * \brief Constructs a scope guard with a given callable action and condition function objects.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is constructible from \a cond.
+ *
+ * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
+ * `std::forward< F >(func)`, otherwise constructs from `func`. If \c Cond is
+ * nothrow constructible from `C&&` then constructs \c Cond from
+ * `std::forward< C >(cond)`, otherwise constructs from `cond`.
+ *
+ * If \c Func or \c Cond construction throws and \a active is \c true, invokes
+ * \a cond and, if it returns \c true, \a func before returning with the exception.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F,
+ typename C
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ detail::is_invocable< C const& >,
+ std::is_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename detail::move_or_copy_construct_ref< C, Cond >::type,
+ bool
+ >
+ >::value >::type
+ //! \endcond
+ >
+ explicit scope_exit(F&& func, C&& cond, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< F, Func >::type,
+ typename detail::move_or_copy_construct_ref< C, Cond >::type,
+ bool
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
+ static_cast< typename detail::move_or_copy_construct_ref< C, Cond >::type >(cond),
+ active
+ )
+ {
+ }
+
+ /*!
+ * \brief Move-constructs a scope guard.
+ *
+ * **Requires:** \c Func and \c Cond are nothrow move-constructible or copy-constructible.
+ *
+ * **Effects:** If \c Func is nothrow move-constructible then move-constructs \c Func from
+ * a member of \a that, otherwise copy-constructs \c Func. If \c Cond is nothrow
+ * move-constructible then move-constructs \c Cond from a member of \a that,
+ * otherwise copy-constructs \c Cond.
+ *
+ * If \c Func or \c Cond construction throws and `that.active() == true`, invokes
+ * \c Cond object stored in \a that and, if it returns \c true, \a Func object
+ * (either the newly constructed one, if its construction succeeded, or the original
+ * one stored in \a that) before returning with the exception.
+ *
+ * If the construction succeeds, marks \a that as inactive.
+ *
+ * **Throws:** Nothing, unless move-construction of the function objects throw.
+ *
+ * \param that Move source.
+ *
+ * \post `that.active() == false`
+ */
+ //! \cond
+ template<
+ bool Requires = std::is_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< Func >::type,
+ typename detail::move_or_copy_construct_ref< Cond >::type,
+ bool
+ >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ //! \endcond
+ scope_exit(scope_exit&& that)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< Func >::type,
+ typename detail::move_or_copy_construct_ref< Cond >::type,
+ bool
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< Func >::type >(that.m_data.get_func()),
+ static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(that.m_data.get_cond()),
+ that.m_data.deactivate()
+ )
+ {
+ }
+
+ scope_exit& operator= (scope_exit&&) = delete;
+
+ scope_exit(scope_exit const&) = delete;
+ scope_exit& operator= (scope_exit const&) = delete;
+
+ /*!
+ * \brief If `active() == true`, and invoking the condition function object returns \c true, invokes
+ * the wrapped callable action function object. Destroys the function objects.
+ *
+ * **Throws:** Nothing, unless invoking a function object throws.
+ */
+ ~scope_exit()
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ detail::conjunction<
+ detail::is_nothrow_invocable< Func& >,
+ detail::is_nothrow_invocable< Cond& >
+ >::value
+ ))
+ {
+ if (BOOST_LIKELY(m_data.m_active && m_data.get_cond()()))
+ m_data.get_func()();
+ }
+
+ /*!
+ * \brief Returns \c true if the scope guard is active, otherwise \c false.
+ *
+ * \note This method does not call the condition function object specified on construction.
+ *
+ * **Throws:** Nothing.
+ */
+ bool active() const noexcept
+ {
+ return m_data.m_active;
+ }
+
+ /*!
+ * \brief Activates or deactivates the scope guard.
+ *
+ * **Throws:** Nothing.
+ *
+ * \param active The active status to set.
+ *
+ * \post `this->active() == active`
+ */
+ void set_active(bool active) noexcept
+ {
+ m_data.m_active = active;
+ }
+};
+
+#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+template< typename Func >
+explicit scope_exit(Func) -> scope_exit< Func >;
+
+template< typename Func >
+explicit scope_exit(Func, bool) -> scope_exit< Func >;
+
+template< typename Func, typename Cond >
+explicit scope_exit(Func, Cond) -> scope_exit< Func, Cond >;
+
+template< typename Func, typename Cond >
+explicit scope_exit(Func, Cond, bool) -> scope_exit< Func, Cond >;
+#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+
+/*!
+ * \brief Creates a scope guard with a given action function object.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_exit< std::decay_t< F > >(std::forward< F >(func), active)`.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F >
+inline scope_exit< typename std::decay< F >::type > make_scope_exit(F&& func, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_exit< typename std::decay< F >::type >,
+ F,
+ bool
+ >::value)
+{
+ return scope_exit< typename std::decay< F >::type >(static_cast< F&& >(func), active);
+}
+
+/*!
+ * \brief Creates a conditional scope guard with given callable function objects.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_exit< std::decay_t< F >, std::decay_t< C > >(
+ * std::forward< F >(func), std::forward< C >(cond), active)`.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F, typename C >
+inline
+#if !defined(BOOST_SCOPE_DOXYGEN)
+typename std::enable_if<
+ std::is_constructible<
+ scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >,
+ F,
+ C,
+ bool
+ >::value,
+ scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >
+>::type
+#else
+scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >
+#endif
+make_scope_exit(F&& func, C&& cond, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >,
+ F,
+ C,
+ bool
+ >::value)
+{
+ return scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >(static_cast< F&& >(func), static_cast< C&& >(cond), active);
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/scope_fail.hpp b/contrib/restricted/boost/scope/include/boost/scope/scope_fail.hpp
new file mode 100644
index 0000000000..a9c2368c57
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/scope_fail.hpp
@@ -0,0 +1,265 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/scope_fail.hpp
+ *
+ * This header contains definition of \c scope_fail template.
+ */
+
+#ifndef BOOST_SCOPE_SCOPE_FAIL_HPP_INCLUDED_
+#define BOOST_SCOPE_SCOPE_FAIL_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/exception_checker.hpp>
+#include <boost/scope/scope_exit.hpp>
+#include <boost/scope/detail/is_not_like.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/type_traits/is_invocable.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+template< typename Func, typename Cond >
+class scope_fail;
+
+namespace detail {
+
+// Workaround for clang < 5.0 which can't pass scope_fail as a template template parameter from within scope_fail definition
+template< typename T >
+using is_not_like_scope_fail = detail::is_not_like< T, scope_fail >;
+
+} // namespace detail
+
+/*!
+ * \brief Scope exit guard that invokes a function upon leaving the scope, if
+ * a failure condition is satisfied.
+ *
+ * The scope guard wraps two function objects: the scope guard action and
+ * a failure condition for invoking the action. Both function objects must
+ * be callable with no arguments and can be one of:
+ *
+ * \li A user-defined class with a public `operator()`.
+ * \li An lvalue reference to such class.
+ * \li An lvalue reference or pointer to function taking no arguments.
+ *
+ * The condition function object `operator()` must return a value
+ * contextually convertible to \c true, if the failure is detected and the
+ * action function object is allowed to be executed, and \c false otherwise.
+ * Additionally, the failure condition function object `operator()` must not
+ * throw, as otherwise the action function object may not be called. If not
+ * specified, the default failure condition checks whether the scope is left
+ * due to an exception - the action function object will not be called if
+ * the scope is left normally.
+ *
+ * \sa scope_exit
+ * \sa scope_success
+ *
+ * \tparam Func Scope guard action function object type.
+ * \tparam Cond Scope guard failure condition function object type.
+ */
+template< typename Func, typename Cond = exception_checker >
+class scope_fail :
+ public scope_exit< Func, Cond >
+{
+//! \cond
+private:
+ using base_type = scope_exit< Func, Cond >;
+
+//! \endcond
+public:
+ /*!
+ * \brief Constructs a scope guard with a given callable function object.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible.
+ *
+ * **Effects:** Constructs the scope guard as if by calling
+ * `scope_fail(std::forward< F >(func), Cond(), active)`.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< base_type, F, bool >,
+ detail::is_not_like_scope_fail< F >
+ >::value >::type
+ //! \endcond
+ >
+ explicit scope_fail(F&& func, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_constructible< base_type, F, bool >::value)) :
+ base_type(static_cast< F&& >(func), active)
+ {
+ }
+
+ /*!
+ * \brief Constructs a scope guard with a given callable action and failure condition function objects.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is constructible from \a cond.
+ *
+ * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
+ * `std::forward< F >(func)`, otherwise constructs from `func`. If \c Cond is
+ * nothrow constructible from `C&&` then constructs \c Cond from
+ * `std::forward< C >(cond)`, otherwise constructs from `cond`.
+ *
+ * If \c Func or \c Cond construction throws and \a active is \c true, invokes
+ * \a cond and, if it returns \c true, \a func before returning with the exception.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable failure condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F,
+ typename C
+ //! \cond
+ , typename = typename std::enable_if< std::is_constructible< base_type, F, C, bool >::value >::type
+ //! \endcond
+ >
+ explicit scope_fail(F&& func, C&& cond, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_constructible< base_type, F, C, bool >::value)) :
+ base_type(static_cast< F&& >(func), static_cast< C&& >(cond), active)
+ {
+ }
+
+ /*!
+ * \brief Move-constructs a scope guard.
+ *
+ * **Requires:** \c Func and \c Cond are nothrow move-constructible or copy-constructible.
+ *
+ * **Effects:** If \c Func is nothrow move-constructible then move-constructs \c Func from
+ * a member of \a that, otherwise copy-constructs \c Func. If \c Cond is nothrow
+ * move-constructible then move-constructs \c Cond from a member of \a that,
+ * otherwise copy-constructs \c Cond.
+ *
+ * If \c Func or \c Cond construction throws and `that.active() == true`, invokes
+ * \c Cond object stored in \a that and, if it returns \c true, \a Func object
+ * (either the newly constructed one, if its construction succeeded, or the original
+ * one stored in \a that) before returning with the exception.
+ *
+ * If the construction succeeds, marks \a that as inactive.
+ *
+ * **Throws:** Nothing, unless move-construction of the function objects throw.
+ *
+ * \param that Move source.
+ *
+ * \post `that.active() == false`
+ */
+ //! \cond
+ template<
+ bool Requires = std::is_move_constructible< base_type >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ //! \endcond
+ scope_fail(scope_fail&& that)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_constructible< base_type >::value)) :
+ base_type(static_cast< base_type&& >(that))
+ {
+ }
+
+ scope_fail& operator= (scope_fail&&) = delete;
+
+ scope_fail(scope_fail const&) = delete;
+ scope_fail& operator= (scope_fail const&) = delete;
+};
+
+#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+template< typename Func >
+explicit scope_fail(Func) -> scope_fail< Func >;
+
+template< typename Func >
+explicit scope_fail(Func, bool) -> scope_fail< Func >;
+
+template<
+ typename Func,
+ typename Cond,
+ typename = typename std::enable_if< detail::is_invocable< Cond const& >::value >::type
+>
+explicit scope_fail(Func, Cond) -> scope_fail< Func, Cond >;
+
+template<
+ typename Func,
+ typename Cond,
+ typename = typename std::enable_if< detail::is_invocable< Cond const& >::value >::type
+>
+explicit scope_fail(Func, Cond, bool) -> scope_fail< Func, Cond >;
+#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+
+/*!
+ * \brief Creates a scope fail guard with a given action function object.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_fail< std::decay_t< F > >(std::forward< F >(func), active)`.
+ *
+ * \param func The callable function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F >
+inline scope_fail< typename std::decay< F >::type > make_scope_fail(F&& func, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_fail< typename std::decay< F >::type >,
+ F,
+ bool
+ >::value)
+{
+ return scope_fail< typename std::decay< F >::type >(static_cast< F&& >(func), active);
+}
+
+/*!
+ * \brief Creates a scope fail with given callable function objects.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_fail< std::decay_t< F >, std::decay_t< C > >(
+ * std::forward< F >(func), std::forward< C >(cond), active)`.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable failure condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F, typename C >
+inline
+#if !defined(BOOST_SCOPE_DOXYGEN)
+typename std::enable_if<
+ detail::is_invocable< C const& >::value,
+ scope_fail< typename std::decay< F >::type, typename std::decay< C >::type >
+>::type
+#else
+scope_fail< typename std::decay< F >::type, typename std::decay< C >::type >
+#endif
+make_scope_fail(F&& func, C&& cond, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_fail< typename std::decay< F >::type, typename std::decay< C >::type >,
+ F,
+ C,
+ bool
+ >::value)
+{
+ return scope_fail< typename std::decay< F >::type, typename std::decay< C >::type >(static_cast< F&& >(func), static_cast< C&& >(cond), active);
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_SCOPE_FAIL_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/scope_success.hpp b/contrib/restricted/boost/scope/include/boost/scope/scope_success.hpp
new file mode 100644
index 0000000000..a5cd3c160d
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/scope_success.hpp
@@ -0,0 +1,309 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/scope_success.hpp
+ *
+ * This header contains definition of \c scope_success template.
+ */
+
+#ifndef BOOST_SCOPE_SCOPE_SUCCESS_HPP_INCLUDED_
+#define BOOST_SCOPE_SCOPE_SUCCESS_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/exception_checker.hpp>
+#include <boost/scope/scope_exit.hpp>
+#include <boost/scope/detail/is_not_like.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/type_traits/is_invocable.hpp>
+#include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+template< typename Func, typename Cond >
+class scope_success;
+
+namespace detail {
+
+// Workaround for clang < 5.0 which can't pass scope_success as a template template parameter from within scope_success definition
+template< typename T >
+using is_not_like_scope_success = detail::is_not_like< T, scope_success >;
+
+template< typename Func >
+class logical_not;
+
+template< typename T >
+using is_not_like_logical_not = detail::is_not_like< T, logical_not >;
+
+template< typename Func >
+class logical_not
+{
+public:
+ using result_type = bool;
+
+private:
+ Func m_func;
+
+public:
+ template<
+ bool Requires = std::is_default_constructible< Func >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ logical_not() noexcept(std::is_nothrow_default_constructible< Func >::value) :
+ m_func()
+ {
+ }
+
+ template<
+ typename F,
+ typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< Func, F >,
+ detail::is_not_like_logical_not< F >
+ >::value >::type
+ >
+ explicit logical_not(F&& func) noexcept(std::is_nothrow_constructible< Func, F >::value) :
+ m_func(static_cast< F&& >(func))
+ {
+ }
+
+ result_type operator()() const noexcept(detail::is_nothrow_invocable< Func const& >::value)
+ {
+ return !m_func();
+ }
+};
+
+} // namespace detail
+
+/*!
+ * \brief Scope exit guard that invokes a function upon leaving the scope, if
+ * a failure condition is not satisfied.
+ *
+ * The scope guard wraps two function objects: the scope guard action and
+ * a failure condition for invoking the action. Both function objects must
+ * be callable with no arguments and can be one of:
+ *
+ * \li A user-defined class with a public `operator()`.
+ * \li An lvalue reference to such class.
+ * \li An lvalue reference or pointer to function taking no arguments.
+ *
+ * The condition function object `operator()` must return a value
+ * contextually convertible to \c true, if the failure is detected and the
+ * action function object is not allowed to be executed, and \c false otherwise.
+ * Additionally, the failure condition function object `operator()` must not
+ * throw, as otherwise the action function object may not be called. If not
+ * specified, the default failure condition checks whether the scope is left
+ * due to an exception - the action function object will only be called if
+ * the scope is left normally.
+ *
+ * \sa scope_exit
+ * \sa scope_fail
+ *
+ * \tparam Func Scope guard action function object type.
+ * \tparam Cond Scope guard failure condition function object type.
+ */
+template< typename Func, typename Cond = exception_checker >
+class scope_success :
+ public scope_exit< Func, detail::logical_not< Cond > >
+{
+//! \cond
+private:
+ using base_type = scope_exit< Func, detail::logical_not< Cond > >;
+
+//! \endcond
+public:
+ /*!
+ * \brief Constructs a scope guard with a given callable function object.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible.
+ *
+ * **Effects:** Constructs the scope guard as if by calling
+ * `scope_success(std::forward< F >(func), Cond(), active)`.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< base_type, F, bool >,
+ detail::is_not_like_scope_success< F >
+ >::value >::type
+ //! \endcond
+ >
+ explicit scope_success(F&& func, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_constructible< base_type, F, bool >::value)) :
+ base_type(static_cast< F&& >(func), active)
+ {
+ }
+
+ /*!
+ * \brief Constructs a scope guard with a given callable action and failure condition function objects.
+ *
+ * **Requires:** \c Func is constructible from \a func. \c Cond is constructible from \a cond.
+ *
+ * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
+ * `std::forward< F >(func)`, otherwise constructs from `func`. If \c Cond is
+ * nothrow constructible from `C&&` then constructs \c Cond from
+ * `std::forward< C >(cond)`, otherwise constructs from `cond`.
+ *
+ * If \c Func or \c Cond construction throws and \a active is \c true, invokes
+ * \a cond and, if it returns \c true, \a func before returning with the exception.
+ *
+ * **Throws:** Nothing, unless construction of the function objects throw.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable failure condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ *
+ * \post `this->active() == active`
+ */
+ template<
+ typename F,
+ typename C
+ //! \cond
+ , typename = typename std::enable_if< std::is_constructible< base_type, F, C, bool >::value >::type
+ //! \endcond
+ >
+ explicit scope_success(F&& func, C&& cond, bool active = true)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_constructible< base_type, F, C, bool >::value)) :
+ base_type(static_cast< F&& >(func), static_cast< C&& >(cond), active)
+ {
+ }
+
+ /*!
+ * \brief Move-constructs a scope guard.
+ *
+ * **Requires:** \c Func and \c Cond are nothrow move-constructible or copy-constructible.
+ *
+ * **Effects:** If \c Func is nothrow move-constructible then move-constructs \c Func from
+ * a member of \a that, otherwise copy-constructs \c Func. If \c Cond is nothrow
+ * move-constructible then move-constructs \c Cond from a member of \a that,
+ * otherwise copy-constructs \c Cond.
+ *
+ * If \c Func or \c Cond construction throws and `that.active() == true`, invokes
+ * \c Cond object stored in \a that and, if it returns \c true, \a Func object
+ * (either the newly constructed one, if its construction succeeded, or the original
+ * one stored in \a that) before returning with the exception.
+ *
+ * If the construction succeeds, marks \a that as inactive.
+ *
+ * **Throws:** Nothing, unless move-construction of the function objects throw.
+ *
+ * \param that Move source.
+ *
+ * \post `that.active() == false`
+ */
+ //! \cond
+ template<
+ bool Requires = std::is_move_constructible< base_type >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ //! \endcond
+ scope_success(scope_success&& that)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_constructible< base_type >::value)) :
+ base_type(static_cast< base_type&& >(that))
+ {
+ }
+
+ scope_success& operator= (scope_success&&) = delete;
+
+ scope_success(scope_success const&) = delete;
+ scope_success& operator= (scope_success const&) = delete;
+};
+
+#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+template< typename Func >
+explicit scope_success(Func) -> scope_success< Func >;
+
+template< typename Func >
+explicit scope_success(Func, bool) -> scope_success< Func >;
+
+template<
+ typename Func,
+ typename Cond,
+ typename = typename std::enable_if< detail::is_invocable< Cond const& >::value >::type
+>
+explicit scope_success(Func, Cond) -> scope_success< Func, Cond >;
+
+template<
+ typename Func,
+ typename Cond,
+ typename = typename std::enable_if< detail::is_invocable< Cond const& >::value >::type
+>
+explicit scope_success(Func, Cond, bool) -> scope_success< Func, Cond >;
+#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+
+/*!
+ * \brief Creates a scope fail guard with a given action function object.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_success< std::decay_t< F > >(std::forward< F >(func), active)`.
+ *
+ * \param func The callable function object to invoke on destruction.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F >
+inline scope_success< typename std::decay< F >::type > make_scope_success(F&& func, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_success< typename std::decay< F >::type >,
+ F,
+ bool
+ >::value)
+{
+ return scope_success< typename std::decay< F >::type >(static_cast< F&& >(func), active);
+}
+
+/*!
+ * \brief Creates a scope fail with given callable function objects.
+ *
+ * **Effects:** Constructs a scope guard as if by calling
+ * `scope_success< std::decay_t< F >, std::decay_t< C > >(
+ * std::forward< F >(func), std::forward< C >(cond), active)`.
+ *
+ * \param func The callable action function object to invoke on destruction.
+ * \param cond The callable failure condition function object.
+ * \param active Indicates whether the scope guard should be active upon construction.
+ */
+template< typename F, typename C >
+inline
+#if !defined(BOOST_SCOPE_DOXYGEN)
+typename std::enable_if<
+ detail::is_invocable< C const& >::value,
+ scope_success< typename std::decay< F >::type, typename std::decay< C >::type >
+>::type
+#else
+scope_success< typename std::decay< F >::type, typename std::decay< C >::type >
+#endif
+make_scope_success(F&& func, C&& cond, bool active = true)
+ noexcept(std::is_nothrow_constructible<
+ scope_success< typename std::decay< F >::type, typename std::decay< C >::type >,
+ F,
+ C,
+ bool
+ >::value)
+{
+ return scope_success< typename std::decay< F >::type, typename std::decay< C >::type >(static_cast< F&& >(func), static_cast< C&& >(cond), active);
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_SCOPE_SUCCESS_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/unique_fd.hpp b/contrib/restricted/boost/scope/include/boost/scope/unique_fd.hpp
new file mode 100644
index 0000000000..305ad38867
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/unique_fd.hpp
@@ -0,0 +1,38 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2023 Andrey Semashev
+ */
+/*!
+ * \file scope/unique_fd.hpp
+ *
+ * This header contains definition of \c unique_fd type.
+ */
+
+#ifndef BOOST_SCOPE_UNIQUE_FD_HPP_INCLUDED_
+#define BOOST_SCOPE_UNIQUE_FD_HPP_INCLUDED_
+
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/unique_resource.hpp>
+#include <boost/scope/fd_deleter.hpp>
+#include <boost/scope/fd_resource_traits.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+//! Unique POSIX-like file descriptor resource
+using unique_fd = unique_resource< int, fd_deleter, fd_resource_traits >;
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_UNIQUE_FD_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/unique_resource.hpp b/contrib/restricted/boost/scope/include/boost/scope/unique_resource.hpp
new file mode 100644
index 0000000000..81683c4076
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/unique_resource.hpp
@@ -0,0 +1,1642 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022-2024 Andrey Semashev
+ */
+/*!
+ * \file scope/unique_resource.hpp
+ *
+ * This header contains definition of \c unique_resource template.
+ */
+
+#ifndef BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_
+#define BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_
+
+#include <new> // for placement new
+#include <type_traits>
+#include <boost/core/addressof.hpp>
+#include <boost/core/invoke_swap.hpp>
+#include <boost/scope/unique_resource_fwd.hpp>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/compact_storage.hpp>
+#include <boost/scope/detail/move_or_copy_assign_ref.hpp>
+#include <boost/scope/detail/move_or_copy_construct_ref.hpp>
+#include <boost/scope/detail/is_nonnull_default_constructible.hpp>
+#include <boost/scope/detail/type_traits/is_swappable.hpp>
+#include <boost/scope/detail/type_traits/is_nothrow_swappable.hpp>
+#include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
+#include <boost/scope/detail/type_traits/negation.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/type_traits/disjunction.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+#if !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
+
+/*!
+ * \brief Simple resource traits for one or more unallocated resource values.
+ *
+ * This class template generates resource traits for `unique_resource` that specify
+ * one or more unallocated resource values. The first value, specified in the \c DefaultValue
+ * non-type template parameter, is considered the default. The other values, listed in
+ * \c UnallocatedValues, are optional. Any resource values other than \c DefaultValue
+ * or listed in \c UnallocatedValues are considered as allocated.
+ *
+ * In order for the generated resource traits to enable optimized implementation of
+ * `unique_resource`, the resource type must support non-throwing construction and assignment
+ * from, and comparison for (in)equality with \c DefaultValue or any of the resource
+ * values listed in \c UnallocatedValues.
+ */
+template< auto DefaultValue, auto... UnallocatedValues >
+struct unallocated_resource
+{
+ //! Returns the default resource value
+ static decltype(DefaultValue) make_default() noexcept
+ {
+ return DefaultValue;
+ }
+
+ //! Tests if \a res is an allocated resource value
+ template< typename Resource >
+ static bool is_allocated(Resource const& res) noexcept
+ {
+ static_assert(noexcept(res != DefaultValue && (... && (res != UnallocatedValues))),
+ "Invalid unallocated resource value types: comparing resource values with the unallocated values must be noexcept");
+ return res != DefaultValue && (... && (res != UnallocatedValues));
+ }
+};
+
+#endif // !defined(BOOST_NO_CXX17_FOLD_EXPRESSIONS) && !defined(BOOST_NO_CXX17_AUTO_NONTYPE_TEMPLATE_PARAMS)
+
+struct default_resource_t { };
+
+//! Keyword representing default, unallocated resource argument
+BOOST_INLINE_VARIABLE constexpr default_resource_t default_resource = { };
+
+namespace detail {
+
+// The type trait indicates whether \c T is a possibly qualified \c default_resource_t type
+template< typename T >
+struct is_default_resource : public std::false_type { };
+template< >
+struct is_default_resource< default_resource_t > : public std::true_type { };
+template< >
+struct is_default_resource< const default_resource_t > : public std::true_type { };
+template< >
+struct is_default_resource< volatile default_resource_t > : public std::true_type { };
+template< >
+struct is_default_resource< const volatile default_resource_t > : public std::true_type { };
+template< typename T >
+struct is_default_resource< T& > : public is_default_resource< T >::type { };
+
+// Lightweight reference wrapper
+template< typename T >
+class ref_wrapper
+{
+private:
+ T* m_value;
+
+public:
+ explicit
+#if !defined(BOOST_CORE_NO_CONSTEXPR_ADDRESSOF)
+ constexpr
+#endif
+ ref_wrapper(T& value) noexcept :
+ m_value(boost::addressof(value))
+ {
+ }
+
+ ref_wrapper& operator= (T& value) noexcept
+ {
+ m_value = boost::addressof(value);
+ return *this;
+ }
+
+ ref_wrapper(T&&) = delete;
+ ref_wrapper& operator= (T&&) = delete;
+
+ operator T& () const noexcept
+ {
+ return *m_value;
+ }
+
+ template< typename... Args >
+ void operator() (Args&&... args) const noexcept(detail::is_nothrow_invocable< T&, Args&&... >::value)
+ {
+ (*m_value)(static_cast< Args&& >(args)...);
+ }
+};
+
+template< typename T >
+struct wrap_reference
+{
+ using type = T;
+};
+
+template< typename T >
+struct wrap_reference< T& >
+{
+ using type = ref_wrapper< T >;
+};
+
+template< typename Resource, bool UseCompactStorage >
+class resource_holder :
+ public detail::compact_storage< typename wrap_reference< Resource >::type >
+{
+public:
+ using resource_type = Resource;
+ using internal_resource_type = typename wrap_reference< resource_type >::type;
+
+private:
+ using resource_base = detail::compact_storage< internal_resource_type >;
+
+public:
+ template<
+ bool Requires = std::is_default_constructible< internal_resource_type >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) :
+ resource_base()
+ {
+ }
+
+ template<
+ typename R,
+ typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type
+ >
+ explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) :
+ resource_base(static_cast< R&& >(res))
+ {
+ }
+
+ template<
+ typename R,
+ typename D,
+ typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type
+ >
+ explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) :
+ resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type())
+ {
+ }
+
+ resource_type& get() noexcept
+ {
+ return resource_base::get();
+ }
+
+ resource_type const& get() const noexcept
+ {
+ return resource_base::get();
+ }
+
+ internal_resource_type& get_internal() noexcept
+ {
+ return resource_base::get();
+ }
+
+ internal_resource_type const& get_internal() const noexcept
+ {
+ return resource_base::get();
+ }
+
+ void move_from(internal_resource_type&& that) noexcept(std::is_nothrow_move_assignable< internal_resource_type >::value)
+ {
+ resource_base::get() = static_cast< internal_resource_type&& >(that);
+ }
+
+private:
+ template< typename R, typename D >
+ explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept :
+ resource_base(static_cast< R&& >(res))
+ {
+ }
+
+ template< typename R, typename D >
+ explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try :
+ resource_base(res)
+ {
+ }
+ catch (...)
+ {
+ if (allocated)
+ del(res);
+ }
+};
+
+template< typename Resource >
+class resource_holder< Resource, false >
+{
+public:
+ using resource_type = Resource;
+ using internal_resource_type = typename wrap_reference< resource_type >::type;
+
+private:
+ // Note: Not using compact_storage since we will need to reuse storage for this complete object in move_from
+ internal_resource_type m_resource;
+
+public:
+ template<
+ bool Requires = std::is_default_constructible< internal_resource_type >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ constexpr resource_holder() noexcept(std::is_nothrow_default_constructible< internal_resource_type >::value) :
+ m_resource()
+ {
+ }
+
+ template<
+ typename R,
+ typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type
+ >
+ explicit resource_holder(R&& res) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) :
+ m_resource(static_cast< R&& >(res))
+ {
+ }
+
+ template<
+ typename R,
+ typename D,
+ typename = typename std::enable_if< std::is_constructible< internal_resource_type, R >::value >::type
+ >
+ explicit resource_holder(R&& res, D&& del, bool allocated) noexcept(std::is_nothrow_constructible< internal_resource_type, R >::value) :
+ resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated, typename std::is_nothrow_constructible< resource_type, R >::type())
+ {
+ }
+
+ resource_type& get() noexcept
+ {
+ return m_resource;
+ }
+
+ resource_type const& get() const noexcept
+ {
+ return m_resource;
+ }
+
+ internal_resource_type& get_internal() noexcept
+ {
+ return m_resource;
+ }
+
+ internal_resource_type const& get_internal() const noexcept
+ {
+ return m_resource;
+ }
+
+ void move_from(internal_resource_type&& that)
+ noexcept(std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::value)
+ {
+ internal_resource_type* p = boost::addressof(m_resource);
+ p->~internal_resource_type();
+ new (p) internal_resource_type(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that));
+ }
+
+private:
+ template< typename R, typename D >
+ explicit resource_holder(R&& res, D&& del, bool allocated, std::true_type) noexcept :
+ m_resource(static_cast< R&& >(res))
+ {
+ }
+
+ template< typename R, typename D >
+ explicit resource_holder(R&& res, D&& del, bool allocated, std::false_type) try :
+ m_resource(res)
+ {
+ }
+ catch (...)
+ {
+ if (allocated)
+ del(res);
+ }
+};
+
+template< typename Resource, typename Deleter >
+class deleter_holder :
+ public detail::compact_storage< typename wrap_reference< Deleter >::type >
+{
+public:
+ using resource_type = Resource;
+ using deleter_type = Deleter;
+ using internal_deleter_type = typename wrap_reference< deleter_type >::type;
+
+private:
+ using deleter_base = detail::compact_storage< internal_deleter_type >;
+
+public:
+ template<
+ bool Requires = detail::is_nonnull_default_constructible< internal_deleter_type >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ constexpr deleter_holder() noexcept(detail::is_nothrow_nonnull_default_constructible< internal_deleter_type >::value) :
+ deleter_base()
+ {
+ }
+
+ template<
+ typename D,
+ typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type
+ >
+ explicit deleter_holder(D&& del) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) :
+ deleter_base(static_cast< D&& >(del))
+ {
+ }
+
+ template<
+ typename D,
+ typename = typename std::enable_if< std::is_constructible< internal_deleter_type, D >::value >::type
+ >
+ explicit deleter_holder(D&& del, resource_type& res, bool allocated) noexcept(std::is_nothrow_constructible< internal_deleter_type, D >::value) :
+ deleter_holder(static_cast< D&& >(del), res, allocated, typename std::is_nothrow_constructible< internal_deleter_type, D >::type())
+ {
+ }
+
+ deleter_type& get() noexcept
+ {
+ return deleter_base::get();
+ }
+
+ deleter_type const& get() const noexcept
+ {
+ return deleter_base::get();
+ }
+
+ internal_deleter_type& get_internal() noexcept
+ {
+ return deleter_base::get();
+ }
+
+ internal_deleter_type const& get_internal() const noexcept
+ {
+ return deleter_base::get();
+ }
+
+private:
+ template< typename D >
+ explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::true_type) noexcept :
+ deleter_base(static_cast< D&& >(del))
+ {
+ }
+
+ template< typename D >
+ explicit deleter_holder(D&& del, resource_type& res, bool allocated, std::false_type) try :
+ deleter_base(del)
+ {
+ }
+ catch (...)
+ {
+ if (BOOST_LIKELY(allocated))
+ del(res);
+ }
+};
+
+/*
+ * This metafunction indicates whether \c resource_holder should use \c compact_storage
+ * to optimize storage for the resource object. Its definition must be coherent with
+ * `resource_holder::move_from` definition and move constructor implementation in
+ * \c unique_resource_data.
+ *
+ * There is one tricky case with \c unique_resource move constructor, when the resource move
+ * constructor is noexcept and deleter's move and copy constructors are not. It is possible
+ * that \c unique_resource_data move constructor moves the resource into the object being
+ * constructed but fails to construct the deleter. In this case we want to move the resource
+ * back to the original \c unique_resource_data object (which is guaranteed to not throw since
+ * the resource's move constructor is non-throwing).
+ *
+ * However, if we use the move constructor to move the resource back, we need to use placement
+ * new, and this only lets us create a complete object of the resource type, which prohibits
+ * the use of \c compact_storage, as it may create the resource object as a base subobject of
+ * \c compact_storage. Using placement new on a base subobject may corrupt data that is placed
+ * in the trailing padding bits of the resource type.
+ *
+ * To work around this limitation, we also test if move assignment of the resource type is
+ * also non-throwing (which is reasonable to expect, given that the move constructor is
+ * non-throwing). If it is, we can avoid having to destroy and move-construct the resource and
+ * use move-assignment instead. This doesn't require a complete object of the resource type
+ * and allows us to use \c compact_storage. If move assignment is not noexcept then we have
+ * to use the move constructor and disable the \c compact_storage optimization.
+ *
+ * So this trait has to detect (a) whether we are affected by this tricky case of the
+ * \c unique_resource move constructor in the first place and (b) whether we can use move
+ * assignment to move the resource back to the original \c unique_resource object. If we're
+ * not affected or we can use move assignment then we enable \c compact_storage.
+ */
+template< typename Resource, typename Deleter >
+using use_resource_compact_storage = detail::disjunction<
+ std::is_nothrow_move_assignable< typename wrap_reference< Resource >::type >,
+ std::is_nothrow_constructible< typename wrap_reference< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter >::type >,
+ detail::negation< std::is_nothrow_constructible< typename wrap_reference< Resource >::type, typename detail::move_or_copy_construct_ref< Resource >::type > >
+>;
+
+template< typename Resource, typename Deleter, typename Traits >
+class unique_resource_data :
+ public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >,
+ public detail::deleter_holder< Resource, Deleter >
+{
+public:
+ using resource_type = Resource;
+ using deleter_type = Deleter;
+ using traits_type = Traits;
+
+private:
+ using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >;
+ using deleter_holder = detail::deleter_holder< resource_type, deleter_type >;
+ using result_of_make_default = decltype(traits_type::make_default());
+
+public:
+ using internal_resource_type = typename resource_holder::internal_resource_type;
+ using internal_deleter_type = typename deleter_holder::internal_deleter_type;
+
+ static_assert(noexcept(traits_type::make_default()), "Invalid unique_resource resource traits: make_default must be noexcept");
+ static_assert(std::is_nothrow_assignable< internal_resource_type&, result_of_make_default >::value,
+ "Invalid unique_resource resource traits: resource must be nothrow-assignable from the result of make_default");
+ static_assert(noexcept(traits_type::is_allocated(std::declval< resource_type const& >())), "Invalid unique_resource resource traits: is_allocated must be noexcept");
+
+public:
+ template<
+ bool Requires = detail::conjunction<
+ std::is_constructible< resource_holder, result_of_make_default >,
+ std::is_default_constructible< deleter_holder >
+ >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ constexpr unique_resource_data()
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< resource_holder, result_of_make_default >,
+ std::is_nothrow_default_constructible< deleter_holder >
+ >::value) :
+ resource_holder(traits_type::make_default()),
+ deleter_holder()
+ {
+ }
+
+ unique_resource_data(unique_resource_data const&) = delete;
+ unique_resource_data& operator= (unique_resource_data const&) = delete;
+
+ unique_resource_data(unique_resource_data&& that)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >,
+ std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >
+ >::value) :
+ unique_resource_data
+ (
+ static_cast< unique_resource_data&& >(that),
+ typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(),
+ typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type()
+ )
+ {
+ }
+
+ template<
+ typename D,
+ typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< resource_holder, result_of_make_default >,
+ std::is_constructible< deleter_holder, D >
+ >::value >::type
+ >
+ explicit unique_resource_data(default_resource_t, D&& del)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< resource_holder, result_of_make_default >,
+ std::is_nothrow_constructible< deleter_holder, D >
+ >::value) :
+ resource_holder(traits_type::make_default()),
+ deleter_holder(static_cast< D&& >(del))
+ {
+ }
+
+ template<
+ typename R,
+ typename D,
+ typename = typename std::enable_if< detail::conjunction<
+ detail::negation< detail::is_default_resource< R > >,
+ std::is_constructible< resource_holder, R, D, bool >,
+ std::is_constructible< deleter_holder, D, resource_type&, bool >
+ >::value >::type
+ >
+ explicit unique_resource_data(R&& res, D&& del)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< resource_holder, R, D, bool >,
+ std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool >
+ >::value) :
+ unique_resource_data(static_cast< R&& >(res), static_cast< D&& >(del), traits_type::is_allocated(res)) // don't forward res to is_allocated to make sure res is not moved-from on resource construction
+ {
+ // Since res may not be of the resource type, the is_allocated call made above may require a type conversion or pick a different overload.
+ // We still require it to be noexcept, as we need to know whether we should deallocate it. Otherwise we may leak the resource.
+ static_assert(noexcept(traits_type::is_allocated(res)), "Invalid unique_resource resource traits: is_allocated must be noexcept");
+ }
+
+ template<
+ bool Requires = detail::conjunction<
+ std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >,
+ std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type >
+ >::value
+ >
+ typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that)
+ noexcept(detail::conjunction<
+ std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >,
+ std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type >
+ >::value)
+ {
+ assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type());
+ return *this;
+ }
+
+ resource_type& get_resource() noexcept
+ {
+ return resource_holder::get();
+ }
+
+ resource_type const& get_resource() const noexcept
+ {
+ return resource_holder::get();
+ }
+
+ internal_resource_type& get_internal_resource() noexcept
+ {
+ return resource_holder::get_internal();
+ }
+
+ internal_resource_type const& get_internal_resource() const noexcept
+ {
+ return resource_holder::get_internal();
+ }
+
+ deleter_type& get_deleter() noexcept
+ {
+ return deleter_holder::get();
+ }
+
+ deleter_type const& get_deleter() const noexcept
+ {
+ return deleter_holder::get();
+ }
+
+ internal_deleter_type& get_internal_deleter() noexcept
+ {
+ return deleter_holder::get_internal();
+ }
+
+ internal_deleter_type const& get_internal_deleter() const noexcept
+ {
+ return deleter_holder::get_internal();
+ }
+
+ bool is_allocated() const noexcept
+ {
+ return traits_type::is_allocated(get_resource());
+ }
+
+ void set_unallocated() noexcept
+ {
+ get_internal_resource() = traits_type::make_default();
+ }
+
+ template< typename R >
+ void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value)
+ {
+ get_internal_resource() = static_cast< R&& >(res);
+ }
+
+ template<
+ bool Requires = detail::conjunction<
+ detail::is_swappable< internal_resource_type >,
+ detail::is_swappable< internal_deleter_type >,
+ detail::disjunction<
+ detail::is_nothrow_swappable< internal_resource_type >,
+ detail::is_nothrow_swappable< internal_deleter_type >
+ >
+ >::value
+ >
+ typename std::enable_if< Requires >::type swap(unique_resource_data& that)
+ noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value)
+ {
+ swap_impl
+ (
+ that,
+ std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(),
+ std::integral_constant< bool, detail::conjunction<
+ detail::is_nothrow_swappable< internal_resource_type >,
+ detail::is_nothrow_swappable< internal_deleter_type >
+ >::value >()
+ );
+ }
+
+private:
+ unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept :
+ resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())),
+ deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter()))
+ {
+ that.set_unallocated();
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) :
+ resource_holder(static_cast< resource_type const& >(that.get_resource())),
+ deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter()))
+ {
+ that.set_unallocated();
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try :
+ resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())),
+ deleter_holder(static_cast< deleter_type const& >(that.get_deleter()))
+ {
+ that.set_unallocated();
+ }
+ catch (...)
+ {
+ // Since only the deleter's constructor could have thrown an exception here, move the resource back
+ // to the original unique_resource. This is guaranteed to not throw.
+ that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal()));
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) :
+ resource_holder(static_cast< resource_type const& >(that.get_resource())),
+ deleter_holder(static_cast< deleter_type const& >(that.get_deleter()))
+ {
+ that.set_unallocated();
+ }
+
+ template<
+ typename R,
+ typename D,
+ typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< resource_holder, R, D, bool >,
+ std::is_constructible< deleter_holder, D, resource_type&, bool >
+ >::value >::type
+ >
+ explicit unique_resource_data(R&& res, D&& del, bool allocated)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< resource_holder, R, D, bool >,
+ std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool >
+ >::value) :
+ resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), allocated),
+ deleter_holder(static_cast< D&& >(del), resource_holder::get(), allocated)
+ {
+ }
+
+ void assign(unique_resource_data&& that, std::true_type)
+ noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value)
+ {
+ get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource());
+ get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter());
+
+ that.set_unallocated();
+ }
+
+ void assign(unique_resource_data&& that, std::false_type)
+ {
+ get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter());
+ get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource());
+
+ that.set_unallocated();
+ }
+
+ void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept
+ {
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ }
+
+ void swap_impl(unique_resource_data& that, std::true_type, std::false_type)
+ {
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ }
+
+ void swap_impl(unique_resource_data& that, std::false_type, std::false_type)
+ {
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ }
+};
+
+template< typename Resource, typename Deleter >
+class unique_resource_data< Resource, Deleter, void > :
+ public detail::resource_holder< Resource, use_resource_compact_storage< Resource, Deleter >::value >,
+ public detail::deleter_holder< Resource, Deleter >
+{
+public:
+ using resource_type = Resource;
+ using deleter_type = Deleter;
+ using traits_type = void;
+
+private:
+ using resource_holder = detail::resource_holder< resource_type, use_resource_compact_storage< resource_type, deleter_type >::value >;
+ using deleter_holder = detail::deleter_holder< resource_type, deleter_type >;
+
+public:
+ using internal_resource_type = typename resource_holder::internal_resource_type;
+ using internal_deleter_type = typename deleter_holder::internal_deleter_type;
+
+private:
+ bool m_allocated;
+
+public:
+ template<
+ bool Requires = detail::conjunction< std::is_default_constructible< resource_holder >, std::is_default_constructible< deleter_holder > >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ constexpr unique_resource_data()
+ noexcept(detail::conjunction< std::is_nothrow_default_constructible< resource_holder >, std::is_nothrow_default_constructible< deleter_holder > >::value) :
+ resource_holder(),
+ deleter_holder(),
+ m_allocated(false)
+ {
+ }
+
+ unique_resource_data(unique_resource_data const&) = delete;
+ unique_resource_data& operator= (unique_resource_data const&) = delete;
+
+ template<
+ bool Requires = detail::conjunction<
+ std::is_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >,
+ std::is_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >
+ >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ unique_resource_data(unique_resource_data&& that)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >,
+ std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >
+ >::value) :
+ unique_resource_data
+ (
+ static_cast< unique_resource_data&& >(that),
+ typename std::is_nothrow_constructible< internal_resource_type, typename detail::move_or_copy_construct_ref< resource_type >::type >::type(),
+ typename std::is_nothrow_constructible< internal_deleter_type, typename detail::move_or_copy_construct_ref< deleter_type >::type >::type()
+ )
+ {
+ }
+
+ template<
+ typename D,
+ typename = typename std::enable_if< detail::conjunction<
+ std::is_default_constructible< resource_holder >,
+ std::is_constructible< deleter_holder, D >
+ >::value >::type
+ >
+ explicit unique_resource_data(default_resource_t, D&& del)
+ noexcept(detail::conjunction<
+ std::is_nothrow_default_constructible< resource_holder >,
+ std::is_nothrow_constructible< deleter_holder, D >
+ >::value) :
+ resource_holder(),
+ deleter_holder(static_cast< D&& >(del)),
+ m_allocated(false)
+ {
+ }
+
+ template<
+ typename R,
+ typename D,
+ typename = typename std::enable_if< detail::conjunction<
+ detail::negation< detail::is_default_resource< R > >,
+ std::is_constructible< resource_holder, R, D, bool >,
+ std::is_constructible< deleter_holder, D, resource_type&, bool >
+ >::value >::type
+ >
+ explicit unique_resource_data(R&& res, D&& del)
+ noexcept(detail::conjunction<
+ std::is_nothrow_constructible< resource_holder, R, D, bool >,
+ std::is_nothrow_constructible< deleter_holder, D, resource_type&, bool >
+ >::value) :
+ resource_holder(static_cast< R&& >(res), static_cast< D&& >(del), true),
+ deleter_holder(static_cast< D&& >(del), resource_holder::get(), true),
+ m_allocated(true)
+ {
+ }
+
+ template<
+ bool Requires = detail::conjunction<
+ std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >,
+ std::is_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type >
+ >::value
+ >
+ typename std::enable_if< Requires, unique_resource_data& >::type operator= (unique_resource_data&& that)
+ noexcept(detail::conjunction<
+ std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >,
+ std::is_nothrow_assignable< internal_deleter_type&, typename detail::move_or_copy_assign_ref< deleter_type >::type >
+ >::value)
+ {
+ assign(static_cast< unique_resource_data&& >(that), typename std::is_nothrow_move_assignable< internal_deleter_type >::type());
+ return *this;
+ }
+
+ resource_type& get_resource() noexcept
+ {
+ return resource_holder::get();
+ }
+
+ resource_type const& get_resource() const noexcept
+ {
+ return resource_holder::get();
+ }
+
+ internal_resource_type& get_internal_resource() noexcept
+ {
+ return resource_holder::get_internal();
+ }
+
+ internal_resource_type const& get_internal_resource() const noexcept
+ {
+ return resource_holder::get_internal();
+ }
+
+ deleter_type& get_deleter() noexcept
+ {
+ return deleter_holder::get();
+ }
+
+ deleter_type const& get_deleter() const noexcept
+ {
+ return deleter_holder::get();
+ }
+
+ internal_deleter_type& get_internal_deleter() noexcept
+ {
+ return deleter_holder::get_internal();
+ }
+
+ internal_deleter_type const& get_internal_deleter() const noexcept
+ {
+ return deleter_holder::get_internal();
+ }
+
+ bool is_allocated() const noexcept
+ {
+ return m_allocated;
+ }
+
+ void set_unallocated() noexcept
+ {
+ m_allocated = false;
+ }
+
+ template< typename R >
+ void assign_resource(R&& res) noexcept(std::is_nothrow_assignable< internal_resource_type&, R >::value)
+ {
+ get_internal_resource() = static_cast< R&& >(res);
+ m_allocated = true;
+ }
+
+ template<
+ bool Requires = detail::conjunction<
+ detail::is_swappable< internal_resource_type >,
+ detail::is_swappable< internal_deleter_type >,
+ detail::disjunction<
+ detail::is_nothrow_swappable< internal_resource_type >,
+ detail::is_nothrow_swappable< internal_deleter_type >
+ >
+ >::value
+ >
+ typename std::enable_if< Requires >::type swap(unique_resource_data& that)
+ noexcept(detail::conjunction< detail::is_nothrow_swappable< internal_resource_type >, detail::is_nothrow_swappable< internal_deleter_type > >::value)
+ {
+ swap_impl
+ (
+ that,
+ std::integral_constant< bool, detail::is_nothrow_swappable< internal_resource_type >::value >(),
+ std::integral_constant< bool, detail::conjunction<
+ detail::is_nothrow_swappable< internal_resource_type >,
+ detail::is_nothrow_swappable< internal_deleter_type >
+ >::value >()
+ );
+ }
+
+private:
+ unique_resource_data(unique_resource_data&& that, std::true_type, std::true_type) noexcept :
+ resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())),
+ deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())),
+ m_allocated(that.m_allocated)
+ {
+ that.m_allocated = false;
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::false_type, std::true_type) :
+ resource_holder(static_cast< resource_type const& >(that.get_resource())),
+ deleter_holder(static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(that.get_deleter())),
+ m_allocated(that.m_allocated)
+ {
+ that.m_allocated = false;
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::true_type, std::false_type) try :
+ resource_holder(static_cast< typename detail::move_or_copy_construct_ref< resource_type >::type >(that.get_resource())),
+ deleter_holder(static_cast< deleter_type const& >(that.get_deleter())),
+ m_allocated(that.m_allocated)
+ {
+ that.m_allocated = false;
+ }
+ catch (...)
+ {
+ // Since only the deleter's constructor could have thrown an exception here, move the resource back
+ // to the original unique_resource. This is guaranteed to not throw.
+ that.resource_holder::move_from(static_cast< internal_resource_type&& >(resource_holder::get_internal()));
+ }
+
+ unique_resource_data(unique_resource_data&& that, std::false_type, std::false_type) :
+ resource_holder(static_cast< resource_type const& >(that.get_resource())),
+ deleter_holder(static_cast< deleter_type const& >(that.get_deleter())),
+ m_allocated(that.m_allocated)
+ {
+ that.m_allocated = false;
+ }
+
+ void assign(unique_resource_data&& that, std::true_type)
+ noexcept(std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< resource_type >::type >::value)
+ {
+ get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource());
+ get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter());
+
+ m_allocated = that.m_allocated;
+ that.m_allocated = false;
+ }
+
+ void assign(unique_resource_data&& that, std::false_type)
+ {
+ get_internal_deleter() = static_cast< typename detail::move_or_copy_assign_ref< deleter_type >::type >(that.get_deleter());
+ get_internal_resource() = static_cast< typename detail::move_or_copy_assign_ref< resource_type >::type >(that.get_resource());
+
+ m_allocated = that.m_allocated;
+ that.m_allocated = false;
+ }
+
+ void swap_impl(unique_resource_data& that, std::true_type, std::true_type) noexcept
+ {
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ boost::core::invoke_swap(m_allocated, that.m_allocated);
+ }
+
+ void swap_impl(unique_resource_data& that, std::true_type, std::false_type)
+ {
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ boost::core::invoke_swap(m_allocated, that.m_allocated);
+ }
+
+ void swap_impl(unique_resource_data& that, std::false_type, std::false_type)
+ {
+ boost::core::invoke_swap(get_internal_resource(), that.get_internal_resource());
+ boost::core::invoke_swap(get_internal_deleter(), that.get_internal_deleter());
+ boost::core::invoke_swap(m_allocated, that.m_allocated);
+ }
+};
+
+template< typename T >
+struct is_dereferenceable_impl
+{
+ template< typename U, typename R = decltype(*std::declval< U const& >()) >
+ static std::true_type _is_dereferenceable_check(int);
+ template< typename U >
+ static std::false_type _is_dereferenceable_check(...);
+
+ using type = decltype(is_dereferenceable_impl::_is_dereferenceable_check< T >(0));
+};
+
+template< typename T >
+struct is_dereferenceable : public is_dereferenceable_impl< T >::type { };
+template< >
+struct is_dereferenceable< void* > : public std::false_type { };
+template< >
+struct is_dereferenceable< const void* > : public std::false_type { };
+template< >
+struct is_dereferenceable< volatile void* > : public std::false_type { };
+template< >
+struct is_dereferenceable< const volatile void* > : public std::false_type { };
+template< >
+struct is_dereferenceable< void*& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const void*& > : public std::false_type { };
+template< >
+struct is_dereferenceable< volatile void*& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const volatile void*& > : public std::false_type { };
+template< >
+struct is_dereferenceable< void* const& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const void* const& > : public std::false_type { };
+template< >
+struct is_dereferenceable< volatile void* const& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const volatile void* const& > : public std::false_type { };
+template< >
+struct is_dereferenceable< void* volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const void* volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< volatile void* volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const volatile void* volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< void* const volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const void* const volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< volatile void* const volatile& > : public std::false_type { };
+template< >
+struct is_dereferenceable< const volatile void* const volatile& > : public std::false_type { };
+
+template< typename T, bool = detail::is_dereferenceable< T >::value >
+struct dereference_traits { };
+template< typename T >
+struct dereference_traits< T, true >
+{
+ using result_type = decltype(*std::declval< T const& >());
+ static constexpr bool is_noexcept = noexcept(*std::declval< T const& >());
+};
+
+} // namespace detail
+
+/*!
+ * \brief RAII wrapper for automatically reclaiming arbitrary resources.
+ *
+ * A \c unique_resource object exclusively owns wrapped resource and invokes
+ * the deleter function object on it on destruction. The wrapped resource can have
+ * any type that is:
+ *
+ * \li Move-constructible, where the move constructor is marked as `noexcept`, or
+ * \li Copy-constructible, or
+ * \li An lvalue reference to an object type.
+ *
+ * The deleter must be a function object type that is callable on an lvalue
+ * of the resource type. The deleter must be copy-constructible.
+ *
+ * An optional resource traits template parameter may be specified. Resource
+ * traits can be used to optimize \c unique_resource implementation when
+ * the following conditions are met:
+ *
+ * \li There is at least one value of the resource type that is considered
+ * unallocated (that is, no allocated resource shall be equal to one of
+ * the unallocated resource values). The unallocated resource values need not
+ * be deallocated using the deleter.
+ * \li One of the unallocated resource values can be considered the default.
+ * Constructing the default resource value and assigning it to a resource
+ * object (whether allocated or not) shall not throw exceptions.
+ * \li Resource objects can be tested for being unallocated. Such a test shall
+ * not throw exceptions.
+ *
+ * If specified, the resource traits must be a class type that has the following
+ * public static members:
+ *
+ * \li `R make_default() noexcept` - must return the default resource value such
+ * that `std::is_constructible< Resource, R >::value &&
+ * std::is_nothrow_assignable< Resource&, R >::value` is \c true.
+ * \li `bool is_allocated(Resource const& res) noexcept` - must return \c true
+ * if \c res is not one of the unallocated resource values and \c false
+ * otherwise.
+ *
+ * Note that `is_allocated(make_default())` must always return \c false.
+ *
+ * When resource traits satisfying the above requirements are specified,
+ * \c unique_resource will be able to avoid storing additional indication of
+ * whether the owned resource object needs to be deallocated with the deleter
+ * on destruction. It will use the default resource value to initialize the owned
+ * resource object when \c unique_resource is not in the allocated state.
+ * Additionally, it will be possible to construct \c unique_resource with
+ * unallocated resource values, which will create \c unique_resource objects in
+ * unallocated state (the deleter will not be called on unallocated resource
+ * values).
+ *
+ * \tparam Resource Resource type.
+ * \tparam Deleter Resource deleter function object type.
+ * \tparam Traits Optional resource traits type.
+ */
+template< typename Resource, typename Deleter, typename Traits BOOST_SCOPE_DETAIL_DOC(= void) >
+class unique_resource
+{
+public:
+ //! Resource type
+ using resource_type = Resource;
+ //! Deleter type
+ using deleter_type = Deleter;
+ //! Resource traits
+ using traits_type = Traits;
+
+//! \cond
+private:
+ using data = detail::unique_resource_data< resource_type, deleter_type, traits_type >;
+ using internal_resource_type = typename data::internal_resource_type;
+ using internal_deleter_type = typename data::internal_deleter_type;
+
+ data m_data;
+
+//! \endcond
+public:
+ /*!
+ * \brief Constructs an unallocated unique resource guard.
+ *
+ * **Requires:** Default \c Resource value can be constructed. \c Deleter is default-constructible
+ * and is not a pointer to function.
+ *
+ * **Effects:** Initializes the \c Resource object with the default resource value. Default-constructs
+ * the \c Deleter object.
+ *
+ * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws.
+ *
+ * \post `this->allocated() == false`
+ */
+ //! \cond
+ template<
+ bool Requires = std::is_default_constructible< data >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ //! \endcond
+ constexpr unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_default_constructible< data >::value))
+ {
+ }
+
+ /*!
+ * \brief Constructs an unallocated unique resource guard with the given deleter.
+ *
+ * **Requires:** Default \c Resource value can be constructed and \c Deleter is constructible from \a del.
+ *
+ * **Effects:** Initializes the \c Resource value with the default resource value. If \c Deleter is nothrow
+ * constructible from `D&&` then constructs \c Deleter from `std::forward< D >(del)`,
+ * otherwise constructs from `del`.
+ *
+ * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws.
+ *
+ * \param res A tag argument indicating default resource value.
+ * \param del Resource deleter function object.
+ *
+ * \post `this->allocated() == false`
+ */
+ template<
+ typename D
+ //! \cond
+ , typename = typename std::enable_if<
+ std::is_constructible< data, default_resource_t, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >::value
+ >::type
+ //! \endcond
+ >
+ unique_resource(default_resource_t res, D&& del)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ default_resource_t,
+ typename detail::move_or_copy_construct_ref< D, deleter_type >::type
+ >::value
+ )) :
+ m_data
+ (
+ res,
+ static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del)
+ )
+ {
+ }
+
+ /*!
+ * \brief Constructs a unique resource guard with the given resource and a default-constructed deleter.
+ *
+ * **Requires:** \c Resource is constructible from \a res. \c Deleter is default-constructible and
+ * is not a pointer to function.
+ *
+ * **Effects:** Constructs the unique resource object as if by calling
+ * `unique_resource(std::forward< R >(res), Deleter())`.
+ *
+ * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws.
+ *
+ * \param res Resource object.
+ */
+ template<
+ typename R
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ detail::is_nothrow_nonnull_default_constructible< deleter_type >,
+ std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< deleter_type >::type >,
+ detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue
+ >::value >::type
+ //! \endcond
+ >
+ explicit unique_resource(R&& res)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< R, resource_type >::type,
+ typename detail::move_or_copy_construct_ref< deleter_type >::type
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res),
+ static_cast< typename detail::move_or_copy_construct_ref< deleter_type >::type >(deleter_type())
+ )
+ {
+ }
+
+ /*!
+ * \brief Constructs a unique resource guard with the given resource and deleter.
+ *
+ * **Requires:** \c Resource is constructible from \a res and \c Deleter is constructible from \a del.
+ *
+ * **Effects:** If \c Resource is nothrow constructible from `R&&` then constructs \c Resource
+ * from `std::forward< R >(res)`, otherwise constructs from `res`. If \c Deleter
+ * is nothrow constructible from `D&&` then constructs \c Deleter from
+ * `std::forward< D >(del)`, otherwise constructs from `del`.
+ *
+ * If construction of \c Resource or \c Deleter throws and \a res is not an unallocated resource
+ * value, invokes \a del on \a res (if \c Resource construction failed) or the constructed
+ * \c Resource object (if \c Deleter construction failed).
+ *
+ * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws.
+ *
+ * \param res Resource object.
+ * \param del Resource deleter function object.
+ *
+ * \post If \a res is an unallocated resource value then `this->allocated() == false`, otherwise
+ * `this->allocated() == true`.
+ */
+ template<
+ typename R,
+ typename D
+ //! \cond
+ , typename = typename std::enable_if< detail::conjunction<
+ std::is_constructible< data, typename detail::move_or_copy_construct_ref< R, resource_type >::type, typename detail::move_or_copy_construct_ref< D, deleter_type >::type >,
+ detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue
+ >::value >::type
+ //! \endcond
+ >
+ unique_resource(R&& res, D&& del)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ std::is_nothrow_constructible<
+ data,
+ typename detail::move_or_copy_construct_ref< R, resource_type >::type,
+ typename detail::move_or_copy_construct_ref< D, deleter_type >::type
+ >::value
+ )) :
+ m_data
+ (
+ static_cast< typename detail::move_or_copy_construct_ref< R, resource_type >::type >(res),
+ static_cast< typename detail::move_or_copy_construct_ref< D, deleter_type >::type >(del)
+ )
+ {
+ }
+
+ unique_resource(unique_resource const&) = delete;
+ unique_resource& operator= (unique_resource const&) = delete;
+
+ /*!
+ * \brief Move-constructs a unique resource guard.
+ *
+ * **Requires:** \c Resource and \c Deleter are move-constructible.
+ *
+ * **Effects:** If \c Resource is nothrow move-constructible then move-constructs \c Resource,
+ * otherwise copy-constructs. If \c Deleter is nothrow move-constructible then move-constructs
+ * \c Deleter, otherwise copy-constructs. Deactivates the moved-from unique resource object.
+ *
+ * If an exception is thrown during construction, \a that is left in its original state.
+ *
+ * \note This logic ensures that in case of exception the resource is not leaked and remains owned by the
+ * move source.
+ *
+ * **Throws:** Nothing, unless construction of \c Resource or \c Deleter throws.
+ *
+ * \param that Move source.
+ *
+ * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then
+ * `this->allocated() == allocated` and `that.allocated() == false`.
+ */
+ //! \cond
+ template<
+ bool Requires = std::is_move_constructible< data >::value,
+ typename = typename std::enable_if< Requires >::type
+ >
+ //! \endcond
+ unique_resource(unique_resource&& that) noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_constructible< data >::value)) :
+ m_data(static_cast< data&& >(that.m_data))
+ {
+ }
+
+ /*!
+ * \brief Move-assigns a unique resource guard.
+ *
+ * **Requires:** \c Resource and \c Deleter are move-assignable.
+ *
+ * **Effects:** Calls `this->reset()`. Then, if \c Deleter is nothrow move-assignable, move-assigns
+ * the \c Deleter object first and the \c Resource object next. Otherwise, move-assigns
+ * the objects in reverse order. Lastly, deactivates the moved-from unique resource object.
+ *
+ * If an exception is thrown, \a that is left in its original state.
+ *
+ * \note The different orders of assignment ensure that in case of exception the resource is not leaked
+ * and remains owned by the move source.
+ *
+ * **Throws:** Nothing, unless assignment of \c Resource or \c Deleter throws.
+ *
+ * \param that Move source.
+ *
+ * \post Let \c allocated be equal to `that.allocated()` prior to the operation. Then
+ * `this->allocated() == allocated` and `that.allocated() == false`.
+ */
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ template< bool Requires = std::is_move_assignable< data >::value >
+ typename std::enable_if< Requires, unique_resource& >::type
+#else
+ unique_resource&
+#endif
+ operator= (unique_resource&& that)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(std::is_nothrow_move_assignable< data >::value))
+ {
+ reset();
+ m_data = static_cast< data&& >(that.m_data);
+ return *this;
+ }
+
+ /*!
+ * \brief If the resource is allocated, calls the deleter function on it. Destroys the resource and the deleter.
+ *
+ * **Throws:** Nothing, unless invoking the deleter throws.
+ */
+ ~unique_resource() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value))
+ {
+ if (BOOST_LIKELY(m_data.is_allocated()))
+ m_data.get_deleter()(m_data.get_resource());
+ }
+
+ /*!
+ * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false.
+ *
+ * \note This method does not test the value of the resource.
+ *
+ * **Throws:** Nothing.
+ */
+ explicit operator bool () const noexcept
+ {
+ return m_data.is_allocated();
+ }
+
+ /*!
+ * \brief Returns \c true if the resource is allocated and to be reclaimed by the deleter, otherwise \c false.
+ *
+ * **Throws:** Nothing.
+ */
+ bool allocated() const noexcept
+ {
+ return m_data.is_allocated();
+ }
+
+ /*!
+ * \brief Returns a reference to the resource object.
+ *
+ * **Throws:** Nothing.
+ */
+ resource_type const& get() const noexcept
+ {
+ return m_data.get_resource();
+ }
+
+ /*!
+ * \brief Returns a reference to the deleter object.
+ *
+ * **Throws:** Nothing.
+ */
+ deleter_type const& get_deleter() const noexcept
+ {
+ return m_data.get_deleter();
+ }
+
+ /*!
+ * \brief Marks the resource as unallocated. Does not call the deleter if the resource was previously allocated.
+ *
+ * **Throws:** Nothing.
+ *
+ * \post `this->allocated() == false`
+ */
+ void release() noexcept
+ {
+ m_data.set_unallocated();
+ }
+
+ /*!
+ * \brief If the resource is allocated, calls the deleter function on it and marks the resource as unallocated.
+ *
+ * **Throws:** Nothing, unless invoking the deleter throws.
+ *
+ * \post `this->allocated() == false`
+ */
+ void reset() noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_invocable< deleter_type&, resource_type& >::value))
+ {
+ if (BOOST_LIKELY(m_data.is_allocated()))
+ {
+ m_data.get_deleter()(m_data.get_resource());
+ m_data.set_unallocated();
+ }
+ }
+
+ /*!
+ * \brief Assigns a new resource object to the unique resource wrapper.
+ *
+ * **Effects:** Calls `this->reset()`. Then, if \c Resource is nothrow assignable from `R&&`,
+ * assigns `std::forward< R >(res)` to the stored resource object, otherwise assigns
+ * `res`.
+ *
+ * If \a res is not an unallocated resource value and an exception is thrown during the operation,
+ * invokes the stored deleter on \a res before returning with the exception.
+ *
+ * **Throws:** Nothing, unless invoking the deleter throws.
+ *
+ * \param res Resource object to assign.
+ *
+ * \post `this->allocated() == false`
+ */
+ template< typename R >
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ typename std::enable_if< detail::conjunction<
+ std::is_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type >,
+ detail::disjunction< detail::negation< std::is_reference< resource_type > >, std::is_reference< R > > // prevent binding lvalue-reference resource to an rvalue
+ >::value >::type
+#else
+ void
+#endif
+ reset(R&& res)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ detail::conjunction<
+ detail::is_nothrow_invocable< deleter_type&, resource_type& >,
+ std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type >
+ >::value
+ ))
+ {
+ reset_impl
+ (
+ static_cast< R&& >(res),
+ typename detail::conjunction<
+ detail::is_nothrow_invocable< deleter_type&, resource_type& >,
+ std::is_nothrow_assignable< internal_resource_type&, typename detail::move_or_copy_assign_ref< R, resource_type >::type >
+ >::type()
+ );
+ }
+
+ /*!
+ * \brief Invokes indirection on the resource object.
+ *
+ * **Requires:** \c Resource is dereferenceable.
+ *
+ * **Effects:** Returns a reference to the resource object as if by calling `get()`.
+ *
+ * \note If \c Resource is not a pointer type, the compiler will invoke its `operator->`.
+ * Such call sequence will continue until a pointer is obtained.
+ *
+ * **Throws:** Nothing. Note that any implicit subsequent calls to other `operator->`
+ * functions that are caused by this call may have different throw conditions.
+ */
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ template< bool Requires = detail::is_dereferenceable< resource_type >::value >
+ typename std::enable_if< Requires, resource_type const& >::type
+#else
+ resource_type const&
+#endif
+ operator-> () const noexcept
+ {
+ return get();
+ }
+
+ /*!
+ * \brief Dereferences the resource object.
+ *
+ * **Requires:** \c Resource is dereferenceable.
+ *
+ * **Effects:** Returns the result of dereferencing the resource object as if by calling `*get()`.
+ *
+ * **Throws:** Nothing, unless dereferencing the resource object throws.
+ */
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ template< bool Requires = detail::is_dereferenceable< resource_type >::value >
+ typename detail::dereference_traits< resource_type, Requires >::result_type
+#else
+ auto
+#endif
+ operator* () const
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::dereference_traits< resource_type, Requires >::is_noexcept))
+ {
+ return *get();
+ }
+
+ /*!
+ * \brief Swaps two unique resource wrappers.
+ *
+ * **Requires:** \c Resource and \c Deleter are swappable. At least one of \c Resource and \c Deleter
+ * is nothrow swappable.
+ *
+ * **Effects:** Swaps the resource objects and deleter objects stored in `*this` and \a that
+ * as if by calling unqualified `swap` in a context where `std::swap` is
+ * found by overload resolution.
+ *
+ * If an exception is thrown, and the failed swap operation supports strong exception
+ * guarantee, both `*this` and \a that are left in their original states.
+ *
+ * **Throws:** Nothing, unless swapping the resource objects or deleters throw.
+ *
+ * \param that Unique resource wrapper to swap with.
+ */
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ template< bool Requires = detail::is_swappable< data >::value >
+ typename std::enable_if< Requires >::type
+#else
+ void
+#endif
+ swap(unique_resource& that)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value))
+ {
+ m_data.swap(that.m_data);
+ }
+
+ /*!
+ * \brief Swaps two unique resource wrappers.
+ *
+ * **Effects:** As if `left.swap(right)`.
+ */
+#if !defined(BOOST_SCOPE_DOXYGEN)
+ template< bool Requires = detail::is_swappable< data >::value >
+ friend typename std::enable_if< Requires >::type
+#else
+ friend void
+#endif
+ swap(unique_resource& left, unique_resource& right)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::is_nothrow_swappable< data >::value))
+ {
+ left.swap(right);
+ }
+
+//! \cond
+private:
+ //! Assigns a new resource object to the unique resource wrapper.
+ template< typename R >
+ void reset_impl(R&& res, std::true_type) noexcept
+ {
+ reset();
+ m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res));
+ }
+
+ //! Assigns a new resource object to the unique resource wrapper.
+ template< typename R >
+ void reset_impl(R&& res, std::false_type)
+ {
+ try
+ {
+ reset();
+ m_data.assign_resource(static_cast< typename detail::move_or_copy_assign_ref< R, resource_type >::type >(res));
+ }
+ catch (...)
+ {
+ m_data.get_deleter()(static_cast< R&& >(res));
+ throw;
+ }
+ }
+//! \endcond
+};
+
+#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+template<
+ typename Resource,
+ typename Deleter,
+ typename = typename std::enable_if< !detail::is_default_resource< Resource >::value >::type
+>
+unique_resource(Resource, Deleter) -> unique_resource< Resource, Deleter >;
+#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
+
+/*!
+ * \brief Checks if the resource is valid and creates a \c unique_resource wrapper.
+ *
+ * **Effects:** If the resource \a res is not equal to \a invalid, creates a unique resource wrapper
+ * that is in allocated state and owns \a res. Otherwise creates a unique resource wrapper
+ * in unallocated state.
+ *
+ * \note This function does not call \a del if \a res is equal to \a invalid.
+ *
+ * **Throws:** Nothing, unless \c unique_resource constructor throws.
+ *
+ * \param res Resource to wrap.
+ * \param invalid An invalid value for the resource.
+ * \param del A deleter to invoke on the resource to free it.
+ */
+template< typename Resource, typename Deleter, typename Invalid >
+inline unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >
+make_unique_resource_checked(Resource&& res, Invalid const& invalid, Deleter&& del)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
+ detail::conjunction<
+ std::is_nothrow_constructible< typename std::decay< Resource >::type, typename detail::move_or_copy_construct_ref< Resource, typename std::decay< Resource >::type >::type >,
+ std::is_nothrow_constructible< typename std::decay< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter, typename std::decay< Deleter >::type >::type >
+ >::value
+ ))
+{
+ using unique_resource_type = unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >;
+ if (!(res == invalid))
+ return unique_resource_type(static_cast< Resource&& >(res), static_cast< Deleter&& >(del));
+ else
+ return unique_resource_type(default_resource_t(), static_cast< Deleter&& >(del));
+}
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_UNIQUE_RESOURCE_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/include/boost/scope/unique_resource_fwd.hpp b/contrib/restricted/boost/scope/include/boost/scope/unique_resource_fwd.hpp
new file mode 100644
index 0000000000..aa9c30ec92
--- /dev/null
+++ b/contrib/restricted/boost/scope/include/boost/scope/unique_resource_fwd.hpp
@@ -0,0 +1,46 @@
+/*
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * https://www.boost.org/LICENSE_1_0.txt)
+ *
+ * Copyright (c) 2022 Andrey Semashev
+ */
+/*!
+ * \file scope/unique_resource_fwd.hpp
+ *
+ * This header contains forward declaration of \c unique_resource template.
+ */
+
+#ifndef BOOST_SCOPE_UNIQUE_RESOURCE_FWD_HPP_INCLUDED_
+#define BOOST_SCOPE_UNIQUE_RESOURCE_FWD_HPP_INCLUDED_
+
+#include <type_traits>
+#include <boost/scope/detail/config.hpp>
+#include <boost/scope/detail/move_or_copy_construct_ref.hpp>
+#include <boost/scope/detail/type_traits/conjunction.hpp>
+#include <boost/scope/detail/header.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+namespace boost {
+namespace scope {
+
+template< typename Resource, typename Deleter, typename Traits = void >
+class unique_resource;
+
+template< typename Resource, typename Deleter, typename Invalid = typename std::decay< Resource >::type >
+unique_resource< typename std::decay< Resource >::type, typename std::decay< Deleter >::type >
+make_unique_resource_checked(Resource&& res, Invalid const& invalid, Deleter&& del)
+ noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(detail::conjunction<
+ std::is_nothrow_constructible< typename std::decay< Resource >::type, typename detail::move_or_copy_construct_ref< Resource, typename std::decay< Resource >::type >::type >,
+ std::is_nothrow_constructible< typename std::decay< Deleter >::type, typename detail::move_or_copy_construct_ref< Deleter, typename std::decay< Deleter >::type >::type >
+ >::value));
+
+} // namespace scope
+} // namespace boost
+
+#include <boost/scope/detail/footer.hpp>
+
+#endif // BOOST_SCOPE_UNIQUE_RESOURCE_FWD_HPP_INCLUDED_
diff --git a/contrib/restricted/boost/scope/ya.make b/contrib/restricted/boost/scope/ya.make
new file mode 100644
index 0000000000..b81b5900fe
--- /dev/null
+++ b/contrib/restricted/boost/scope/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(BSL-1.0)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(1.86.0)
+
+ORIGINAL_SOURCE(https://github.com/boostorg/scope/archive/boost-1.86.0.tar.gz)
+
+PEERDIR(
+ contrib/restricted/boost/config
+ contrib/restricted/boost/core
+ contrib/restricted/boost/type_traits
+)
+
+ADDINCL(
+ GLOBAL contrib/restricted/boost/scope/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+END()
diff --git a/contrib/restricted/expected-lite/.yandex_meta/devtools.copyrights.report b/contrib/restricted/expected-lite/.yandex_meta/devtools.copyrights.report
new file mode 100644
index 0000000000..8f9a274352
--- /dev/null
+++ b/contrib/restricted/expected-lite/.yandex_meta/devtools.copyrights.report
@@ -0,0 +1,41 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+KEEP COPYRIGHT_SERVICE_LABEL 81e2e1bd273033f7a382ceaf46d6ae84
+BELONGS ya.make
+ License text:
+ // Copyright (C) 2016-2020 Martin Moene.
+ Scancode info:
+ Original SPDX id: COPYRIGHT_SERVICE_LABEL
+ Score : 100.00
+ Match type : COPYRIGHT
+ Files with this license:
+ include/nonstd/expected.hpp [3:3]
diff --git a/contrib/restricted/expected-lite/.yandex_meta/devtools.licenses.report b/contrib/restricted/expected-lite/.yandex_meta/devtools.licenses.report
new file mode 100644
index 0000000000..e7b3d6ba44
--- /dev/null
+++ b/contrib/restricted/expected-lite/.yandex_meta/devtools.licenses.report
@@ -0,0 +1,79 @@
+# File format ($ symbol means the beginning of a line):
+#
+# $ # this message
+# $ # =======================
+# $ # comments (all commentaries should starts with some number of spaces and # symbol)
+# ${action} {license id} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make
+# ${all_file_action} filename
+# $ # user commentaries (many lines)
+# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify)
+# ${action} {license spdx} {license text hash}
+# $BELONGS ./ya/make/file/relative/path/3/ya.make
+# ${all_file_action} filename
+# $ # user commentaries
+# $ generated description
+# $ ...
+#
+# You can modify action, all_file_action and add commentaries
+# Available actions:
+# keep - keep license in contrib and use in credits
+# skip - skip license
+# remove - remove all files with this license
+# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file
+#
+# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory)
+# We suppose that that files can contain some license info
+# Available all file actions:
+# FILE_IGNORE - ignore file (do nothing)
+# FILE_INCLUDE - include all file data into licenses text file
+# =======================
+
+SKIP IPL-1.0 18358e6da236a2ee9e6e43715ed951f6
+BELONGS ya.make
+ # Not a license
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: IPL-1.0
+ Score : 57.14
+ Match type : REFERENCE
+ Links : http://www.opensource.org/licenses/ibmpl.php, https://spdx.org/licenses/IPL-1.0
+ Files with this license:
+ README.md [3:3]
+
+KEEP BSL-1.0 2c7a3fa82e66676005cd4ee2608fd7d2
+BELONGS ya.make
+ Note: matched license text is too long. Read it in the source files.
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : TEXT
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ LICENSE.txt [1:23]
+
+KEEP BSL-1.0 73a2516db620f6413502ee77bdb4af88
+BELONGS ya.make
+ License text:
+ *expected lite* is distributed under the [Boost Software License](https://github.com/martinmoene/XXXX-lite/blob/master/LICENSE.txt).
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ README.md [76:76]
+
+KEEP BSL-1.0 cccf0dd010adef3878bb56734414eca6
+BELONGS ya.make
+FILE_INCLUDE LICENSE.txt found in files: include/nonstd/expected.hpp at line 6
+ License text:
+ // Distributed under the Boost Software License, Version 1.0.
+ // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ Scancode info:
+ Original SPDX id: BSL-1.0
+ Score : 100.00
+ Match type : NOTICE
+ Links : http://www.boost.org/LICENSE_1_0.txt, http://www.boost.org/users/license.html, https://spdx.org/licenses/BSL-1.0
+ Files with this license:
+ include/nonstd/expected.hpp [5:6]
diff --git a/contrib/restricted/expected-lite/.yandex_meta/licenses.list.txt b/contrib/restricted/expected-lite/.yandex_meta/licenses.list.txt
new file mode 100644
index 0000000000..481545b760
--- /dev/null
+++ b/contrib/restricted/expected-lite/.yandex_meta/licenses.list.txt
@@ -0,0 +1,62 @@
+====================BSL-1.0====================
+*expected lite* is distributed under the [Boost Software License](https://github.com/martinmoene/XXXX-lite/blob/master/LICENSE.txt).
+
+
+====================BSL-1.0====================
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+
+====================BSL-1.0====================
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+====================COPYRIGHT====================
+// Copyright (C) 2016-2020 Martin Moene.
+
+
+====================File: LICENSE.txt====================
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/contrib/restricted/expected-lite/CHANGES.txt b/contrib/restricted/expected-lite/CHANGES.txt
new file mode 100644
index 0000000000..9c18a5702f
--- /dev/null
+++ b/contrib/restricted/expected-lite/CHANGES.txt
@@ -0,0 +1,5 @@
+Changes for expected lite
+
+version 0.0 2016-03-13
+
+- Initial code commit.
diff --git a/contrib/restricted/expected-lite/LICENSE.txt b/contrib/restricted/expected-lite/LICENSE.txt
new file mode 100644
index 0000000000..36b7cd93cd
--- /dev/null
+++ b/contrib/restricted/expected-lite/LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/contrib/restricted/expected-lite/README.md b/contrib/restricted/expected-lite/README.md
new file mode 100644
index 0000000000..bbef2e20f8
--- /dev/null
+++ b/contrib/restricted/expected-lite/README.md
@@ -0,0 +1,506 @@
+# expected lite: expected objects for C++11 and later
+
+[![Language](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/expected-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/expected-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/sle31w7obrm8lhe1?svg=true)](https://ci.appveyor.com/project/martinmoene/expected-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fexpected-lite.svg)](https://github.com/martinmoene/expected-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://raw.githubusercontent.com/martinmoene/expected-lite/master/include/nonstd/expected.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/expected-lite) [![Vcpkg](https://img.shields.io/badge/on-vcpkg-blue.svg)](https://vcpkg.link/ports/expected-lite) [![Try it online](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/MnnwqOtE8ZQ4rRsv) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/9BuMZx)
+
+*expected lite* is a single-file header-only library for objects that either represent a valid value or an error that you can pass by value. It is intended for use with C++11 and later. The library is based on the [std:&#58;expected](http://wg21.link/p0323) proposal [1] .
+
+**Contents**
+- [Example usage](#example-usage)
+- [In a nutshell](#in-a-nutshell)
+- [License](#license)
+- [Dependencies](#dependencies)
+- [Installation](#installation)
+- [Synopsis](#synopsis)
+- [Comparison with like types](#comparison)
+- [Reported to work with](#reported-to-work-with)
+- [Implementation notes](#implementation-notes)
+- [Other implementations of expected](#other-implementations-of-expected)
+- [Notes and references](#notes-and-references)
+- [Appendix](#appendix)
+
+## Example usage
+
+```Cpp
+#include "nonstd/expected.hpp"
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+using namespace nonstd;
+using namespace std::literals;
+
+auto to_int( char const * const text ) -> expected<int, std::string>
+{
+ char * pos = nullptr;
+ auto value = strtol( text, &pos, 0 );
+
+ if ( pos != text ) return value;
+ else return make_unexpected( "'"s + text + "' isn't a number" );
+}
+
+int main( int argc, char * argv[] )
+{
+ auto text = argc > 1 ? argv[1] : "42";
+
+ auto ei = to_int( text );
+
+ if ( ei ) std::cout << "'" << text << "' is " << *ei << ", ";
+ else std::cout << "Error: " << ei.error();
+}
+```
+
+### Compile and run
+
+```
+prompt> g++ -std=c++14 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe 123 && 01-basic.exe abc
+'123' is 123, Error: 'abc' isn't a number
+```
+
+## In a nutshell
+
+**expected lite** is a single-file header-only library to represent value objects that either contain a valid value or an error. The library is a partly implementation of the proposal for [std:&#58;expected](http://wg21.link/p0323) [1,2,3] for use with C++11 and later.
+
+**Some Features and properties of expected lite** are ease of installation (single header), default and explicit construction of an expected, construction and assignment from a value that is convertible to the underlying type, copy- and move-construction and copy- and move-assignment from another expected of the same type, testing for the presence of a value, operators for unchecked access to the value or the error (pointer or reference), value() and value_or() for checked access to the value, relational operators, swap() and various factory functions.
+
+Version 0.7.0 introduces the monadic operations of propsoal [p2505](http://wg21.link/p2505).
+
+*expected lite* shares the approach to in-place tags with [any-lite](https://github.com/martinmoene/any-lite), [optional-lite](https://github.com/martinmoene/optional-lite) and with [variant-lite](https://github.com/martinmoene/variant-lite) and these libraries can be used together.
+
+**Not provided** are reference-type expecteds. *expected lite* doesn't honour triviality of value and error types. *expected lite* doesn't handle overloaded *address of* operators.
+
+For more examples, see [1].
+
+## License
+
+*expected lite* is distributed under the [Boost Software License](https://github.com/martinmoene/XXXX-lite/blob/master/LICENSE.txt).
+
+## Dependencies
+
+*expected lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header).
+
+## Installation
+
+*expected lite* is a single-file header-only library. Put `expected.hpp` directly into the project source tree or somewhere reachable from your project.
+
+## Synopsis
+
+**Contents**
+- [Configuration](#configuration)
+- [Types in namespace nonstd](#types-in-namespace-nonstd)
+- [Interface of expected](#interface-of-expected)
+- [Algorithms for expected](#algorithms-for-expected)
+- [Interface of unexpected_type](#interface-of-unexpected_type)
+- [Algorithms for unexpected_type](#algorithms-for-unexpected_type)
+
+### Configuration
+
+#### Tweak header
+
+If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *expected lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/expected.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define expected_CPLUSPLUS 201103L`.
+
+#### Standard selection macro
+
+\-D<b>nsel\_CPLUSPLUS</b>=199711L
+Define this macro to override the auto-detection of the supported C++ standard, or if your compiler does not set the `__cplusplus` macro correctly.
+
+#### Select `std::expected` or `nonstd::expected`
+
+At default, *expected lite* uses `std::expected` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::expected` or expected lite's `nonstd::expected` as `nonstd::expected` via the following macros.
+
+-D<b>nsel\_CONFIG\_SELECT\_EXPECTED</b>=nsel_EXPECTED_DEFAULT
+Define this to `nsel_EXPECTED_STD` to select `std::expected` as `nonstd::expected`. Define this to `nsel_EXPECTED_NONSTD` to select `nonstd::expected` as `nonstd::expected`. Default is undefined, which has the same effect as defining to `nsel_EXPECTED_DEFAULT`.
+
+-D<b>nsel\_P0323R</b>=7 *(default)*
+Define this to the proposal revision number to control the presence and behavior of features (see tables). Default is 7 for the latest revision.
+
+#### Disable C++ exceptions
+
+-D<b>nsel\_CONFIG\_NO\_EXCEPTIONS</b>=0
+Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions` or `/kernel`). Default determined in header.
+
+#### Enable SEH exceptions
+
+-D<b>nsel\_CONFIG\_NO\_EXCEPTIONS\_SEH</b>=0
+Define this to 1 or 0 to control the use of SEH when C++ exceptions are disabled (see above). If not defined, the header tries and detect if SEH is available if C++ exceptions have been disabled (e.g. via `-fno-exceptions` or `/kernel`). Default determined in header.
+
+#### Enable compilation errors
+
+\-D<b>nsel\_CONFIG\_CONFIRMS\_COMPILATION\_ERRORS</b>=0
+Define this macro to 1 to experience the by-design compile-time errors of the library in the test suite. Default is 0.
+
+#### Configure P2505 monadic operations
+
+By default, *expected lite* provides monadic operations as described in [P2505R5](http://wg21.link/p2505r5). You can disable these operations by defining the following macro.
+
+-D<b>nsel\_P2505R</b>=0
+
+You can use the R3 revision of P2505, which lacks `error_or`, and uses `remove_cvref` for transforms, by defining the following macro.
+
+-D<b>nsel\_P2505R</b>=3
+
+### Types in namespace nonstd
+
+| Purpose | Type | Note / Object |
+|-----------------|------|---------------|
+| Expected | template&lt;typename T, typename E = std::exception_ptr><br>class **expected**; | nsel_P0323 <= 2 |
+| Expected | template&lt;typename T, typename E><br>class **expected**; | nsel_P0323 > 2 |
+| Error type | template&lt;typename E><br>class **unexpected_type**; | &nbsp; |
+| Error type | template&lt;><br>class **unexpected_type**&lt;std::exception_ptr>; | nsel_P0323 <= 2 |
+| Error type | template&lt;typename E><br>class **unexpected**; | >= C++17 |
+| Traits | template&lt;typename E><br>struct **is_unexpected**; | nsel_P0323 <= 3 |
+| In-place value construction | struct **in_place_t**; | in_place_t in_place{}; |
+| In-place error construction | struct **in_place_unexpected_t**; | in_place_unexpected_t<br>unexpect{}; |
+| In-place error construction | struct **in_place_unexpected_t**; | in_place_unexpected_t<br>in_place_unexpected{}; |
+| Error reporting | class **bad_expected_access**; |&nbsp; |
+
+### Interface of expected
+
+| Kind | Method | Result |
+|--------------|-------------------------------------------------------------------------|--------|
+| Construction | [constexpr] **expected**() noexcept(...) | an object with default value |
+| &nbsp; | [constexpr] **expected**( expected const & other ) | initialize to contents of other |
+| &nbsp; | [constexpr] **expected**( expected && other ) | move contents from other |
+| &nbsp; | [constexpr] **expected**( value_type const & value ) | initialize to value |
+| &nbsp; | [constexpr] **expected**( value_type && value ) noexcept(...) | move from value |
+| &nbsp; | [constexpr] explicit **expected**( in_place_t, Args&&... args ) | construct value in-place from args |
+| &nbsp; | [constexpr] explicit **expected**( in_place_t,<br>&emsp;std::initializer_list&lt;U> il, Args&&... args ) | construct value in-place from args |
+| &nbsp; | [constexpr] **expected**( unexpected_type<E> const & error ) | initialize to error |
+| &nbsp; | [constexpr] **expected**( unexpected_type<E> && error ) | move from error |
+| &nbsp; | [constexpr] explicit **expected**( in_place_unexpected_t,<br>&emsp;Args&&... args ) | construct error in-place from args |
+| &nbsp; | [constexpr] explicit **expected**( in_place_unexpected_t,<br>&emsp;std::initializer_list&lt;U> il, Args&&... args )| construct error in-place from args |
+| Destruction | ~**expected**() | destruct current content |
+| Assignment | expected **operator=**( expected const & other ) | assign contents of other;<br>destruct current content, if any |
+| &nbsp; | expected & **operator=**( expected && other ) noexcept(...) | move contents of other |
+| &nbsp; | expected & **operator=**( U && v ) | move value from v |
+| &nbsp; | expected & **operator=**( unexpected_type<E> const & u ) | initialize to unexpected |
+| &nbsp; | expected & **operator=**( unexpected_type<E> && u ) | move from unexpected |
+| &nbsp; | template&lt;typename... Args><br>void **emplace**( Args &&... args ) | emplace from args |
+| &nbsp; | template&lt;typename U, typename... Args><br>void **emplace**( std::initializer_list&lt;U> il, Args &&... args ) | emplace from args |
+| Swap | void **swap**( expected & other ) noexcept | swap with other |
+| Observers | constexpr value_type const \* **operator->**() const | pointer to current content (const);<br>must contain value |
+| &nbsp; | value_type \* **operator->**() | pointer to current content (non-const);<br>must contain value |
+| &nbsp; | constexpr value_type const & **operator \***() const & | the current content (const ref);<br>must contain value |
+| &nbsp; | constexpr value_type && **operator \***() && | the current content (non-const ref);<br>must contain value |
+| &nbsp; | constexpr explicit operator **bool**() const noexcept | true if contains value |
+| &nbsp; | constexpr **has_value**() const noexcept | true if contains value |
+| &nbsp; | constexpr value_type const & **value**() const & | current content (const ref);<br>see [note 1](#note1) |
+| &nbsp; | value_type & **value**() & | current content (non-const ref);<br>see [note 1](#note1) |
+| &nbsp; | constexpr value_type && **value**() && | move from current content;<br>see [note 1](#note1) |
+| &nbsp; | constexpr error_type const & **error**() const & | current error (const ref);<br>must contain error |
+| &nbsp; | error_type & **error**() & | current error (non-const ref);<br>must contain error |
+| &nbsp; | constexpr error_type && **error**() && | move from current error;<br>must contain error |
+| &nbsp; | constexpr unexpected_type<E> **get_unexpected**() const | the error as unexpected&lt;>;<br>must contain error |
+| &nbsp; | template&lt;typename Ex><br>bool **has_exception**() const | true of contains exception (as base) |
+| &nbsp; | value_type **value_or**( U && v ) const & | value or move from v |
+| &nbsp; | value_type **value_or**( U && v ) && | move from value or move from v |
+| &nbsp; | constexpr error_type **error_or**( G && e ) const & | return current error or v [requires nsel_P2505R >= 4] |
+| &nbsp; | constexpr error_type **error_or**( G && e ) && | move from current error or from v [requires nsel_P2505R >=4] |
+| Monadic operations<br>(requires nsel_P2505R >= 3) | constexpr auto **and_then**( F && f ) & G| return f(value()) if has value, otherwise the error |
+| &nbsp; | constexpr auto **and_then**( F && f ) const & | return f(value()) if has value, otherwise the error |
+| &nbsp; | constexpr auto **and_then**( F && f ) && | return f(std::move(value())) if has value, otherwise the error |
+| &nbsp; | constexpr auto **and_then**( F && f ) const && | return f(std::move(value())) if has value, otherwise the error |
+| &nbsp; | constexpr auto **or_else**( F && f ) & | return the value, or f(error()) if there is no value |
+| &nbsp; | constexpr auto **or_else**( F && f ) const & | return the value, or f(error()) if there is no value |
+| &nbsp; | constexpr auto **or_else**( F && f ) && | return the value, or f(std::move(error())) if there is no value |
+| &nbsp; | constexpr auto **or_else**( F && f ) const && | return the value, or f(std::move(error())) if there is no value |
+| &nbsp; | constexpr auto **transform**( F && f ) & | return f(value()) wrapped if has value, otherwise the error |
+| &nbsp; | constexpr auto **transform**( F && f ) const & | return f(value()) wrapped if has value, otherwise the error |
+| &nbsp; | constexpr auto **transform**( F && f ) && | return f(std::move(value())) wrapped if has value, otherwise the error |
+| &nbsp; | constexpr auto **transform**( F && f ) const && | return f(std::move(value())) wrapped if has value, otherwise the error |
+| &nbsp; | constexpr auto **transform_error**( F && f ) & | return the value if has value, or f(error()) otherwise |
+| &nbsp; | constexpr auto **transform_error**( F && f ) const & | return the value if has value, or f(error()) otherwise |
+| &nbsp; | constexpr auto **transform_error**( F && f ) && | return the value if has value, or f(std::move(error())) otherwise |
+| &nbsp; | constexpr auto **transform_error**( F && f ) const && | return the value if has value, or f(std::move(error())) otherwise |
+| &nbsp; | ... | &nbsp; |
+
+<a id="note1"></a>Note 1: checked access: if no content, for std::exception_ptr rethrows error(), otherwise throws bad_expected_access(error()).
+
+### Algorithms for expected
+
+| Kind | Function |
+|---------------------------------|----------|
+| Comparison with expected | &nbsp; |
+| ==&ensp;!= | template&lt;typename T1, typename E1, typename T2, typename E2><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T1,E1> const & x,<br>&emsp;expected&lt;T2,E2> const & y ) |
+| Comparison with expected | nsel_P0323R <= 2 |
+| <&ensp;>&ensp;<=&ensp;>= | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T,E> const & x,<br>&emsp;expected&lt;T,E> const & y ) |
+| Comparison with unexpected_type | &nbsp; |
+| ==&ensp;!= | template&lt;typename T1, typename E1, typename E2><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T1,E1> const & x,<br>&emsp;unexpected_type&lt;E2> const & u ) |
+| &nbsp; | template&lt;typename T1, typename E1, typename E2><br>constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;E2> const & u,<br>&emsp;expected&lt;T1,E1> const & x ) |
+| Comparison with unexpected_type | nsel_P0323R <= 2 |
+| <&ensp;>&ensp;<=&ensp;>= | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T,E> const & x,<br>&emsp;unexpected_type&lt;E> const & u ) |
+| &nbsp; | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;E> const & u,<br>&emsp;expected&lt;T,E> const & x ) |
+| Comparison with T | &nbsp; |
+| ==&ensp;!= | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T,E> const & x,<br>&emsp;T const & v ) |
+| &nbsp; | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;T const & v,<br>&emsp;expected&lt;T,E> const & x ) |
+| Comparison with T | nsel_P0323R <= 2 |
+| <&ensp;>&ensp;<=&ensp;>= | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;expected&lt;T,E> const & x,<br>&emsp;T const & v ) |
+| &nbsp; | template&lt;typename T, typename E><br>constexpr bool operator ***op***(<br>&emsp;T const & v,<br>&emsp;expected&lt;T,E> const & x ) |
+| Specialized algorithms | &nbsp; |
+| Swap | template&lt;typename T, typename E><br>void **swap**(<br>&emsp;expected&lt;T,E> & x,<br>&emsp;expected&lt;T,E> & y )&emsp;noexcept( noexcept( x.swap(y) ) ) |
+| Make expected from | nsel_P0323R <= 3 |
+| &emsp;Value | template&lt;typename T><br>constexpr auto **make_expected**( T && v ) -><br>&emsp;expected< typename std::decay&lt;T>::type> |
+| &emsp;Nothing | auto **make_expected**() -> expected&lt;void> |
+| &emsp;Current exception | template&lt;typename T><br>constexpr auto **make_expected_from_current_exception**() -> expected&lt;T> |
+| &emsp;Exception | template&lt;typename T><br>auto **make_expected_from_exception**( std::exception_ptr v ) -> expected&lt;T>|
+| &emsp;Error | template&lt;typename T, typename E><br>constexpr auto **make_expected_from_error**( E e ) -><br>&emsp;expected&lt;T, typename std::decay&lt;E>::type> |
+| &emsp;Call | template&lt;typename F><br>auto **make_expected_from_call**( F f ) -><br>&emsp;expected< typename std::result_of&lt;F()>::type>|
+| &emsp;Call, void specialization | template&lt;typename F><br>auto **make_expected_from_call**( F f ) -> expected&lt;void> |
+
+### Interface of unexpected_type
+
+| Kind | Method | Result |
+|--------------|-----------------------------------------------------------|--------|
+| Construction | **unexpected_type**() = delete; | no default construction |
+| &nbsp; | constexpr explicit **unexpected_type**( E const & error ) | copy-constructed from an E |
+| &nbsp; | constexpr explicit **unexpected_type**( E && error ) | move-constructed from an E |
+| Observers | constexpr error_type const & **error**() const | can observe contained error |
+| &nbsp; | error_type & **error**() | can modify contained error |
+| deprecated | constexpr error_type const & **value**() const | can observe contained error |
+| deprecated | error_type & **value**() | can modify contained error |
+
+### Algorithms for unexpected_type
+
+| Kind | Function |
+|-------------------------------|----------|
+| Comparison with unexpected | &nbsp; |
+| ==&ensp;!= | template&lt;typename E><br>constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;E> const & x,<br>&emsp;unexpected_type&lt;E> const & y ) |
+| Comparison with unexpected | nsel_P0323R <= 2 |
+| <&ensp;>&ensp;<=&ensp;>= | template&lt;typename E><br>constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;E> const & x,<br>&emsp;unexpected_type&lt;E> const & y ) |
+| Comparison with exception_ptr | &nbsp; |
+| ==&ensp;!= | constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;std::exception_ptr> const & x,<br>&emsp;unexpected_type&lt;std::exception_ptr> const & y ) |
+| Comparison with exception_ptr | nsel_P0323R <= 2 |
+| <&ensp;>&ensp;<=&ensp;>= | constexpr bool operator ***op***(<br>&emsp;unexpected_type&lt;std::exception_ptr> const & x,<br>&emsp;unexpected_type&lt;std::exception_ptr> const & y ) |
+| Specialized algorithms | &nbsp; |
+| Make unexpected from | &nbsp; |
+| &emsp;Error | template&lt;typename E><br>[constexpr] auto **make_unexpected**( E && v) -><br>&emsp;unexpected_type< typename std::decay&lt;E>::type>|
+| Make unexpected from | nsel_P0323R <= 3 |
+| &emsp;Current exception | [constexpr] auto **make_unexpected_from_current_exception**() -><br>&emsp;unexpected_type< std::exception_ptr>|
+
+<a id="comparison"></a>
+## Comparison with like types
+
+|Feature |<br>std::pair|std:: optional |std:: expected |nonstd:: expected |Boost. Expected |Nonco expected |Andrei Expected |Hagan required |
+|----------------------|-------------|---------------|---------------|------------------|----------------|---------------|----------------|---------------|
+|More information | see [14] | see [5] | see [1] | this work | see [4] | see [7] | see [8] | see [13] |
+| | | | | | | | | |
+| C++03 | yes | no | no | no/not yet | no (union) | no | no | yes |
+| C++11 | yes | no | no | yes | yes | yes | yes | yes |
+| C++14 | yes | no | no | yes | yes | yes | yes | yes |
+| C++17 | yes | yes | no | yes | yes | yes | yes | yes |
+| | | | | | | | | |
+|DefaultConstructible | T param | yes | yes | yes | yes | no | no | no |
+|In-place construction | no | yes | yes | yes | yes | yes | no | no |
+|Literal type | yes | yes | yes | yes | yes | no | no | no |
+| | | | | | | | | |
+|Disengaged information| possible | no | yes | yes | yes | yes | yes | no |
+|Vary disengaged type | yes | no | yes | yes | yes | no | no | no |
+|Engaged nonuse throws | no | no | no | no | error_traits | no | no | yes |
+|Disengaged use throws | no | yes, value() | yes, value() | yes, value() | yes,<br>value()| yes,<br>get() | yes,<br>get() | n/a |
+| | | | | | | | | |
+|Proxy (rel.ops) | no | yes | yes | yes | yes | no | no | no |
+|References | no | yes | no/not yet | no/not yet | no/not yet | yes | no | no |
+|Chained visitor(s) | no | no | yes | yes | yes | no | no | no |
+
+Note 1: std:&#58;*experimental*:&#58;expected
+
+Note 2: sources for [Nonco expected](https://github.com/martinmoene/spike-expected/tree/master/nonco), [Andrei Expected](https://github.com/martinmoene/spike-expected/tree/master/alexandrescu) and [Hagan required](https://github.com/martinmoene/spike-expected/tree/master/hagan) can befound in the [spike-expected](https://github.com/martinmoene/spike-expected) repository.
+
+## Reported to work with
+
+TBD
+
+## Implementation notes
+
+TBD
+
+## Other implementations of expected
+
+- Simon Brand. [C++11/14/17 std::expected with functional-style extensions](https://github.com/TartanLlama/expected). Single-header.
+- Isabella Muerte. [MNMLSTC Core](https://github.com/mnmlstc/core) (C++11).
+- Vicente J. Botet Escriba. [stdmake's expected](https://github.com/viboes/std-make/tree/master/include/experimental/fundamental/v3/expected) (C++17).
+- Facebook. [ Folly's Expected.h](https://github.com/facebook/folly/blob/master/folly/Expected.h) (C++14).
+
+## Notes and references
+
+[1] Vicente J. Botet Escriba. [p0323 - A proposal to add a utility class to represent expected object (latest)](http://wg21.link/p0323) (HTML). ([r12](http://wg21.link/p0323r12), [r11](http://wg21.link/p0323r11), [r10](http://wg21.link/p0323r10), [r9](http://wg21.link/p0323r9), [r8](http://wg21.link/p0323r8), [r7](http://wg21.link/p0323r7), [r6](http://wg21.link/p0323r6), [r5](http://wg21.link/p0323r5), [r4](http://wg21.link/p0323r4), [r3](http://wg21.link/p0323r3), [r2](http://wg21.link/p0323r2), [r1](http://wg21.link/n4109), [r0](http://wg21.link/n4015), [draft](https://github.com/viboes/std-make/blob/master/doc/proposal/expected/DXXXXR0_expected.pdf)).
+
+[2] Vicente J. Botet Escriba. [JASEL: Just a simple experimental library for C++](https://github.com/viboes/std-make). Reference implementation of [expected](https://github.com/viboes/std-make/tree/master/include/experimental/fundamental/v3/expected).
+
+[3] Vicente J. Botet Escriba. [Expected - An exception-friendly Error Monad](https://www.youtube.com/watch?v=Zdlt1rgYdMQ). C++Now 2014. 24 September 2014.
+
+[4] Pierre Talbot. [Boost.Expected. Unofficial Boost candidate](http://www.google-melange.com/gsoc/proposal/review/google/gsoc2013/trademark/25002). 5 May 2013. [GitHub](https://github.com/TrademarkPewPew/Boost.Expected), [GSoC 2013 Proposal](http://www.google-melange.com/gsoc/proposal/review/google/gsoc2013/trademark/25002), [boost@lists.boost.org](http://permalink.gmane.org/gmane.comp.lib.boost.devel/240056 ).
+
+[5] Fernando Cacciola and Andrzej Krzemieński. [A proposal to add a utility class to represent optional objects (Revision 4)](http://isocpp.org/files/papers/N3672.html). ISO/IEC JTC1 SC22 WG21 N3672 2013-04-19.
+
+[6] Andrzej Krzemieński, [Optional library implementation in C++11](https://github.com/akrzemi1/Optional/).
+
+[7] Anto Nonco. [Extending expected<T> to deal with references](http://anto-nonco.blogspot.nl/2013/03/extending-expected-to-deal-with.html). 27 May 2013.
+
+[8] Andrei Alexandrescu. Systematic Error Handling in C++. Prepared for The C++and Beyond Seminar 2012. [Video](http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C). [Slides](http://sdrv.ms/RXjNPR).
+
+[9] Andrei Alexandrescu. [Choose your Poison: Exceptions or Error Codes? (PDF)](http://accu.org/content/conf2007/Alexandrescu-Choose_Your_Poison.pdf). ACCU Conference 2007.
+
+[10] Andrei Alexandrescu. [The Power of None (PPT)](http://nwcpp.org/static/talks/2006/The_Power_of_None.ppt). Northwest C++ Users' Group. [May 17th, 2006](http://nwcpp.org/may-2006.html).
+
+[11] Jon Jagger. [A Return Type That Doesn't Like Being Ignored](http://accu.org/var/uploads/journals/overload53-FINAL.pdf#page=18). Overload issue 53, February 2003.
+
+[12] Andrei Alexandrescu. [Error Handling in C++: Are we inching towards a total solution?](http://accu.org/index.php/conferences/2002/speakers2002). ACCU Conference 2002.
+
+[13] Ken Hagan et al. [Exploding return codes](https://groups.google.com/d/msg/comp.lang.c++.moderated/BkZqPfoq3ys/H_PMR8Sat4oJ). comp.lang.c++.moderated. 11 February 2000.
+
+[14] [std::pair](http://en.cppreference.com/w/cpp/utility/pair). cppreference.com
+
+[15] Niall Douglas. [Outcome](https://ned14.github.io/outcome/). Very lightweight outcome&lt;T> and result&lt;T> (non-Boost edition).
+
+[16] Niall Douglas. [p0762 - Concerns about expected&lt;T, E> from the Boost.Outcome peer review](http://wg21.link/p0762). 15 October 2017.
+
+[17] Jeff Garland. [p2505 - Monadic Functions for `std::expected`](http://wg21.link/p2505) (HTML). ([r0](http://wg21.link/p2505r0), [r1](http://wg21.link/p2505r1), [r2](http://wg21.link/p2505r2), [r3](http://wg21.link/p2505r3), [r4](http://wg21.link/p2505r4), [r5](http://wg21.link/p2505r5)).
+
+## Appendix
+
+### A.1 Compile-time information
+
+The version of *expected lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`.
+
+### A.2 Expected lite test specification
+
+<details>
+<summary>click to expand</summary>
+<p>
+
+```Text
+unexpected_type: Disallows default construction
+unexpected_type: Allows to copy-construct from unexpected_type, default
+unexpected_type: Allows to move-construct from unexpected_type, default
+unexpected_type: Allows to in-place-construct
+unexpected_type: Allows to in-place-construct from initializer_list
+unexpected_type: Allows to copy-construct from error_type
+unexpected_type: Allows to move-construct from error_type
+unexpected_type: Allows to copy-construct from unexpected_type, explicit converting
+unexpected_type: Allows to copy-construct from unexpected_type, non-explicit converting
+unexpected_type: Allows to move-construct from unexpected_type, explicit converting
+unexpected_type: Allows to move-construct from unexpected_type, non-explicit converting
+unexpected_type: Allows to copy-assign from unexpected_type, default
+unexpected_type: Allows to move-assign from unexpected_type, default
+unexpected_type: Allows to copy-assign from unexpected_type, converting
+unexpected_type: Allows to move-assign from unexpected, converting
+unexpected_type: Allows to observe its value via a l-value reference
+unexpected_type: Allows to observe its value via a r-value reference
+unexpected_type: Allows to modify its value via a l-value reference
+unexpected_type: Allows to be swapped
+unexpected_type<std::exception_ptr>: Disallows default construction
+unexpected_type<std::exception_ptr>: Allows to copy-construct from error_type
+unexpected_type<std::exception_ptr>: Allows to move-construct from error_type
+unexpected_type<std::exception_ptr>: Allows to copy-construct from an exception
+unexpected_type<std::exception_ptr>: Allows to observe its value
+unexpected_type<std::exception_ptr>: Allows to modify its value
+unexpected_type: Provides relational operators
+unexpected_type: Provides relational operators, std::exception_ptr specialization
+make_unexpected(): Allows to create an unexpected_type<E> from an E
+unexpected: C++17 and later provide unexpected_type as unexpected
+bad_expected_access: Disallows default construction
+bad_expected_access: Allows construction from error_type
+bad_expected_access: Allows to observe its error
+bad_expected_access: Allows to change its error
+bad_expected_access: Provides non-empty what()
+expected: Allows to default construct
+expected: Allows to default construct from noncopyable, noncopyable value type
+expected: Allows to default construct from noncopyable, noncopyable error type
+expected: Allows to copy-construct from expected: value
+expected: Allows to copy-construct from expected: error
+expected: Allows to move-construct from expected: value
+expected: Allows to move-construct from expected: error
+expected: Allows to copy-construct from expected; value, explicit converting
+expected: Allows to copy-construct from expected; error, explicit converting
+expected: Allows to copy-construct from expected; value, non-explicit converting
+expected: Allows to copy-construct from expected; error, non-explicit converting
+expected: Allows to move-construct from expected; value, explicit converting
+expected: Allows to move-construct from expected; error, explicit converting
+expected: Allows to move-construct from expected; value, non-explicit converting
+expected: Allows to move-construct from expected; error, non-explicit converting
+expected: Allows to forward-construct from value, explicit converting
+expected: Allows to forward-construct from value, non-explicit converting
+expected: Allows to in-place-construct value
+expected: Allows to in-place-construct value from initializer_list
+expected: Allows to copy-construct from unexpected, explicit converting
+expected: Allows to copy-construct from unexpected, non-explicit converting
+expected: Allows to move-construct from unexpected, explicit converting
+expected: Allows to move-construct from unexpected, non-explicit converting
+expected: Allows to in-place-construct error
+expected: Allows to in-place-construct error from initializer_list
+expected: Allows to copy-assign from expected, value
+expected: Allows to copy-assign from expected, error
+expected: Allows to move-assign from expected, value
+expected: Allows to move-assign from expected, error
+expected: Allows to forward-assign from value
+expected: Allows to copy-assign from unexpected
+expected: Allows to move-assign from unexpected
+expected: Allows to move-assign from move-only unexpected
+expected: Allows to emplace value
+expected: Allows to emplace value from initializer_list
+expected: Allows to be swapped
+expected: Allows to observe its value via a pointer
+expected: Allows to observe its value via a pointer to constant
+expected: Allows to modify its value via a pointer
+expected: Allows to observe its value via a l-value reference
+expected: Allows to observe its value via a r-value reference
+expected: Allows to modify its value via a l-value reference
+expected: Allows to modify its value via a r-value reference
+expected: Allows to observe if it contains a value (or error)
+expected: Allows to observe its value
+expected: Allows to modify its value
+expected: Allows to move its value
+expected: Allows to observe its error
+expected: Allows to modify its error
+expected: Allows to move its error
+expected: Allows to observe its error as unexpected
+expected: Allows to query if it contains an exception of a specific base type
+expected: Allows to observe its value if available, or obtain a specified value otherwise
+expected: Allows to move its value if available, or obtain a specified value otherwise
+expected: Throws bad_expected_access on value access when disengaged
+expected: Allows to observe its unexpected value, or fallback to the specified value with error_or [monadic p2505r4]
+expected: Allows to map value with and_then [monadic p2505r3]
+expected: Allows to map unexpected with or_else [monadic p2505r3]
+expected: Allows to transform value [monadic p2505r3]
+expected: Allows to map errors with transform_error [monadic p2505r3]
+expected<void>: Allows to default-construct
+expected<void>: Allows to copy-construct from expected<void>: value
+expected<void>: Allows to copy-construct from expected<void>: error
+expected<void>: Allows to move-construct from expected<void>: value
+expected<void>: Allows to move-construct from expected<void>: error
+expected<void>: Allows to in-place-construct
+expected<void>: Allows to copy-construct from unexpected, explicit converting
+expected<void>: Allows to copy-construct from unexpected, non-explicit converting
+expected<void>: Allows to move-construct from unexpected, explicit converting
+expected<void>: Allows to move-construct from unexpected, non-explicit converting
+expected<void>: Allows to in-place-construct unexpected_type
+expected<void>: Allows to in-place-construct error from initializer_list
+expected<void>: Allows to copy-assign from expected, value
+expected<void>: Allows to copy-assign from expected, error
+expected<void>: Allows to move-assign from expected, value
+expected<void>: Allows to move-assign from expected, error
+expected<void>: Allows to emplace value
+expected<void>: Allows to be swapped
+expected<void>: Allows to observe if it contains a value (or error)
+expected<void>: Allows to observe its value
+expected<void>: Allows to observe its error
+expected<void>: Allows to modify its error
+expected<void>: Allows to move its error
+expected<void>: Allows to observe its error as unexpected
+expected<void>: Allows to query if it contains an exception of a specific base type
+expected<void>: Throws bad_expected_access on value access when disengaged
+expected<void>: Allows to observe unexpected value, or fallback to a default value with error_or [monadic p2505r4]
+expected<void>: Allows to call argless functions with and_then [monadic p2505r3]
+expected<void>: Allows to map to expected or unexpected with or_else [monadic p2505r3]
+expected<void>: Allows to assign a new expected value using transform [monadic p2505r3]
+expected<void>: Allows to map unexpected error value via transform_error [monadic p2505r3]
+operators: Provides expected relational operators
+operators: Provides expected relational operators (void)
+swap: Allows expected to be swapped
+std::hash: Allows to compute hash value for expected
+tweak header: reads tweak header if supported [tweak]
+```
+
+</p>
+</details>
diff --git a/contrib/restricted/expected-lite/include/nonstd/expected.hpp b/contrib/restricted/expected-lite/include/nonstd/expected.hpp
new file mode 100644
index 0000000000..588ba1a22e
--- /dev/null
+++ b/contrib/restricted/expected-lite/include/nonstd/expected.hpp
@@ -0,0 +1,3555 @@
+// This version targets C++11 and later.
+//
+// Copyright (C) 2016-2020 Martin Moene.
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// expected lite is based on:
+// A proposal to add a utility class to represent expected monad
+// by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323
+
+#ifndef NONSTD_EXPECTED_LITE_HPP
+#define NONSTD_EXPECTED_LITE_HPP
+
+#define expected_lite_MAJOR 0
+#define expected_lite_MINOR 8
+#define expected_lite_PATCH 0
+
+#define expected_lite_VERSION expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH)
+
+#define expected_STRINGIFY( x ) expected_STRINGIFY_( x )
+#define expected_STRINGIFY_( x ) #x
+
+// expected-lite configuration:
+
+#define nsel_EXPECTED_DEFAULT 0
+#define nsel_EXPECTED_NONSTD 1
+#define nsel_EXPECTED_STD 2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/expected.tweak.hpp>)
+# error #include <nonstd/expected.tweak.hpp>
+# endif
+#define expected_HAVE_TWEAK_HEADER 1
+#else
+#define expected_HAVE_TWEAK_HEADER 0
+//# pragma message("expected.hpp: Note: Tweak header not supported.")
+#endif
+
+// expected selection and configuration:
+
+#if !defined( nsel_CONFIG_SELECT_EXPECTED )
+# define nsel_CONFIG_SELECT_EXPECTED ( nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_NONSTD )
+#endif
+
+// Proposal revisions:
+//
+// DXXXXR0: --
+// N4015 : -2 (2014-05-26)
+// N4109 : -1 (2014-06-29)
+// P0323R0: 0 (2016-05-28)
+// P0323R1: 1 (2016-10-12)
+// -------:
+// P0323R2: 2 (2017-06-15)
+// P0323R3: 3 (2017-10-15)
+// P0323R4: 4 (2017-11-26)
+// P0323R5: 5 (2018-02-08)
+// P0323R6: 6 (2018-04-02)
+// P0323R7: 7 (2018-06-22) *
+//
+// expected-lite uses 2 and higher
+
+#ifndef nsel_P0323R
+# define nsel_P0323R 7
+#endif
+
+// Monadic operations proposal revisions:
+//
+// P2505R0: 0 (2021-12-12)
+// P2505R1: 1 (2022-02-10)
+// P2505R2: 2 (2022-04-15)
+// P2505R3: 3 (2022-06-05)
+// P2505R4: 4 (2022-06-15)
+// P2505R5: 5 (2022-09-20) *
+//
+// expected-lite uses 5
+
+#ifndef nsel_P2505R
+# define nsel_P2505R 5
+#endif
+
+// Control presence of C++ exception handling (try and auto discover):
+
+#ifndef nsel_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+# include <cstddef> // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+# define nsel_CONFIG_NO_EXCEPTIONS 0
+# else
+# define nsel_CONFIG_NO_EXCEPTIONS 1
+# endif
+#endif
+
+// at default use SEH with MSVC for no C++ exceptions
+
+#ifndef nsel_CONFIG_NO_EXCEPTIONS_SEH
+# define nsel_CONFIG_NO_EXCEPTIONS_SEH ( nsel_CONFIG_NO_EXCEPTIONS && _MSC_VER )
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef nsel_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+# define nsel_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+# define nsel_CPLUSPLUS __cplusplus
+# endif
+#endif
+
+#define nsel_CPP98_OR_GREATER ( nsel_CPLUSPLUS >= 199711L )
+#define nsel_CPP11_OR_GREATER ( nsel_CPLUSPLUS >= 201103L )
+#define nsel_CPP14_OR_GREATER ( nsel_CPLUSPLUS >= 201402L )
+#define nsel_CPP17_OR_GREATER ( nsel_CPLUSPLUS >= 201703L )
+#define nsel_CPP20_OR_GREATER ( nsel_CPLUSPLUS >= 202002L )
+#define nsel_CPP23_OR_GREATER ( nsel_CPLUSPLUS >= 202300L )
+
+// Use C++23 std::expected if available and requested:
+
+#if nsel_CPP23_OR_GREATER && defined(__has_include )
+# if __has_include( <expected> )
+# define nsel_HAVE_STD_EXPECTED 1
+# else
+# define nsel_HAVE_STD_EXPECTED 0
+# endif
+#else
+# define nsel_HAVE_STD_EXPECTED 0
+#endif
+
+#define nsel_USES_STD_EXPECTED ( (nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, expected-lite, value-ptr-lite, variant-lite:
+//
+
+#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES
+#define nonstd_lite_HAVE_IN_PLACE_TYPES 1
+
+// C++17 std::in_place in <utility>:
+
+#if nsel_CPP17_OR_GREATER
+
+#include <utility>
+
+namespace nonstd {
+
+using std::in_place;
+using std::in_place_type;
+using std::in_place_index;
+using std::in_place_t;
+using std::in_place_type_t;
+using std::in_place_index_t;
+
+#define nonstd_lite_in_place_t( T) std::in_place_t
+#define nonstd_lite_in_place_type_t( T) std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K) std::in_place_index_t<K>
+
+#define nonstd_lite_in_place( T) std::in_place_t{}
+#define nonstd_lite_in_place_type( T) std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K) std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // nsel_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+ return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+ return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> )
+#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T) nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K) nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // nsel_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+//
+// Using std::expected:
+//
+
+#if nsel_USES_STD_EXPECTED
+
+#error #include <expected>
+
+namespace nonstd {
+
+ using std::expected;
+ using std::unexpected;
+ using std::bad_expected_access;
+ using std::unexpect_t;
+ using std::unexpect;
+
+ //[[deprecated("replace unexpected_type with unexpected")]]
+
+ template< typename E >
+ using unexpected_type = unexpected<E>;
+
+ // Unconditionally provide make_unexpected():
+
+ template< typename E>
+ constexpr auto make_unexpected( E && value ) -> unexpected< typename std::decay<E>::type >
+ {
+ return unexpected< typename std::decay<E>::type >( std::forward<E>(value) );
+ }
+} // namespace nonstd
+
+#else // nsel_USES_STD_EXPECTED
+
+#include <cassert>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <new>
+#include <system_error>
+#include <type_traits>
+#include <utility>
+
+// additional includes:
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+# if nsel_CONFIG_NO_EXCEPTIONS_SEH
+# include <windows.h> // for ExceptionCodes
+# else
+// already included: <cassert>
+# endif
+#else
+# include <stdexcept>
+#endif
+
+// C++ feature usage:
+
+#if nsel_CPP11_OR_GREATER
+# define nsel_constexpr constexpr
+#else
+# define nsel_constexpr /*constexpr*/
+#endif
+
+#if nsel_CPP14_OR_GREATER
+# define nsel_constexpr14 constexpr
+#else
+# define nsel_constexpr14 /*constexpr*/
+#endif
+
+#if nsel_CPP17_OR_GREATER
+# define nsel_inline17 inline
+#else
+# define nsel_inline17 /*inline*/
+#endif
+
+// Compiler versions:
+//
+// MSVC++ 6.0 _MSC_VER == 1200 nsel_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0)
+// MSVC++ 7.0 _MSC_VER == 1300 nsel_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002)
+// MSVC++ 7.1 _MSC_VER == 1310 nsel_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003)
+// MSVC++ 8.0 _MSC_VER == 1400 nsel_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005)
+// MSVC++ 9.0 _MSC_VER == 1500 nsel_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008)
+// MSVC++ 10.0 _MSC_VER == 1600 nsel_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010)
+// MSVC++ 11.0 _MSC_VER == 1700 nsel_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012)
+// MSVC++ 12.0 _MSC_VER == 1800 nsel_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013)
+// MSVC++ 14.0 _MSC_VER == 1900 nsel_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015)
+// MSVC++ 14.1 _MSC_VER >= 1910 nsel_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017)
+// MSVC++ 14.2 _MSC_VER >= 1920 nsel_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+# define nsel_COMPILER_MSVC_VER (_MSC_VER )
+# define nsel_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) )
+#else
+# define nsel_COMPILER_MSVC_VER 0
+# define nsel_COMPILER_MSVC_VERSION 0
+#endif
+
+#define nsel_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define nsel_COMPILER_CLANG_VERSION nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nsel_COMPILER_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nsel_COMPILER_GNUC_VERSION nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nsel_COMPILER_GNUC_VERSION 0
+#endif
+
+// half-open range [lo..hi):
+//#define nsel_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Method enabling
+
+#define nsel_REQUIRES_0(...) \
+ template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
+
+#define nsel_REQUIRES_T(...) \
+ , typename std::enable_if< (__VA_ARGS__), int >::type = 0
+
+#define nsel_REQUIRES_R(R, ...) \
+ typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define nsel_REQUIRES_A(...) \
+ , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#ifdef __clang__
+# pragma clang diagnostic push
+#elif defined __GNUC__
+# pragma GCC diagnostic push
+#endif // __clang__
+
+#if nsel_COMPILER_MSVC_VERSION >= 140
+# define nsel_DISABLE_MSVC_WARNINGS(codes) __pragma( warning(push) ) __pragma( warning(disable: codes) )
+#else
+# define nsel_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#ifdef __clang__
+# define nsel_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
+# define nsel_RESTORE_MSVC_WARNINGS()
+#elif defined __GNUC__
+# define nsel_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
+# define nsel_RESTORE_MSVC_WARNINGS()
+#elif nsel_COMPILER_MSVC_VERSION >= 140
+# define nsel_RESTORE_WARNINGS() __pragma( warning( pop ) )
+# define nsel_RESTORE_MSVC_WARNINGS() nsel_RESTORE_WARNINGS()
+#else
+# define nsel_RESTORE_WARNINGS()
+# define nsel_RESTORE_MSVC_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11)
+
+nsel_DISABLE_MSVC_WARNINGS( 26409 )
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nsel_HAS_CPP0X _HAS_CPP0X
+#else
+# define nsel_HAS_CPP0X 0
+#endif
+
+// Presence of language and library features:
+
+#define nsel_CPP17_000 (nsel_CPP17_OR_GREATER)
+
+// Presence of C++17 language features:
+
+#define nsel_HAVE_DEPRECATED nsel_CPP17_000
+
+// C++ feature usage:
+
+#if nsel_HAVE_DEPRECATED
+# define nsel_deprecated(msg) [[deprecated(msg)]]
+#else
+# define nsel_deprecated(msg) /*[[deprecated]]*/
+#endif
+
+//
+// expected:
+//
+
+namespace nonstd { namespace expected_lite {
+
+// type traits C++17:
+
+namespace std17 {
+
+#if nsel_CPP17_OR_GREATER
+
+using std::conjunction;
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#else // nsel_CPP17_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+ template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+ static std::true_type test( int /* unused */);
+
+ template< typename >
+ static std::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+ // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
+
+ template< typename T >
+ static constexpr bool satisfies()
+ {
+ return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+ }
+
+ template< typename T >
+ static auto test( int ) -> std::integral_constant<bool, satisfies<T>()>{}
+
+ template< typename >
+ static auto test(...) -> std::false_type;
+};
+} // namespace detail
+
+// is [nothrow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+// conjunction:
+
+template< typename... > struct conjunction : std::true_type{};
+template< typename B1 > struct conjunction<B1> : B1{};
+
+template< typename B1, typename... Bn >
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type{};
+
+#endif // nsel_CPP17_OR_GREATER
+
+} // namespace std17
+
+// type traits C++20:
+
+namespace std20 {
+
+#if defined(__cpp_lib_remove_cvref)
+
+using std::remove_cvref;
+
+#else
+
+template< typename T >
+struct remove_cvref
+{
+ typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
+};
+
+#endif
+
+} // namespace std20
+
+// forward declaration:
+
+template< typename T, typename E >
+class expected;
+
+namespace detail {
+
+#if nsel_P2505R >= 3
+template< typename T >
+struct is_expected : std::false_type {};
+
+template< typename T, typename E >
+struct is_expected< expected< T, E > > : std::true_type {};
+#endif // nsel_P2505R >= 3
+
+/// discriminated union to hold value or 'error'.
+
+template< typename T, typename E >
+class storage_t_noncopy_nonmove_impl
+{
+ template< typename, typename > friend class nonstd::expected_lite::expected;
+
+public:
+ using value_type = T;
+ using error_type = E;
+
+ // no-op construction
+ storage_t_noncopy_nonmove_impl() {}
+ ~storage_t_noncopy_nonmove_impl() {}
+
+ explicit storage_t_noncopy_nonmove_impl( bool has_value )
+ : m_has_value( has_value )
+ {}
+
+ void construct_value()
+ {
+ new( &m_value ) value_type();
+ }
+
+ // void construct_value( value_type const & e )
+ // {
+ // new( &m_value ) value_type( e );
+ // }
+
+ // void construct_value( value_type && e )
+ // {
+ // new( &m_value ) value_type( std::move( e ) );
+ // }
+
+ template< class... Args >
+ void emplace_value( Args&&... args )
+ {
+ new( &m_value ) value_type( std::forward<Args>(args)...);
+ }
+
+ template< class U, class... Args >
+ void emplace_value( std::initializer_list<U> il, Args&&... args )
+ {
+ new( &m_value ) value_type( il, std::forward<Args>(args)... );
+ }
+
+ void destruct_value()
+ {
+ m_value.~value_type();
+ }
+
+ // void construct_error( error_type const & e )
+ // {
+ // // new( &m_error ) error_type( e );
+ // }
+
+ // void construct_error( error_type && e )
+ // {
+ // // new( &m_error ) error_type( std::move( e ) );
+ // }
+
+ template< class... Args >
+ void emplace_error( Args&&... args )
+ {
+ new( &m_error ) error_type( std::forward<Args>(args)...);
+ }
+
+ template< class U, class... Args >
+ void emplace_error( std::initializer_list<U> il, Args&&... args )
+ {
+ new( &m_error ) error_type( il, std::forward<Args>(args)... );
+ }
+
+ void destruct_error()
+ {
+ m_error.~error_type();
+ }
+
+ constexpr value_type const & value() const &
+ {
+ return m_value;
+ }
+
+ value_type & value() &
+ {
+ return m_value;
+ }
+
+ constexpr value_type const && value() const &&
+ {
+ return std::move( m_value );
+ }
+
+ nsel_constexpr14 value_type && value() &&
+ {
+ return std::move( m_value );
+ }
+
+ value_type const * value_ptr() const
+ {
+ return &m_value;
+ }
+
+ value_type * value_ptr()
+ {
+ return &m_value;
+ }
+
+ error_type const & error() const &
+ {
+ return m_error;
+ }
+
+ error_type & error() &
+ {
+ return m_error;
+ }
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( m_error );
+ }
+
+ nsel_constexpr14 error_type && error() &&
+ {
+ return std::move( m_error );
+ }
+
+ bool has_value() const
+ {
+ return m_has_value;
+ }
+
+ void set_has_value( bool v )
+ {
+ m_has_value = v;
+ }
+
+private:
+ union
+ {
+ value_type m_value;
+ error_type m_error;
+ };
+
+ bool m_has_value = false;
+};
+
+template< typename T, typename E >
+class storage_t_impl
+{
+ template< typename, typename > friend class nonstd::expected_lite::expected;
+
+public:
+ using value_type = T;
+ using error_type = E;
+
+ // no-op construction
+ storage_t_impl() {}
+ ~storage_t_impl() {}
+
+ explicit storage_t_impl( bool has_value )
+ : m_has_value( has_value )
+ {}
+
+ void construct_value()
+ {
+ new( &m_value ) value_type();
+ }
+
+ void construct_value( value_type const & e )
+ {
+ new( &m_value ) value_type( e );
+ }
+
+ void construct_value( value_type && e )
+ {
+ new( &m_value ) value_type( std::move( e ) );
+ }
+
+ template< class... Args >
+ void emplace_value( Args&&... args )
+ {
+ new( &m_value ) value_type( std::forward<Args>(args)...);
+ }
+
+ template< class U, class... Args >
+ void emplace_value( std::initializer_list<U> il, Args&&... args )
+ {
+ new( &m_value ) value_type( il, std::forward<Args>(args)... );
+ }
+
+ void destruct_value()
+ {
+ m_value.~value_type();
+ }
+
+ void construct_error( error_type const & e )
+ {
+ new( &m_error ) error_type( e );
+ }
+
+ void construct_error( error_type && e )
+ {
+ new( &m_error ) error_type( std::move( e ) );
+ }
+
+ template< class... Args >
+ void emplace_error( Args&&... args )
+ {
+ new( &m_error ) error_type( std::forward<Args>(args)...);
+ }
+
+ template< class U, class... Args >
+ void emplace_error( std::initializer_list<U> il, Args&&... args )
+ {
+ new( &m_error ) error_type( il, std::forward<Args>(args)... );
+ }
+
+ void destruct_error()
+ {
+ m_error.~error_type();
+ }
+
+ constexpr value_type const & value() const &
+ {
+ return m_value;
+ }
+
+ value_type & value() &
+ {
+ return m_value;
+ }
+
+ constexpr value_type const && value() const &&
+ {
+ return std::move( m_value );
+ }
+
+ nsel_constexpr14 value_type && value() &&
+ {
+ return std::move( m_value );
+ }
+
+ value_type const * value_ptr() const
+ {
+ return &m_value;
+ }
+
+ value_type * value_ptr()
+ {
+ return &m_value;
+ }
+
+ error_type const & error() const &
+ {
+ return m_error;
+ }
+
+ error_type & error() &
+ {
+ return m_error;
+ }
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( m_error );
+ }
+
+ nsel_constexpr14 error_type && error() &&
+ {
+ return std::move( m_error );
+ }
+
+ bool has_value() const
+ {
+ return m_has_value;
+ }
+
+ void set_has_value( bool v )
+ {
+ m_has_value = v;
+ }
+
+private:
+ union
+ {
+ value_type m_value;
+ error_type m_error;
+ };
+
+ bool m_has_value = false;
+};
+
+/// discriminated union to hold only 'error'.
+
+template< typename E >
+struct storage_t_impl<void, E>
+{
+ template< typename, typename > friend class nonstd::expected_lite::expected;
+
+public:
+ using value_type = void;
+ using error_type = E;
+
+ // no-op construction
+ storage_t_impl() {}
+ ~storage_t_impl() {}
+
+ explicit storage_t_impl( bool has_value )
+ : m_has_value( has_value )
+ {}
+
+ void construct_error( error_type const & e )
+ {
+ new( &m_error ) error_type( e );
+ }
+
+ void construct_error( error_type && e )
+ {
+ new( &m_error ) error_type( std::move( e ) );
+ }
+
+ template< class... Args >
+ void emplace_error( Args&&... args )
+ {
+ new( &m_error ) error_type( std::forward<Args>(args)...);
+ }
+
+ template< class U, class... Args >
+ void emplace_error( std::initializer_list<U> il, Args&&... args )
+ {
+ new( &m_error ) error_type( il, std::forward<Args>(args)... );
+ }
+
+ void destruct_error()
+ {
+ m_error.~error_type();
+ }
+
+ error_type const & error() const &
+ {
+ return m_error;
+ }
+
+ error_type & error() &
+ {
+ return m_error;
+ }
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( m_error );
+ }
+
+ nsel_constexpr14 error_type && error() &&
+ {
+ return std::move( m_error );
+ }
+
+ bool has_value() const
+ {
+ return m_has_value;
+ }
+
+ void set_has_value( bool v )
+ {
+ m_has_value = v;
+ }
+
+private:
+ union
+ {
+ char m_dummy;
+ error_type m_error;
+ };
+
+ bool m_has_value = false;
+};
+
+template< typename T, typename E, bool isConstructable, bool isMoveable >
+class storage_t
+{
+public:
+};
+
+template< typename T, typename E >
+class storage_t<T, E, false, false> : public storage_t_noncopy_nonmove_impl<T, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_noncopy_nonmove_impl<T, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other ) = delete;
+ storage_t( storage_t && other ) = delete;
+
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, true> : public storage_t_impl<T, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<T, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other )
+ : storage_t_impl<T, E>( other.has_value() )
+ {
+ if ( this->has_value() ) this->construct_value( other.value() );
+ else this->construct_error( other.error() );
+ }
+
+ storage_t(storage_t && other )
+ : storage_t_impl<T, E>( other.has_value() )
+ {
+ if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+ else this->construct_error( std::move( other.error() ) );
+ }
+};
+
+template< typename E >
+class storage_t<void, E, true, true> : public storage_t_impl<void, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<void, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other )
+ : storage_t_impl<void, E>( other.has_value() )
+ {
+ if ( this->has_value() ) ;
+ else this->construct_error( other.error() );
+ }
+
+ storage_t(storage_t && other )
+ : storage_t_impl<void, E>( other.has_value() )
+ {
+ if ( this->has_value() ) ;
+ else this->construct_error( std::move( other.error() ) );
+ }
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, false> : public storage_t_impl<T, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<T, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other )
+ : storage_t_impl<T, E>(other.has_value())
+ {
+ if ( this->has_value() ) this->construct_value( other.value() );
+ else this->construct_error( other.error() );
+ }
+
+ storage_t( storage_t && other ) = delete;
+};
+
+template< typename E >
+class storage_t<void, E, true, false> : public storage_t_impl<void, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<void, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other )
+ : storage_t_impl<void, E>(other.has_value())
+ {
+ if ( this->has_value() ) ;
+ else this->construct_error( other.error() );
+ }
+
+ storage_t( storage_t && other ) = delete;
+};
+
+template< typename T, typename E >
+class storage_t<T, E, false, true> : public storage_t_impl<T, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<T, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other ) = delete;
+
+ storage_t( storage_t && other )
+ : storage_t_impl<T, E>( other.has_value() )
+ {
+ if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+ else this->construct_error( std::move( other.error() ) );
+ }
+};
+
+template< typename E >
+class storage_t<void, E, false, true> : public storage_t_impl<void, E>
+{
+public:
+ storage_t() = default;
+ ~storage_t() = default;
+
+ explicit storage_t( bool has_value )
+ : storage_t_impl<void, E>( has_value )
+ {}
+
+ storage_t( storage_t const & other ) = delete;
+
+ storage_t( storage_t && other )
+ : storage_t_impl<void, E>( other.has_value() )
+ {
+ if ( this->has_value() ) ;
+ else this->construct_error( std::move( other.error() ) );
+ }
+};
+
+#if nsel_P2505R >= 3
+// C++11 invoke implementation
+template< typename >
+struct is_reference_wrapper : std::false_type {};
+template< typename T >
+struct is_reference_wrapper< std::reference_wrapper< T > > : std::true_type {};
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+ nsel_REQUIRES_T(
+ std::is_function<FnT>::value
+ && ( std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value )
+ )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+ noexcept( noexcept( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... ) ) )
+ -> decltype( (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )...) )
+{
+ return (std::forward< ObjectT >( obj ).*memfnptr)( std::forward< Args >( args )... );
+}
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+ nsel_REQUIRES_T(
+ std::is_function<FnT>::value
+ && is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+ )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+ noexcept( noexcept( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) ) )
+ -> decltype( (obj.get().*memfnptr)( std::forward< Args >( args ) ... ) )
+{
+ return (obj.get().*memfnptr)( std::forward< Args >( args ) ... );
+}
+
+template< typename FnT, typename ClassT, typename ObjectT, typename... Args
+ nsel_REQUIRES_T(
+ std::is_function<FnT>::value
+ && !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+ )
+>
+nsel_constexpr auto invoke_member_function_impl( FnT ClassT::* memfnptr, ObjectT && obj, Args && ... args )
+ noexcept( noexcept( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) ) )
+ -> decltype( ((*std::forward< ObjectT >( obj )).*memfnptr)( std::forward< Args >( args ) ... ) )
+{
+ return ((*std::forward<ObjectT>(obj)).*memfnptr)( std::forward< Args >( args ) ... );
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+ nsel_REQUIRES_T(
+ std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ || std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+ noexcept( noexcept( std::forward< ObjectT >( obj ).*memobjptr ) )
+ -> decltype( std::forward< ObjectT >( obj ).*memobjptr )
+{
+ return std::forward< ObjectT >( obj ).*memobjptr;
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+ nsel_REQUIRES_T(
+ is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+ )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+ noexcept( noexcept( obj.get().*memobjptr ) )
+ -> decltype( obj.get().*memobjptr )
+{
+ return obj.get().*memobjptr;
+}
+
+template< typename MemberT, typename ClassT, typename ObjectT
+ nsel_REQUIRES_T(
+ !std::is_same< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ && !std::is_base_of< ClassT, typename std20::remove_cvref< ObjectT >::type >::value
+ && !is_reference_wrapper< typename std20::remove_cvref< ObjectT >::type >::value
+ )
+>
+nsel_constexpr auto invoke_member_object_impl( MemberT ClassT::* memobjptr, ObjectT && obj )
+ noexcept( noexcept( (*std::forward< ObjectT >( obj )).*memobjptr ) )
+ -> decltype( (*std::forward< ObjectT >( obj )).*memobjptr )
+{
+ return (*std::forward< ObjectT >( obj )).*memobjptr;
+}
+
+template< typename F, typename... Args
+ nsel_REQUIRES_T(
+ std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value
+ )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+ noexcept( noexcept( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) )
+ -> decltype( invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) )
+{
+ return invoke_member_function_impl( std::forward< F >( f ), std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename... Args
+ nsel_REQUIRES_T(
+ std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value
+ )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+ noexcept( noexcept( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) ) )
+ -> decltype( invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... ) )
+{
+ return invoke_member_object_impl( std::forward< F >( f ), std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename... Args
+ nsel_REQUIRES_T(
+ !std::is_member_function_pointer< typename std20::remove_cvref< F >::type >::value
+ && !std::is_member_object_pointer< typename std20::remove_cvref< F >::type >::value
+ )
+>
+nsel_constexpr auto invoke( F && f, Args && ... args )
+ noexcept( noexcept( std::forward< F >( f )( std::forward< Args >( args ) ... ) ) )
+ -> decltype( std::forward< F >( f )( std::forward< Args >( args ) ... ) )
+{
+ return std::forward< F >( f )( std::forward< Args >( args ) ... );
+}
+
+template< typename F, typename ... Args >
+using invoke_result_nocvref_t = typename std20::remove_cvref< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type;
+
+#if nsel_P2505R >= 5
+template< typename F, typename ... Args >
+using transform_invoke_result_t = typename std::remove_cv< decltype( invoke( std::declval< F >(), std::declval< Args >()... ) ) >::type;
+#else
+template< typename F, typename ... Args >
+using transform_invoke_result_t = invoke_result_nocvref_t
+#endif // nsel_P2505R >= 5
+
+template< typename T >
+struct valid_expected_value_type : std::integral_constant< bool, std::is_destructible< T >::value && !std::is_reference< T >::value && !std::is_array< T >::value > {};
+
+#endif // nsel_P2505R >= 3
+} // namespace detail
+
+/// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected.
+
+#if nsel_P0323R <= 2
+template< typename E = std::exception_ptr >
+class unexpected_type
+#else
+template< typename E >
+class unexpected_type
+#endif // nsel_P0323R
+{
+public:
+ using error_type = E;
+
+ // x.x.5.2.1 Constructors
+
+// unexpected_type() = delete;
+
+ constexpr unexpected_type( unexpected_type const & ) = default;
+ constexpr unexpected_type( unexpected_type && ) = default;
+
+ template< typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, Args&&...>::value
+ )
+ >
+ constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), Args &&... args )
+ : m_error( std::forward<Args>( args )...)
+ {}
+
+ template< typename U, typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+ )
+ >
+ constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), std::initializer_list<U> il, Args &&... args )
+ : m_error( il, std::forward<Args>( args )...)
+ {}
+
+ template< typename E2
+ nsel_REQUIRES_T(
+ std::is_constructible<E,E2>::value
+ && !std::is_same< typename std20::remove_cvref<E2>::type, nonstd_lite_in_place_t(E2) >::value
+ && !std::is_same< typename std20::remove_cvref<E2>::type, unexpected_type >::value
+ )
+ >
+ constexpr explicit unexpected_type( E2 && error )
+ : m_error( std::forward<E2>( error ) )
+ {}
+
+ template< typename E2
+ nsel_REQUIRES_T(
+ std::is_constructible< E, E2>::value
+ && !std::is_constructible<E, unexpected_type<E2> & >::value
+ && !std::is_constructible<E, unexpected_type<E2> >::value
+ && !std::is_constructible<E, unexpected_type<E2> const & >::value
+ && !std::is_constructible<E, unexpected_type<E2> const >::value
+ && !std::is_convertible< unexpected_type<E2> &, E>::value
+ && !std::is_convertible< unexpected_type<E2> , E>::value
+ && !std::is_convertible< unexpected_type<E2> const &, E>::value
+ && !std::is_convertible< unexpected_type<E2> const , E>::value
+ && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+ )
+ >
+ constexpr explicit unexpected_type( unexpected_type<E2> const & error )
+ : m_error( E{ error.error() } )
+ {}
+
+ template< typename E2
+ nsel_REQUIRES_T(
+ std::is_constructible< E, E2>::value
+ && !std::is_constructible<E, unexpected_type<E2> & >::value
+ && !std::is_constructible<E, unexpected_type<E2> >::value
+ && !std::is_constructible<E, unexpected_type<E2> const & >::value
+ && !std::is_constructible<E, unexpected_type<E2> const >::value
+ && !std::is_convertible< unexpected_type<E2> &, E>::value
+ && !std::is_convertible< unexpected_type<E2> , E>::value
+ && !std::is_convertible< unexpected_type<E2> const &, E>::value
+ && !std::is_convertible< unexpected_type<E2> const , E>::value
+ && std::is_convertible< E2 const &, E>::value /*=> explicit */
+ )
+ >
+ constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> const & error )
+ : m_error( error.error() )
+ {}
+
+ template< typename E2
+ nsel_REQUIRES_T(
+ std::is_constructible< E, E2>::value
+ && !std::is_constructible<E, unexpected_type<E2> & >::value
+ && !std::is_constructible<E, unexpected_type<E2> >::value
+ && !std::is_constructible<E, unexpected_type<E2> const & >::value
+ && !std::is_constructible<E, unexpected_type<E2> const >::value
+ && !std::is_convertible< unexpected_type<E2> &, E>::value
+ && !std::is_convertible< unexpected_type<E2> , E>::value
+ && !std::is_convertible< unexpected_type<E2> const &, E>::value
+ && !std::is_convertible< unexpected_type<E2> const , E>::value
+ && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+ )
+ >
+ constexpr explicit unexpected_type( unexpected_type<E2> && error )
+ : m_error( E{ std::move( error.error() ) } )
+ {}
+
+ template< typename E2
+ nsel_REQUIRES_T(
+ std::is_constructible< E, E2>::value
+ && !std::is_constructible<E, unexpected_type<E2> & >::value
+ && !std::is_constructible<E, unexpected_type<E2> >::value
+ && !std::is_constructible<E, unexpected_type<E2> const & >::value
+ && !std::is_constructible<E, unexpected_type<E2> const >::value
+ && !std::is_convertible< unexpected_type<E2> &, E>::value
+ && !std::is_convertible< unexpected_type<E2> , E>::value
+ && !std::is_convertible< unexpected_type<E2> const &, E>::value
+ && !std::is_convertible< unexpected_type<E2> const , E>::value
+ && std::is_convertible< E2 const &, E>::value /*=> non-explicit */
+ )
+ >
+ constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> && error )
+ : m_error( std::move( error.error() ) )
+ {}
+
+ // x.x.5.2.2 Assignment
+
+ nsel_constexpr14 unexpected_type& operator=( unexpected_type const & ) = default;
+ nsel_constexpr14 unexpected_type& operator=( unexpected_type && ) = default;
+
+ template< typename E2 = E >
+ nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> const & other )
+ {
+ unexpected_type{ other.error() }.swap( *this );
+ return *this;
+ }
+
+ template< typename E2 = E >
+ nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> && other )
+ {
+ unexpected_type{ std::move( other.error() ) }.swap( *this );
+ return *this;
+ }
+
+ // x.x.5.2.3 Observers
+
+ nsel_constexpr14 E & error() & noexcept
+ {
+ return m_error;
+ }
+
+ constexpr E const & error() const & noexcept
+ {
+ return m_error;
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ nsel_constexpr14 E && error() && noexcept
+ {
+ return std::move( m_error );
+ }
+
+ constexpr E const && error() const && noexcept
+ {
+ return std::move( m_error );
+ }
+
+#endif
+
+ // x.x.5.2.3 Observers - deprecated
+
+ nsel_deprecated("replace value() with error()")
+
+ nsel_constexpr14 E & value() & noexcept
+ {
+ return m_error;
+ }
+
+ nsel_deprecated("replace value() with error()")
+
+ constexpr E const & value() const & noexcept
+ {
+ return m_error;
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ nsel_deprecated("replace value() with error()")
+
+ nsel_constexpr14 E && value() && noexcept
+ {
+ return std::move( m_error );
+ }
+
+ nsel_deprecated("replace value() with error()")
+
+ constexpr E const && value() const && noexcept
+ {
+ return std::move( m_error );
+ }
+
+#endif
+
+ // x.x.5.2.4 Swap
+
+ template< typename U=E >
+ nsel_REQUIRES_R( void,
+ std17::is_swappable<U>::value
+ )
+ swap( unexpected_type & other ) noexcept (
+ std17::is_nothrow_swappable<U>::value
+ )
+ {
+ using std::swap;
+ swap( m_error, other.m_error );
+ }
+
+ // TODO: ??? unexpected_type: in-class friend operator==, !=
+
+private:
+ error_type m_error;
+};
+
+#if nsel_CPP17_OR_GREATER
+
+/// template deduction guide:
+
+template< typename E >
+unexpected_type( E ) -> unexpected_type< E >;
+
+#endif
+
+/// class unexpected_type, std::exception_ptr specialization (P0323R2)
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+#if nsel_P0323R <= 2
+
+// TODO: Should expected be specialized for particular E types such as exception_ptr and how?
+// See p0323r7 2.1. Ergonomics, http://wg21.link/p0323
+template<>
+class unexpected_type< std::exception_ptr >
+{
+public:
+ using error_type = std::exception_ptr;
+
+ unexpected_type() = delete;
+
+ ~unexpected_type(){}
+
+ explicit unexpected_type( std::exception_ptr const & error )
+ : m_error( error )
+ {}
+
+ explicit unexpected_type(std::exception_ptr && error )
+ : m_error( std::move( error ) )
+ {}
+
+ template< typename E >
+ explicit unexpected_type( E error )
+ : m_error( std::make_exception_ptr( error ) )
+ {}
+
+ std::exception_ptr const & value() const
+ {
+ return m_error;
+ }
+
+ std::exception_ptr & value()
+ {
+ return m_error;
+ }
+
+private:
+ std::exception_ptr m_error;
+};
+
+#endif // nsel_P0323R
+#endif // !nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.4, Unexpected equality operators
+
+template< typename E1, typename E2 >
+constexpr bool operator==( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+ return x.error() == y.error();
+}
+
+template< typename E1, typename E2 >
+constexpr bool operator!=( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+ return ! ( x == y );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename E >
+constexpr bool operator<( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+ return x.error() < y.error();
+}
+
+template< typename E >
+constexpr bool operator>( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+ return ( y < x );
+}
+
+template< typename E >
+constexpr bool operator<=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+ return ! ( y < x );
+}
+
+template< typename E >
+constexpr bool operator>=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+ return ! ( x < y );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.5 Specialized algorithms
+
+template< typename E
+ nsel_REQUIRES_T(
+ std17::is_swappable<E>::value
+ )
+>
+void swap( unexpected_type<E> & x, unexpected_type<E> & y) noexcept ( noexcept ( x.swap(y) ) )
+{
+ x.swap( y );
+}
+
+#if nsel_P0323R <= 2
+
+// unexpected: relational operators for std::exception_ptr:
+
+inline constexpr bool operator<( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+ return false;
+}
+
+inline constexpr bool operator>( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+ return false;
+}
+
+inline constexpr bool operator<=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+ return ( x == y );
+}
+
+inline constexpr bool operator>=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+ return ( x == y );
+}
+
+#endif // nsel_P0323R
+
+// unexpected: traits
+
+#if nsel_P0323R <= 3
+
+template< typename E>
+struct is_unexpected : std::false_type {};
+
+template< typename E>
+struct is_unexpected< unexpected_type<E> > : std::true_type {};
+
+#endif // nsel_P0323R
+
+// unexpected: factory
+
+// keep make_unexpected() removed in p0323r2 for pre-C++17:
+
+template< typename E>
+nsel_constexpr14 auto
+make_unexpected( E && value ) -> unexpected_type< typename std::decay<E>::type >
+{
+ return unexpected_type< typename std::decay<E>::type >( std::forward<E>(value) );
+}
+
+#if nsel_P0323R <= 3
+
+/*nsel_constexpr14*/ auto inline
+make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr >
+{
+ return unexpected_type< std::exception_ptr >( std::current_exception() );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.6, x.x.7 expected access error
+
+template< typename E >
+class bad_expected_access;
+
+/// x.x.7 bad_expected_access<void>: expected access error
+
+template <>
+class bad_expected_access< void > : public std::exception
+{
+public:
+ explicit bad_expected_access()
+ : std::exception()
+ {}
+};
+
+/// x.x.6 bad_expected_access: expected access error
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename E >
+class bad_expected_access : public bad_expected_access< void >
+{
+public:
+ using error_type = E;
+
+ explicit bad_expected_access( error_type error )
+ : m_error( error )
+ {}
+
+ virtual char const * what() const noexcept override
+ {
+ return "bad_expected_access";
+ }
+
+ nsel_constexpr14 error_type & error() &
+ {
+ return m_error;
+ }
+
+ constexpr error_type const & error() const &
+ {
+ return m_error;
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ nsel_constexpr14 error_type && error() &&
+ {
+ return std::move( m_error );
+ }
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( m_error );
+ }
+
+#endif
+
+private:
+ error_type m_error;
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.8 unexpect tag, in_place_unexpected tag: construct an error
+
+struct unexpect_t{};
+using in_place_unexpected_t = unexpect_t;
+
+nsel_inline17 constexpr unexpect_t unexpect{};
+nsel_inline17 constexpr unexpect_t in_place_unexpected{};
+
+/// class error_traits
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+
+namespace detail {
+ inline bool text( char const * /*text*/ ) { return true; }
+}
+
+template< typename Error >
+struct error_traits
+{
+ static void rethrow( Error const & /*e*/ )
+ {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+ RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+ assert( false && detail::text("throw bad_expected_access<Error>{ e };") );
+#endif
+ }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+ static void rethrow( std::exception_ptr const & /*e*/ )
+ {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+ RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+ assert( false && detail::text("throw bad_expected_access<std::exception_ptr>{ e };") );
+#endif
+ }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+ static void rethrow( std::error_code const & /*e*/ )
+ {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+ RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+ assert( false && detail::text("throw std::system_error( e );") );
+#endif
+ }
+};
+
+#else // nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename Error >
+struct error_traits
+{
+ static void rethrow( Error const & e )
+ {
+ throw bad_expected_access<Error>{ e };
+ }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+ static void rethrow( std::exception_ptr const & e )
+ {
+ std::rethrow_exception( e );
+ }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+ static void rethrow( std::error_code const & e )
+ {
+ throw std::system_error( e );
+ }
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+#if nsel_P2505R >= 3
+namespace detail {
+
+// from https://en.cppreference.com/w/cpp/utility/expected/unexpected:
+// "the type of the unexpected value. The type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type."
+template< typename T >
+struct valid_unexpected_type : std::integral_constant< bool,
+ std::is_same< T, typename std20::remove_cvref< T >::type >::value
+ && std::is_object< T >::value
+ && !std::is_array< T >::value
+> {};
+
+template< typename T >
+struct valid_unexpected_type< unexpected_type< T > > : std::false_type {};
+
+} // namespace detail
+#endif // nsel_P2505R >= 3
+
+} // namespace expected_lite
+
+// provide nonstd::unexpected_type:
+
+using expected_lite::unexpected_type;
+
+namespace expected_lite {
+
+/// class expected
+
+#if nsel_P0323R <= 2
+template< typename T, typename E = std::exception_ptr >
+class expected
+#else
+template< typename T, typename E >
+class expected
+#endif // nsel_P0323R
+{
+private:
+ template< typename, typename > friend class expected;
+
+public:
+ using value_type = T;
+ using error_type = E;
+ using unexpected_type = nonstd::unexpected_type<E>;
+
+ template< typename U >
+ struct rebind
+ {
+ using type = expected<U, error_type>;
+ };
+
+ // x.x.4.1 constructors
+
+ nsel_REQUIRES_0(
+ std::is_default_constructible<T>::value
+ )
+ nsel_constexpr14 expected()
+ : contained( true )
+ {
+ contained.construct_value();
+ }
+
+ nsel_constexpr14 expected( expected const & ) = default;
+ nsel_constexpr14 expected( expected && ) = default;
+
+ template< typename U, typename G
+ nsel_REQUIRES_T(
+ std::is_constructible< T, U const &>::value
+ && std::is_constructible<E, G const &>::value
+ && !std::is_constructible<T, expected<U, G> & >::value
+ && !std::is_constructible<T, expected<U, G> && >::value
+ && !std::is_constructible<T, expected<U, G> const & >::value
+ && !std::is_constructible<T, expected<U, G> const && >::value
+ && !std::is_convertible< expected<U, G> & , T>::value
+ && !std::is_convertible< expected<U, G> &&, T>::value
+ && !std::is_convertible< expected<U, G> const & , T>::value
+ && !std::is_convertible< expected<U, G> const &&, T>::value
+ && (!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( expected<U, G> const & other )
+ : contained( other.has_value() )
+ {
+ if ( has_value() ) contained.construct_value( T{ other.contained.value() } );
+ else contained.construct_error( E{ other.contained.error() } );
+ }
+
+ template< typename U, typename G
+ nsel_REQUIRES_T(
+ std::is_constructible< T, U const &>::value
+ && std::is_constructible<E, G const &>::value
+ && !std::is_constructible<T, expected<U, G> & >::value
+ && !std::is_constructible<T, expected<U, G> && >::value
+ && !std::is_constructible<T, expected<U, G> const & >::value
+ && !std::is_constructible<T, expected<U, G> const && >::value
+ && !std::is_convertible< expected<U, G> & , T>::value
+ && !std::is_convertible< expected<U, G> &&, T>::value
+ && !std::is_convertible< expected<U, G> const &, T>::value
+ && !std::is_convertible< expected<U, G> const &&, T>::value
+ && !(!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> const & other )
+ : contained( other.has_value() )
+ {
+ if ( has_value() ) contained.construct_value( other.contained.value() );
+ else contained.construct_error( other.contained.error() );
+ }
+
+ template< typename U, typename G
+ nsel_REQUIRES_T(
+ std::is_constructible< T, U>::value
+ && std::is_constructible<E, G>::value
+ && !std::is_constructible<T, expected<U, G> & >::value
+ && !std::is_constructible<T, expected<U, G> && >::value
+ && !std::is_constructible<T, expected<U, G> const & >::value
+ && !std::is_constructible<T, expected<U, G> const && >::value
+ && !std::is_convertible< expected<U, G> & , T>::value
+ && !std::is_convertible< expected<U, G> &&, T>::value
+ && !std::is_convertible< expected<U, G> const & , T>::value
+ && !std::is_convertible< expected<U, G> const &&, T>::value
+ && (!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( expected<U, G> && other )
+ : contained( other.has_value() )
+ {
+ if ( has_value() ) contained.construct_value( T{ std::move( other.contained.value() ) } );
+ else contained.construct_error( E{ std::move( other.contained.error() ) } );
+ }
+
+ template< typename U, typename G
+ nsel_REQUIRES_T(
+ std::is_constructible< T, U>::value
+ && std::is_constructible<E, G>::value
+ && !std::is_constructible<T, expected<U, G> & >::value
+ && !std::is_constructible<T, expected<U, G> && >::value
+ && !std::is_constructible<T, expected<U, G> const & >::value
+ && !std::is_constructible<T, expected<U, G> const && >::value
+ && !std::is_convertible< expected<U, G> & , T>::value
+ && !std::is_convertible< expected<U, G> &&, T>::value
+ && !std::is_convertible< expected<U, G> const & , T>::value
+ && !std::is_convertible< expected<U, G> const &&, T>::value
+ && !(!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> && other )
+ : contained( other.has_value() )
+ {
+ if ( has_value() ) contained.construct_value( std::move( other.contained.value() ) );
+ else contained.construct_error( std::move( other.contained.error() ) );
+ }
+
+ template< typename U = T
+ nsel_REQUIRES_T(
+ std::is_copy_constructible<U>::value
+ )
+ >
+ nsel_constexpr14 expected( value_type const & value )
+ : contained( true )
+ {
+ contained.construct_value( value );
+ }
+
+ template< typename U = T
+ nsel_REQUIRES_T(
+ std::is_constructible<T,U&&>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+ && !std::is_same< expected<T,E> , typename std20::remove_cvref<U>::type>::value
+ && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+ && !std::is_convertible<U&&,T>::value /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( U && value ) noexcept
+ (
+ std::is_nothrow_move_constructible<U>::value &&
+ std::is_nothrow_move_constructible<E>::value
+ )
+ : contained( true )
+ {
+ contained.construct_value( T{ std::forward<U>( value ) } );
+ }
+
+ template< typename U = T
+ nsel_REQUIRES_T(
+ std::is_constructible<T,U&&>::value
+ && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+ && !std::is_same< expected<T,E> , typename std20::remove_cvref<U>::type>::value
+ && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+ && std::is_convertible<U&&,T>::value /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( U && value ) noexcept
+ (
+ std::is_nothrow_move_constructible<U>::value &&
+ std::is_nothrow_move_constructible<E>::value
+ )
+ : contained( true )
+ {
+ contained.construct_value( std::forward<U>( value ) );
+ }
+
+ // construct error:
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G const & >::value
+ && !std::is_convertible< G const &, E>::value /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error )
+ : contained( false )
+ {
+ contained.construct_error( E{ error.error() } );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G const & >::value
+ && std::is_convertible< G const &, E>::value /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error )
+ : contained( false )
+ {
+ contained.construct_error( error.error() );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G&& >::value
+ && !std::is_convertible< G&&, E>::value /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error )
+ : contained( false )
+ {
+ contained.construct_error( E{ std::move( error.error() ) } );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G&& >::value
+ && std::is_convertible< G&&, E>::value /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error )
+ : contained( false )
+ {
+ contained.construct_error( std::move( error.error() ) );
+ }
+
+ // in-place construction, value
+
+ template< typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<T, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), Args&&... args )
+ : contained( true )
+ {
+ contained.emplace_value( std::forward<Args>( args )... );
+ }
+
+ template< typename U, typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<T, std::initializer_list<U>, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
+ : contained( true )
+ {
+ contained.emplace_value( il, std::forward<Args>( args )... );
+ }
+
+ // in-place construction, error
+
+ template< typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+ : contained( false )
+ {
+ contained.emplace_error( std::forward<Args>( args )... );
+ }
+
+ template< typename U, typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+ : contained( false )
+ {
+ contained.emplace_error( il, std::forward<Args>( args )... );
+ }
+
+ // x.x.4.2 destructor
+
+ // TODO: ~expected: triviality
+ // Effects: If T is not cv void and is_trivially_destructible_v<T> is false and bool(*this), calls val.~T(). If is_trivially_destructible_v<E> is false and !bool(*this), calls unexpect.~unexpected<E>().
+ // Remarks: If either T is cv void or is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true, then this destructor shall be a trivial destructor.
+
+ ~expected()
+ {
+ if ( has_value() ) contained.destruct_value();
+ else contained.destruct_error();
+ }
+
+ // x.x.4.3 assignment
+
+ expected & operator=( expected const & other )
+ {
+ expected( other ).swap( *this );
+ return *this;
+ }
+
+ expected & operator=( expected && other ) noexcept
+ (
+ std::is_nothrow_move_constructible< T>::value
+ && std::is_nothrow_move_assignable< T>::value
+ && std::is_nothrow_move_constructible<E>::value // added for missing
+ && std::is_nothrow_move_assignable< E>::value ) // nothrow above
+ {
+ expected( std::move( other ) ).swap( *this );
+ return *this;
+ }
+
+ template< typename U
+ nsel_REQUIRES_T(
+ !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value
+ && std17::conjunction<std::is_scalar<T>, std::is_same<T, std::decay<U>> >::value
+ && std::is_constructible<T ,U>::value
+ && std::is_assignable< T&,U>::value
+ && std::is_nothrow_move_constructible<E>::value )
+ >
+ expected & operator=( U && value )
+ {
+ expected( std::forward<U>( value ) ).swap( *this );
+ return *this;
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G const&>::value &&
+ std::is_copy_constructible<G>::value // TODO: std::is_nothrow_copy_constructible<G>
+ && std::is_copy_assignable<G>::value
+ )
+ >
+ expected & operator=( nonstd::unexpected_type<G> const & error )
+ {
+ expected( unexpect, error.error() ).swap( *this );
+ return *this;
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_constructible<E, G&&>::value &&
+ std::is_move_constructible<G>::value // TODO: std::is_nothrow_move_constructible<G>
+ && std::is_move_assignable<G>::value
+ )
+ >
+ expected & operator=( nonstd::unexpected_type<G> && error )
+ {
+ expected( unexpect, std::move( error.error() ) ).swap( *this );
+ return *this;
+ }
+
+ template< typename... Args
+ nsel_REQUIRES_T(
+ std::is_nothrow_constructible<T, Args&&...>::value
+ )
+ >
+ value_type & emplace( Args &&... args )
+ {
+ expected( nonstd_lite_in_place(T), std::forward<Args>(args)... ).swap( *this );
+ return value();
+ }
+
+ template< typename U, typename... Args
+ nsel_REQUIRES_T(
+ std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value
+ )
+ >
+ value_type & emplace( std::initializer_list<U> il, Args &&... args )
+ {
+ expected( nonstd_lite_in_place(T), il, std::forward<Args>(args)... ).swap( *this );
+ return value();
+ }
+
+ // x.x.4.4 swap
+
+ template< typename U=T, typename G=E >
+ nsel_REQUIRES_R( void,
+ std17::is_swappable< U>::value
+ && std17::is_swappable<G>::value
+ && ( std::is_move_constructible<U>::value || std::is_move_constructible<G>::value )
+ )
+ swap( expected & other ) noexcept
+ (
+ std::is_nothrow_move_constructible<T>::value && std17::is_nothrow_swappable<T&>::value &&
+ std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+ )
+ {
+ using std::swap;
+
+ if ( bool(*this) && bool(other) ) { swap( contained.value(), other.contained.value() ); }
+ else if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+ else if ( bool(*this) && ! bool(other) ) { error_type t( std::move( other.error() ) );
+ other.contained.destruct_error();
+ other.contained.construct_value( std::move( contained.value() ) );
+ contained.destruct_value();
+ contained.construct_error( std::move( t ) );
+ bool has_value = contained.has_value();
+ bool other_has_value = other.has_value();
+ other.contained.set_has_value(has_value);
+ contained.set_has_value(other_has_value);
+ }
+ else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); }
+ }
+
+ // x.x.4.5 observers
+
+ constexpr value_type const * operator ->() const
+ {
+ return assert( has_value() ), contained.value_ptr();
+ }
+
+ value_type * operator ->()
+ {
+ return assert( has_value() ), contained.value_ptr();
+ }
+
+ constexpr value_type const & operator *() const &
+ {
+ return assert( has_value() ), contained.value();
+ }
+
+ value_type & operator *() &
+ {
+ return assert( has_value() ), contained.value();
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ constexpr value_type const && operator *() const &&
+ {
+ return std::move( ( assert( has_value() ), contained.value() ) );
+ }
+
+ nsel_constexpr14 value_type && operator *() &&
+ {
+ return std::move( ( assert( has_value() ), contained.value() ) );
+ }
+
+#endif
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+
+ constexpr bool has_value() const noexcept
+ {
+ return contained.has_value();
+ }
+
+ nsel_DISABLE_MSVC_WARNINGS( 4702 ) // warning C4702: unreachable code, see issue 65.
+
+ constexpr value_type const & value() const &
+ {
+ return has_value()
+ ? ( contained.value() )
+ : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+ }
+
+ value_type & value() &
+ {
+ return has_value()
+ ? ( contained.value() )
+ : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+ }
+ nsel_RESTORE_MSVC_WARNINGS()
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ constexpr value_type const && value() const &&
+ {
+ return std::move( has_value()
+ ? ( contained.value() )
+ : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+ }
+
+ nsel_constexpr14 value_type && value() &&
+ {
+ return std::move( has_value()
+ ? ( contained.value() )
+ : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+ }
+
+#endif
+
+ constexpr error_type const & error() const &
+ {
+ return assert( ! has_value() ), contained.error();
+ }
+
+ error_type & error() &
+ {
+ return assert( ! has_value() ), contained.error();
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( ( assert( ! has_value() ), contained.error() ) );
+ }
+
+ error_type && error() &&
+ {
+ return std::move( ( assert( ! has_value() ), contained.error() ) );
+ }
+
+#endif
+
+ constexpr unexpected_type get_unexpected() const
+ {
+ return make_unexpected( contained.error() );
+ }
+
+ template< typename Ex >
+ bool has_exception() const
+ {
+ using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+ return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+ }
+
+ template< typename U
+ nsel_REQUIRES_T(
+ std::is_copy_constructible< T>::value
+ && std::is_convertible<U&&, T>::value
+ )
+ >
+ value_type value_or( U && v ) const &
+ {
+ return has_value()
+ ? contained.value()
+ : static_cast<T>( std::forward<U>( v ) );
+ }
+
+ template< typename U
+ nsel_REQUIRES_T(
+ std::is_move_constructible< T>::value
+ && std::is_convertible<U&&, T>::value
+ )
+ >
+ value_type value_or( U && v ) &&
+ {
+ return has_value()
+ ? std::move( contained.value() )
+ : static_cast<T>( std::forward<U>( v ) );
+ }
+
+#if nsel_P2505R >= 4
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_copy_constructible< E >::value
+ && std::is_convertible< G, E >::value
+ )
+ >
+ nsel_constexpr error_type error_or( G && e ) const &
+ {
+ return has_value()
+ ? static_cast< E >( std::forward< G >( e ) )
+ : contained.error();
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_move_constructible< E >::value
+ && std::is_convertible< G, E >::value
+ )
+ >
+ nsel_constexpr14 error_type error_or( G && e ) &&
+ {
+ return has_value()
+ ? static_cast< E >( std::forward< G >( e ) )
+ : std::move( contained.error() );
+ }
+#endif // nsel_P2505R >= 4
+
+#if nsel_P2505R >= 3
+ // Monadic operations (P2505)
+ template< typename F
+ nsel_REQUIRES_T(
+ detail::is_expected < detail::invoke_result_nocvref_t< F, value_type & > > ::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type & >::error_type, error_type >::value
+ && std::is_constructible< error_type, error_type & >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type & > and_then( F && f ) &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, value_type & >( detail::invoke( std::forward< F >( f ), value() ) )
+ : detail::invoke_result_nocvref_t< F, value_type & >( unexpect, error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type & > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value
+ && std::is_constructible< error_type, const error_type & >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type & > and_then( F && f ) const &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const value_type & >( detail::invoke( std::forward< F >( f ), value() ) )
+ : detail::invoke_result_nocvref_t< F, const value_type & >( unexpect, error() );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, value_type && > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, value_type && >::error_type, error_type >::value
+ && std::is_constructible< error_type, error_type && >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, value_type && > and_then( F && f ) &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) )
+ : detail::invoke_result_nocvref_t< F, value_type && >( unexpect, std::move( error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const value_type && > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, const value_type & >::error_type, error_type >::value
+ && std::is_constructible< error_type, const error_type && >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const value_type && > and_then( F && f ) const &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const value_type && >( detail::invoke( std::forward< F >( f ), std::move( value() ) ) )
+ : detail::invoke_result_nocvref_t< F, const value_type && >( unexpect, std::move( error() ) );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type, value_type >::value
+ && std::is_constructible< value_type, value_type & >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, error_type & >( value() )
+ : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type, value_type >::value
+ && std::is_constructible< value_type, const value_type & >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const error_type & >( value() )
+ : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type, value_type >::value
+ && std::is_constructible< value_type, value_type && >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, error_type && >( std::move( value() ) )
+ : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type, value_type >::value
+ && std::is_constructible< value_type, const value_type && >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const error_type && >( std::move( value() ) )
+ : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type & >::value
+ && !std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value
+ && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type & > >::value
+ )
+ >
+ nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type & >, error_type > transform( F && f ) &
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F, value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type & >::value
+ && std::is_void< detail::transform_invoke_result_t< F, value_type & > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, error_type > transform( F && f ) &
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type & >::value
+ && !std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value
+ && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type & > >::value
+ )
+ >
+ nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type & >, error_type > transform( F && f ) const &
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F, const value_type & >, error_type >( detail::invoke( std::forward< F >( f ), **this ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type & >::value
+ && std::is_void< detail::transform_invoke_result_t< F, const value_type & > >::value
+ )
+ >
+ nsel_constexpr expected< void, error_type > transform( F && f ) const &
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type && >::value
+ && !std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value
+ && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, value_type && > >::value
+ )
+ >
+ nsel_constexpr14 expected< detail::transform_invoke_result_t< F, value_type && >, error_type > transform( F && f ) &&
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F, value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) )
+ : make_unexpected( std::move( error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type && >::value
+ && std::is_void< detail::transform_invoke_result_t< F, value_type && > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, error_type > transform( F && f ) &&
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+ : make_unexpected( std::move( error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type && >::value
+ && !std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value
+ && detail::valid_expected_value_type< detail::transform_invoke_result_t< F, const value_type && > >::value
+ )
+ >
+ nsel_constexpr expected< detail::transform_invoke_result_t< F, const value_type && >, error_type > transform( F && f ) const &&
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F, const value_type && >, error_type >( detail::invoke( std::forward< F >( f ), std::move( **this ) ) )
+ : make_unexpected( std::move( error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type && >::value
+ && std::is_void< detail::transform_invoke_result_t< F, const value_type && > >::value
+ )
+ >
+ nsel_constexpr expected< void, error_type > transform( F && f ) const &&
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ), **this ), expected< void, error_type >() )
+ : make_unexpected( std::move( error() ) );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value
+ && std::is_constructible< value_type, value_type & >::value
+ )
+ >
+ nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) &
+ {
+ return has_value()
+ ? expected< value_type, detail::transform_invoke_result_t< F, error_type & > >( in_place, **this )
+ : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value
+ && std::is_constructible< value_type, const value_type & >::value
+ )
+ >
+ nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const &
+ {
+ return has_value()
+ ? expected< value_type, detail::transform_invoke_result_t< F, const error_type & > >( in_place, **this )
+ : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value
+ && std::is_constructible< value_type, value_type && >::value
+ )
+ >
+ nsel_constexpr14 expected< value_type, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) &&
+ {
+ return has_value()
+ ? expected< value_type, detail::transform_invoke_result_t< F, error_type && > >( in_place, std::move( **this ) )
+ : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value
+ && std::is_constructible< value_type, const value_type && >::value
+ )
+ >
+ nsel_constexpr expected< value_type, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const &&
+ {
+ return has_value()
+ ? expected< value_type, detail::transform_invoke_result_t< F, const error_type && > >( in_place, std::move( **this ) )
+ : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+#endif
+#endif // nsel_P2505R >= 3
+ // unwrap()
+
+// template <class U, class E>
+// constexpr expected<U,E> expected<expected<U,E>,E>::unwrap() const&;
+
+// template <class T, class E>
+// constexpr expected<T,E> expected<T,E>::unwrap() const&;
+
+// template <class U, class E>
+// expected<U,E> expected<expected<U,E>, E>::unwrap() &&;
+
+// template <class T, class E>
+// template expected<T,E> expected<T,E>::unwrap() &&;
+
+ // factories
+
+// template< typename Ex, typename F>
+// expected<T,E> catch_exception(F&& f);
+
+// template< typename F>
+// expected<decltype(func(declval<T>())),E> map(F&& func) ;
+
+// template< typename F>
+// 'see below' bind(F&& func);
+
+// template< typename F>
+// expected<T,E> catch_error(F&& f);
+
+// template< typename F>
+// 'see below' then(F&& func);
+
+private:
+ detail::storage_t
+ <
+ T
+ ,E
+ , std::is_copy_constructible<T>::value && std::is_copy_constructible<E>::value
+ , std::is_move_constructible<T>::value && std::is_move_constructible<E>::value
+ >
+ contained;
+};
+
+/// class expected, void specialization
+
+template< typename E >
+class expected<void, E>
+{
+private:
+ template< typename, typename > friend class expected;
+
+public:
+ using value_type = void;
+ using error_type = E;
+ using unexpected_type = nonstd::unexpected_type<E>;
+
+ // x.x.4.1 constructors
+
+ constexpr expected() noexcept
+ : contained( true )
+ {}
+
+ nsel_constexpr14 expected( expected const & other ) = default;
+ nsel_constexpr14 expected( expected && other ) = default;
+
+ constexpr explicit expected( nonstd_lite_in_place_t(void) )
+ : contained( true )
+ {}
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ !std::is_convertible<G const &, E>::value /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error )
+ : contained( false )
+ {
+ contained.construct_error( E{ error.error() } );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_convertible<G const &, E>::value /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error )
+ : contained( false )
+ {
+ contained.construct_error( error.error() );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ !std::is_convertible<G&&, E>::value /*=> explicit */
+ )
+ >
+ nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error )
+ : contained( false )
+ {
+ contained.construct_error( E{ std::move( error.error() ) } );
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_convertible<G&&, E>::value /*=> non-explicit */
+ )
+ >
+ nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error )
+ : contained( false )
+ {
+ contained.construct_error( std::move( error.error() ) );
+ }
+
+ template< typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+ : contained( false )
+ {
+ contained.emplace_error( std::forward<Args>( args )... );
+ }
+
+ template< typename U, typename... Args
+ nsel_REQUIRES_T(
+ std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+ )
+ >
+ nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+ : contained( false )
+ {
+ contained.emplace_error( il, std::forward<Args>( args )... );
+ }
+
+ // destructor
+
+ ~expected()
+ {
+ if ( ! has_value() )
+ {
+ contained.destruct_error();
+ }
+ }
+
+ // x.x.4.3 assignment
+
+ expected & operator=( expected const & other )
+ {
+ expected( other ).swap( *this );
+ return *this;
+ }
+
+ expected & operator=( expected && other ) noexcept
+ (
+ std::is_nothrow_move_assignable<E>::value &&
+ std::is_nothrow_move_constructible<E>::value )
+ {
+ expected( std::move( other ) ).swap( *this );
+ return *this;
+ }
+
+ void emplace()
+ {
+ expected().swap( *this );
+ }
+
+ // x.x.4.4 swap
+
+ template< typename G = E >
+ nsel_REQUIRES_R( void,
+ std17::is_swappable<G>::value
+ && std::is_move_constructible<G>::value
+ )
+ swap( expected & other ) noexcept
+ (
+ std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+ )
+ {
+ using std::swap;
+
+ if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+ else if ( bool(*this) && ! bool(other) ) { contained.construct_error( std::move( other.error() ) );
+ bool has_value = contained.has_value();
+ bool other_has_value = other.has_value();
+ other.contained.set_has_value(has_value);
+ contained.set_has_value(other_has_value);
+ }
+ else if ( ! bool(*this) && bool(other) ) { other.swap( *this ); }
+ }
+
+ // x.x.4.5 observers
+
+ constexpr explicit operator bool() const noexcept
+ {
+ return has_value();
+ }
+
+ constexpr bool has_value() const noexcept
+ {
+ return contained.has_value();
+ }
+
+ void value() const
+ {
+ if ( ! has_value() )
+ {
+ error_traits<error_type>::rethrow( contained.error() );
+ }
+ }
+
+ constexpr error_type const & error() const &
+ {
+ return assert( ! has_value() ), contained.error();
+ }
+
+ error_type & error() &
+ {
+ return assert( ! has_value() ), contained.error();
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+ constexpr error_type const && error() const &&
+ {
+ return std::move( ( assert( ! has_value() ), contained.error() ) );
+ }
+
+ error_type && error() &&
+ {
+ return std::move( ( assert( ! has_value() ), contained.error() ) );
+ }
+
+#endif
+
+ constexpr unexpected_type get_unexpected() const
+ {
+ return make_unexpected( contained.error() );
+ }
+
+ template< typename Ex >
+ bool has_exception() const
+ {
+ using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+ return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+ }
+
+#if nsel_P2505R >= 4
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_copy_constructible< E >::value
+ && std::is_convertible< G, E >::value
+ )
+ >
+ nsel_constexpr error_type error_or( G && e ) const &
+ {
+ return has_value()
+ ? static_cast< E >( std::forward< G >( e ) )
+ : contained.error();
+ }
+
+ template< typename G = E
+ nsel_REQUIRES_T(
+ std::is_move_constructible< E >::value
+ && std::is_convertible< G, E >::value
+ )
+ >
+ nsel_constexpr14 error_type error_or( G && e ) &&
+ {
+ return has_value()
+ ? static_cast< E >( std::forward< G >( e ) )
+ : std::move( contained.error() );
+ }
+#endif // nsel_P2505R >= 4
+
+#if nsel_P2505R >= 3
+ // Monadic operations (P2505)
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+ && std::is_constructible< error_type, error_type & >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+ : detail::invoke_result_nocvref_t< F >( unexpect, error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+ && std::is_constructible< error_type, const error_type & >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+ : detail::invoke_result_nocvref_t< F >( unexpect, error() );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+ && std::is_constructible< error_type, error_type && >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F > and_then( F && f ) &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+ : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F > >::value
+ && std::is_same< typename detail::invoke_result_nocvref_t< F >::error_type, error_type >::value
+ && std::is_constructible< error_type, const error_type && >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F > and_then( F && f ) const &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F >( detail::invoke( std::forward< F >( f ) ) )
+ : detail::invoke_result_nocvref_t< F >( unexpect, std::move( error() ) );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, error_type & > >::value
+ && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type & >::value_type >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type & > or_else( F && f ) &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, error_type & >()
+ : detail::invoke_result_nocvref_t< F, error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type & > >::value
+ && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type & >::value_type >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type & > or_else( F && f ) const &
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const error_type & >()
+ : detail::invoke_result_nocvref_t< F, const error_type & >( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, error_type && > >::value
+ && std::is_void< typename detail::invoke_result_nocvref_t< F, error_type && >::value_type >::value
+ )
+ >
+ nsel_constexpr14 detail::invoke_result_nocvref_t< F, error_type && > or_else( F && f ) &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, error_type && >()
+ : detail::invoke_result_nocvref_t< F, error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::is_expected< detail::invoke_result_nocvref_t< F, const error_type && > >::value
+ && std::is_void< typename detail::invoke_result_nocvref_t< F, const error_type && >::value_type >::value
+ )
+ >
+ nsel_constexpr detail::invoke_result_nocvref_t< F, const error_type && > or_else( F && f ) const &&
+ {
+ return has_value()
+ ? detail::invoke_result_nocvref_t< F, const error_type && >()
+ : detail::invoke_result_nocvref_t< F, const error_type && >( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type & >::value
+ && !std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) &
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type & >::value
+ && std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, error_type > transform( F && f ) &
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type & >::value
+ && !std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const &
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type & >::value
+ && std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr expected< void, error_type > transform( F && f ) const &
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type && >::value
+ && !std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr14 expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) &&
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, error_type && >::value
+ && std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, error_type > transform( F && f ) &&
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type && >::value
+ && !std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr expected< detail::transform_invoke_result_t< F >, error_type > transform( F && f ) const &&
+ {
+ return has_value()
+ ? expected< detail::transform_invoke_result_t< F >, error_type >( detail::invoke( std::forward< F >( f ) ) )
+ : make_unexpected( error() );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ std::is_constructible< error_type, const error_type && >::value
+ && std::is_void< detail::transform_invoke_result_t< F > >::value
+ )
+ >
+ nsel_constexpr expected< void, error_type > transform( F && f ) const &&
+ {
+ return has_value()
+ ? ( detail::invoke( std::forward< F >( f ) ), expected< void, error_type >() )
+ : make_unexpected( error() );
+ }
+#endif
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type & > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type & > > transform_error( F && f ) &
+ {
+ return has_value()
+ ? expected< void, detail::transform_invoke_result_t< F, error_type & > >()
+ : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type & > >::value
+ )
+ >
+ nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type & > > transform_error( F && f ) const &
+ {
+ return has_value()
+ ? expected< void, detail::transform_invoke_result_t< F, const error_type & > >()
+ : make_unexpected( detail::invoke( std::forward< F >( f ), error() ) );
+ }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, error_type && > >::value
+ )
+ >
+ nsel_constexpr14 expected< void, detail::transform_invoke_result_t< F, error_type && > > transform_error( F && f ) &&
+ {
+ return has_value()
+ ? expected< void, detail::transform_invoke_result_t< F, error_type && > >()
+ : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+
+ template<typename F
+ nsel_REQUIRES_T(
+ detail::valid_unexpected_type< detail::transform_invoke_result_t< F, const error_type && > >::value
+ )
+ >
+ nsel_constexpr expected< void, detail::transform_invoke_result_t< F, const error_type && > > transform_error( F && f ) const &&
+ {
+ return has_value()
+ ? expected< void, detail::transform_invoke_result_t< F, const error_type && > >()
+ : make_unexpected( detail::invoke( std::forward< F >( f ), std::move( error() ) ) );
+ }
+#endif
+#endif // nsel_P2505R >= 3
+
+// template constexpr 'see below' unwrap() const&;
+//
+// template 'see below' unwrap() &&;
+
+ // factories
+
+// template< typename Ex, typename F>
+// expected<void,E> catch_exception(F&& f);
+//
+// template< typename F>
+// expected<decltype(func()), E> map(F&& func) ;
+//
+// template< typename F>
+// 'see below' bind(F&& func) ;
+//
+// template< typename F>
+// expected<void,E> catch_error(F&& f);
+//
+// template< typename F>
+// 'see below' then(F&& func);
+
+private:
+ detail::storage_t
+ <
+ void
+ , E
+ , std::is_copy_constructible<E>::value
+ , std::is_move_constructible<E>::value
+ >
+ contained;
+};
+
+// x.x.4.6 expected<>: comparison operators
+
+template< typename T1, typename E1, typename T2, typename E2
+ nsel_REQUIRES_T(
+ !std::is_void<T1>::value && !std::is_void<T2>::value
+ )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+ return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error();
+}
+
+template< typename T1, typename E1, typename T2, typename E2
+ nsel_REQUIRES_T(
+ std::is_void<T1>::value && std::is_void<T2>::value
+ )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+ return bool(x) != bool(y) ? false : bool(x) || static_cast<bool>( x.error() == y.error() );
+}
+
+template< typename T1, typename E1, typename T2, typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+ return !(x == y);
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, expected<T,E> const & y )
+{
+ return (!y) ? false : (!x) ? true : *x < *y;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, expected<T,E> const & y )
+{
+ return (y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, expected<T,E> const & y )
+{
+ return !(y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y )
+{
+ return !(x < y);
+}
+
+#endif
+
+// x.x.4.7 expected: comparison with T
+
+template< typename T1, typename E1, typename T2
+ nsel_REQUIRES_T(
+ !std::is_void<T1>::value
+ )
+>
+constexpr bool operator==( expected<T1,E1> const & x, T2 const & v )
+{
+ return bool(x) ? *x == v : false;
+}
+
+template< typename T1, typename E1, typename T2
+ nsel_REQUIRES_T(
+ !std::is_void<T1>::value
+ )
+>
+constexpr bool operator==(T2 const & v, expected<T1,E1> const & x )
+{
+ return bool(x) ? v == *x : false;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( expected<T1,E1> const & x, T2 const & v )
+{
+ return bool(x) ? *x != v : true;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( T2 const & v, expected<T1,E1> const & x )
+{
+ return bool(x) ? v != *x : true;
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, T const & v )
+{
+ return bool(x) ? *x < v : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( T const & v, expected<T,E> const & x )
+{
+ return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( T const & v, expected<T,E> const & x )
+{
+ return bool(x) ? *x < v : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, T const & v )
+{
+ return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( T const & v, expected<T,E> const & x )
+{
+ return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, T const & v )
+{
+ return bool(x) ? ! ( v < *x ) : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, T const & v )
+{
+ return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( T const & v, expected<T,E> const & x )
+{
+ return bool(x) ? ! ( v < *x ) : true;
+}
+
+#endif // nsel_P0323R
+
+// x.x.4.8 expected: comparison with unexpected_type
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+ return (!x) ? x.get_unexpected() == u : false;
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+ return ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+ return ! ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+ return ! ( x == u );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+ return (!x) ? ( x.get_unexpected() < u ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+ return (!x) ? ( u < x.get_unexpected() ) : true ;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+ return ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+ return ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+ return ! ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( unexpected_type<E> const & u, expected<T,E> const & x)
+{
+ return ! ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+ return ! ( u > x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+ return ! ( x > u );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.x Specialized algorithms
+
+template< typename T, typename E
+ nsel_REQUIRES_T(
+ ( std::is_void<T>::value || std::is_move_constructible<T>::value )
+ && std::is_move_constructible<E>::value
+ && std17::is_swappable<T>::value
+ && std17::is_swappable<E>::value )
+>
+void swap( expected<T,E> & x, expected<T,E> & y ) noexcept ( noexcept ( x.swap(y) ) )
+{
+ x.swap( y );
+}
+
+#if nsel_P0323R <= 3
+
+template< typename T >
+constexpr auto make_expected( T && v ) -> expected< typename std::decay<T>::type >
+{
+ return expected< typename std::decay<T>::type >( std::forward<T>( v ) );
+}
+
+// expected<void> specialization:
+
+auto inline make_expected() -> expected<void>
+{
+ return expected<void>( in_place );
+}
+
+template< typename T >
+constexpr auto make_expected_from_current_exception() -> expected<T>
+{
+ return expected<T>( make_unexpected_from_current_exception() );
+}
+
+template< typename T >
+auto make_expected_from_exception( std::exception_ptr v ) -> expected<T>
+{
+ return expected<T>( unexpected_type<std::exception_ptr>( std::forward<std::exception_ptr>( v ) ) );
+}
+
+template< typename T, typename E >
+constexpr auto make_expected_from_error( E e ) -> expected<T, typename std::decay<E>::type>
+{
+ return expected<T, typename std::decay<E>::type>( make_unexpected( e ) );
+}
+
+template< typename F
+ nsel_REQUIRES_T( ! std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected< typename std::result_of<F()>::type >
+{
+ try
+ {
+ return make_expected( f() );
+ }
+ catch (...)
+ {
+ return make_unexpected_from_current_exception();
+ }
+}
+
+template< typename F
+ nsel_REQUIRES_T( std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected<void>
+{
+ try
+ {
+ f();
+ return make_expected();
+ }
+ catch (...)
+ {
+ return make_unexpected_from_current_exception();
+ }
+}
+
+#endif // nsel_P0323R
+
+} // namespace expected_lite
+
+using namespace expected_lite;
+
+// using expected_lite::expected;
+// using ...
+
+} // namespace nonstd
+
+namespace std {
+
+// expected: hash support
+
+template< typename T, typename E >
+struct hash< nonstd::expected<T,E> >
+{
+ using result_type = std::size_t;
+ using argument_type = nonstd::expected<T,E>;
+
+ constexpr result_type operator()(argument_type const & arg) const
+ {
+ return arg ? std::hash<T>{}(*arg) : result_type{};
+ }
+};
+
+// TBD - ?? remove? see spec.
+template< typename T, typename E >
+struct hash< nonstd::expected<T&,E> >
+{
+ using result_type = std::size_t;
+ using argument_type = nonstd::expected<T&,E>;
+
+ constexpr result_type operator()(argument_type const & arg) const
+ {
+ return arg ? std::hash<T>{}(*arg) : result_type{};
+ }
+};
+
+// TBD - implement
+// bool(e), hash<expected<void,E>>()(e) shall evaluate to the hashing true;
+// otherwise it evaluates to an unspecified value if E is exception_ptr or
+// a combination of hashing false and hash<E>()(e.error()).
+
+template< typename E >
+struct hash< nonstd::expected<void,E> >
+{
+};
+
+} // namespace std
+
+namespace nonstd {
+
+// void unexpected() is deprecated && removed in C++17
+
+#if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141
+template< typename E >
+using unexpected = unexpected_type<E>;
+#endif
+
+} // namespace nonstd
+
+#undef nsel_REQUIRES
+#undef nsel_REQUIRES_0
+#undef nsel_REQUIRES_T
+
+nsel_RESTORE_WARNINGS()
+
+#endif // nsel_USES_STD_EXPECTED
+
+#endif // NONSTD_EXPECTED_LITE_HPP
diff --git a/contrib/restricted/expected-lite/ya.make b/contrib/restricted/expected-lite/ya.make
new file mode 100644
index 0000000000..21762a23a3
--- /dev/null
+++ b/contrib/restricted/expected-lite/ya.make
@@ -0,0 +1,13 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(BSL-1.0)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(0.8.0)
+
+ORIGINAL_SOURCE(https://github.com/martinmoene/expected-lite/archive/v0.8.0.tar.gz)
+
+END()